Skip to content

Commit

Permalink
Merge branch 'topic/133' into 'master'
Browse files Browse the repository at this point in the history
Rule 'unassigned_out_parameters': also check the subp's decl part.

Closes #133

See merge request eng/libadalang/langkit-query-language!220
  • Loading branch information
Roldak committed May 6, 2024
2 parents d849b42 + 6593cb5 commit 07bcc99
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 30 deletions.
14 changes: 9 additions & 5 deletions lkql_checker/doc/gnatcheck_rm/predefined_rules.rst
Expand Up @@ -8321,17 +8321,17 @@ This rule has the following (mandatory) parameter for the ``+R`` option:

.. index:: Unassigned_OUT_Parameters

Flag procedures' ``out`` parameters that are not assigned.
Flag subprograms' ``out`` parameters that are not assigned.

An ``out`` parameter is flagged if the *sequence of statements* of
the procedure body (before the procedure body's exception part, if any)
contains no assignment to the parameter.
An ``out`` parameter is flagged if neither the *declarative part* nor the
*sequence of statements* of the subprogram body (before the subprogram body's
exception part, if any) contain an assignment to the parameter.

An ``out`` parameter is flagged if an *exception handler* contains neither an
assignment to the parameter nor a raise statement nor a call to a procedure
marked No_Return.

Bodies of generic procedures are also considered.
Bodies of generic subprograms are also considered.

The following are treated as assignments to an ``out`` parameter:

Expand All @@ -8345,6 +8345,10 @@ The rule has an optional parameter for the ``+R`` option:
Ignore assignments to subcomponents of an ``out`` parameter when detecting
if the parameter is assigned.

.. note:: An assignment to a subprogram's parameter can occur in the subprogram
body's *declarative part* in the presence of a nested subprogram declaration
which itself contains an assignment to the enclosing subprogram's parameter.

.. warning:: This rule only detects the described cases of unassigned variables
and doesn't provide a full guarantee that there is no uninitialized access.
It is only a partial replacement for the validity checks provided by
Expand Down
44 changes: 19 additions & 25 deletions lkql_checker/share/lkql/unassigned_out_parameters.lkql
@@ -1,11 +1,11 @@
# Flag procedures' out parameters that are not assigned.
# An out parameter is flagged if the sequence of statements of the procedure
# body (before the procedure body's exception part, if any) contains no
# Flag subprogram' out parameters that are not assigned.
# An out parameter is flagged if the sequence of statements of the subprogram
# body (before the subprogram body's exception part, if any) contains no
# assignment to the parameter.
# An out parameter is also flagged if an exception handler contains neither an
# assignment to the parameter nor a raise statement nor a call to a procedure
# marked No_Return.
# Bodies of generic procedures are also considered.
# Bodies of generic subprograms are also considered.
#
# The following are treated as assignments to an out parameter:
# - an assignment statement, with the parameter or some component as the target
Expand Down Expand Up @@ -51,30 +51,24 @@ fun check_exception_handler(param, name, h, ignore_comp) =
p_has_aspect("No_Return"))) or
check_stmts(param, name, h, ignore_comp)

fun enclosing_body_stmts(node) =
match stdlib.enclosing_body(node)
| null => null
| NullSubpDecl => null
| p => p.f_stmts

@unit_check(help="OUT parameters do not get values in procedure bodies",
@unit_check(help="OUT parameters do not get values in subprogram bodies",
category="Feature")
fun unassigned_out_parameters(unit, ignore_component_assignments=false) = [
{message: "unassigned OUT parameter " & n.f_name.text, loc: n}
for n in from unit.root
# Look for out parameters of subprogram bodies only
select node@DefiningName(parent: DefiningNameList(parent:
ParamSpec(f_mode: ModeOut,
parent: ParamSpecList(parent: Params(parent:
SubpSpec(parent: SubpBody))))))
when {
val f = enclosing_body_stmts(node);
val decl = node.p_basic_decl();

f and not (
check_stmts(decl, node.f_name, f.f_stmts, ignore_component_assignments)
and not [m for m in (from f.f_exceptions select h@ExceptionHandler
when not check_exception_handler(decl, node.f_name, h,
ignore_component_assignments))
])
}]
decl@ParamSpec(f_mode: ModeOut,
parent: ParamSpecList(parent: Params(parent:
SubpSpec(parent: body@SubpBody))))))
when not (
check_stmts(decl, node.f_name, body.f_decls,
ignore_component_assignments)
or check_stmts(decl, node.f_name, body.f_stmts.f_stmts,
ignore_component_assignments)
) or stdlib.any([
m for m in (from body.f_stmts.f_exceptions select h@ExceptionHandler
when not check_exception_handler(decl, node.f_name, h,
ignore_component_assignments))
])
]
11 changes: 11 additions & 0 deletions testsuite/tests/checks/unassigned_out_params/params.adb
Expand Up @@ -87,6 +87,17 @@ procedure Params is
raise;
end;

function Func_With_Out_Param (X : in out Integer) return Integer is
begin
X := 1;
return 2;
end Func_With_Out_Param;

procedure Proc_Param_Assigned_In_Decl_Part (X : in out Integer) is
I : Integer := Func_With_Out_Param (X);
begin
null;
end Proc_Param_Assigned_In_Decl_Part;
begin
null;
end Params;
@@ -0,0 +1,2 @@
project Prj is
end Prj;
25 changes: 25 additions & 0 deletions testsuite/tests/checks/unassigned_out_params_nested_subp/test.adb
@@ -0,0 +1,25 @@
procedure Test is
procedure P
(A : out Integer; -- NOFLAG
B : out Integer -- NOFLAG
)
is
procedure Init_A is
begin
A := 2;
end Init_A;
begin
Init_A;

declare
procedure Init_B is
begin
B := 2;
end Init_B;
begin
Init_B;
end;
end P;
begin
null;
end Test;
Empty file.
@@ -0,0 +1,4 @@
driver: 'checker'
rule_name: 'Unassigned_OUT_Parameters'
project: 'prj.gpr'

0 comments on commit 07bcc99

Please sign in to comment.