Skip to content

Commit cd0838b

Browse files
committed
Merge branch 'topic/c_thales_gnatcheck6' into 'master'
Implement rules for C-Thales-GNATcheck6 See merge request eng/libadalang/langkit-query-language!439
2 parents 8e89625 + 61159ee commit cd0838b

File tree

18 files changed

+436
-1
lines changed

18 files changed

+436
-1
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,6 @@ asis/
5757
# Documentation build artifacts
5858
user_manual/build/
5959
lkql_checker/doc/build/
60+
61+
*.stdout
62+
*.stderr

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ all: lkql gnatcheck build_lkql_native_jit doc
3636

3737
lkql: build/bin/liblkqllang_parse
3838

39-
doc: build_lkql_native_jit
39+
doc:
4040
cd user_manual && make clean html
4141
cd lkql_checker/doc && make generate html-all
4242

lkql_checker/doc/generated/list_of_rules.rst

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ GNATcheck rules.
8787
* :ref:`Local_Instantiations`
8888
* :ref:`Local_Packages`
8989
* :ref:`Local_USE_Clauses`
90+
* :ref:`Lowercase_Keywords`
9091
* :ref:`Max_Identifier_Length`
9192
* :ref:`Maximum_Expression_Complexity`
9293
* :ref:`Maximum_Lines`
@@ -106,6 +107,7 @@ GNATcheck rules.
106107
* :ref:`Nested_Paths`
107108
* :ref:`Nested_Subprograms`
108109
* :ref:`No_Closing_Names`
110+
* :ref:`No_Dependence`
109111
* :ref:`No_Explicit_Real_Range`
110112
* :ref:`No_Inherited_Classwide_Pre`
111113
* :ref:`No_Others_In_Exception_Handlers`
@@ -138,6 +140,7 @@ GNATcheck rules.
138140
* :ref:`Overloaded_Operators`
139141
* :ref:`Overly_Nested_Control_Structures`
140142
* :ref:`Overly_Nested_Scopes`
143+
* :ref:`Overriding_Marks`
141144
* :ref:`Parameters_Aliasing`
142145
* :ref:`Parameters_Out_Of_Order`
143146
* :ref:`POS_On_Enumeration_Types`

lkql_checker/doc/generated/predefined_rules.rst

+93
Original file line numberDiff line numberDiff line change
@@ -5963,6 +5963,31 @@ the definition of exemption sections are:
59635963
59645964
59655965
5966+
.. _Lowercase_Keywords:
5967+
5968+
``Lowercase_Keywords``
5969+
^^^^^^^^^^^^^^^^^^^^^^
5970+
5971+
.. index:: Lowercase_Keywords
5972+
5973+
Flag Ada keywords that are not purely lowercase, such as ``BEGIN`` or
5974+
``beGin``.
5975+
5976+
Please note that this check is **not** language version sensitive. Every
5977+
keyword from Ada 83 to Ada 2012 will be flagged. This means that this
5978+
check might give false positives on usage of identifiers that are
5979+
keywords in newer versions of Ada.
5980+
5981+
.. rubric:: Example
5982+
5983+
.. code-block:: ada
5984+
:emphasize-lines: 1,2
5985+
5986+
packagE Foo is -- FLAG
5987+
END Foo; -- FLAG
5988+
5989+
5990+
59665991
.. _Max_Identifier_Length:
59675992

59685993
``Max_Identifier_Length``
@@ -6098,6 +6123,39 @@ A line containing one or more identifiers may end with a comment.
60986123
60996124
61006125
6126+
.. _No_Dependence:
6127+
6128+
``No_Dependence``
6129+
^^^^^^^^^^^^^^^^^
6130+
6131+
.. index:: No_Dependence
6132+
6133+
Flag every explicit dependency (with clause) to any of the library units
6134+
designated by names passed as parameters.
6135+
6136+
This rule has the following optional parameter for the ``+R`` option and for
6137+
LKQL rule options files:
6138+
6139+
*Unit_Names: list[string]*
6140+
List of fully qualified names designating the library units that
6141+
should not be explicitly depended upon.
6142+
6143+
The list of unit names is case insensitive. Any case can be used both in
6144+
the parameter or in the code's with clauses.
6145+
6146+
.. rubric:: Example
6147+
6148+
.. code-block:: ada
6149+
:emphasize-lines: 2
6150+
6151+
-- if the rule is activated as +RNo_Dependence:Unchecked_Conversion
6152+
with Unchecked_Conversion; -- FLAG
6153+
6154+
package Foo is
6155+
end Foo;
6156+
6157+
6158+
61016159
.. _Numeric_Format:
61026160

61036161
``Numeric_Format``
@@ -6190,6 +6248,41 @@ first construct is flagged
61906248
61916249
61926250
6251+
.. _Overriding_Marks:
6252+
6253+
``Overriding_Marks``
6254+
^^^^^^^^^^^^^^^^^^^^
6255+
6256+
.. index:: Overriding_Marks
6257+
6258+
Check that overriding subprograms are explicitly marked as such.
6259+
6260+
This applies to all subprograms of a derived type that override a
6261+
primitive operation of the type, for both tagged and untagged types. In
6262+
particular, the declaration of a primitive operation of a type extension
6263+
that overrides an inherited operation must carry an overriding
6264+
indicator. Another case is the declaration of a function that overrides
6265+
a predefined operator (such as an equality operator).
6266+
6267+
.. attention:: This doesn't apply to primitives of multiple untagged
6268+
types, and as such, won't ever flag such overriding primitives.
6269+
6270+
.. rubric:: Example
6271+
6272+
.. code-block:: ada
6273+
:emphasize-lines: 7
6274+
6275+
package Foo is
6276+
type A is null record;
6277+
procedure Prim (Self : A) is null;
6278+
6279+
type B is new A;
6280+
6281+
procedure Prim (Self : B) is null; -- FLAG
6282+
end Foo;
6283+
6284+
6285+
61936286
.. _Profile_Discrepancies:
61946287

61956288
``Profile_Discrepancies``
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
val re_83 =
2+
"abort|abs|accept|access|aliased|all|and|array|"
3+
& "at|begin|body|case|constant|declare|delay|delta|digits|"
4+
& "do|else|elsif|end|entry|exception|exit|for|function|"
5+
& "generic|goto|if|in|is|limited|loop|mod|new|"
6+
& "not|null|others|out|of|or|package|pragma|"
7+
& "private|procedure|raise|range|record|rem|"
8+
& "renames|return|reverse|select|separate|"
9+
& "subtype|task|terminate|then|type|"
10+
& "use|when|while|with|xor"
11+
val re_95 = "abstract|protected|requeue|tagged|until"
12+
val re_2005 = "interface|overriding|synchronized"
13+
val re_2012 = "some"
14+
15+
@memoized
16+
fun keyword_matcher(language_version) =
17+
|" Return a regex matcher that matches keywords, given a language version
18+
pattern("^(" & (match language_version
19+
| "ada_83" => re_83
20+
| "ada_95" => re_83 & "|" & re_95
21+
| "ada_2005" => re_83 & "|" & re_95 & "|" & re_2005
22+
| "ada_2012" => re_83 & "|" & re_95 & "|" & re_2005 & "|" & re_2012
23+
| "ada_2022" => re_83 & "|" & re_95 & "|" & re_2005 & "|" & re_2012) & ")$")
24+
25+
val match_uppercase = pattern("[A-Z]+")
26+
27+
@unit_check(category="Style", subcategory="Readability")
28+
fun lowercase_keywords(unit, language_version="ada_2022") =
29+
|" Flag Ada keywords that are not purely lowercase, such as ``BEGIN`` or
30+
|" ``beGin``.
31+
|"
32+
|" Please note that this check is **not** language version sensitive. Every
33+
|" keyword from Ada 83 to Ada 2012 will be flagged. This means that this
34+
|" check might give false positives on usage of identifiers that are
35+
|" keywords in newer versions of Ada.
36+
|"
37+
|" .. rubric:: Example
38+
|"
39+
|" .. code-block:: ada
40+
|" :emphasize-lines: 1,2
41+
|"
42+
|" packagE Foo is -- FLAG
43+
|" END Foo; -- FLAG
44+
[
45+
{message: "Keyword should be lowercase", loc: tok}
46+
for tok in unit.tokens
47+
if tok.text.to_lower_case.contains(keyword_matcher(language_version))
48+
and tok.text.contains(match_uppercase)
49+
]
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import stdlib
2+
3+
@check(category="Style", subcategory="Readability", message="Forbidden dependency")
4+
fun no_dependence(node, unit_names=[]) =
5+
|" Flag every explicit dependency (with clause) to any of the library units
6+
|" designated by names passed as parameters.
7+
|"
8+
|" This rule has the following optional parameter for the ``+R`` option and for
9+
|" LKQL rule options files:
10+
|"
11+
|" *Unit_Names: list[string]*
12+
|" List of fully qualified names designating the library units that
13+
|" should not be explicitly depended upon.
14+
|"
15+
|" The list of unit names is case insensitive. Any case can be used both in
16+
|" the parameter or in the code's with clauses.
17+
|"
18+
|" .. rubric:: Example
19+
|"
20+
|" .. code-block:: ada
21+
|" :emphasize-lines: 2
22+
|"
23+
|" -- if the rule is activated as +RNo_Dependence:Unchecked_Conversion
24+
|" with Unchecked_Conversion; -- FLAG
25+
|"
26+
|" package Foo is
27+
|" end Foo;
28+
node is n@Name(
29+
any parent(depth=2): WithClause
30+
) when stdlib.any([name.to_lower_case == n.text.to_lower_case for name in unit_names])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import stdlib
2+
3+
@memoized
4+
fun range(n) =
5+
|" Quick and dirty range function. Made to work-around the absence of
6+
|" iterator.enumerate in GNATcheck 24.3
7+
if n == 0 then [] else range(n - 1) & [n]
8+
9+
fun match_signature(child_prim, parent_prim, child_type, parent_type) =
10+
|" Custom signature matching function, made to handle the fact that in 24.3
11+
|" LAL's base_subp_declarations doesn't work.
12+
{
13+
14+
fun match_types(child_ptype, parent_ptype) =
15+
# Regular match types, except in the case where the parameter types
16+
# match the controlling type
17+
child_ptype.p_matching_type(parent_ptype)
18+
or (child_ptype.p_matching_type(child_type)
19+
and parent_ptype.p_matching_type(parent_type));
20+
21+
# Check that the names are the same
22+
child_prim.p_defining_name().p_name_matches(parent_prim.p_defining_name())
23+
24+
and {
25+
val child_spec = child_prim.p_subp_spec_or_null();
26+
val parent_spec = parent_prim.p_subp_spec_or_null();
27+
28+
val child_params = child_spec.p_formal_params();
29+
val parent_params = parent_spec.p_formal_params();
30+
31+
# Check that return type is the same
32+
((child_spec.p_returns() is null and parent_spec.p_returns() is null)
33+
or (child_spec.p_returns() is not null and parent_spec.p_returns() is not null
34+
and match_types(child_spec.p_returns().p_designated_type_decl(),
35+
parent_spec.p_returns().p_designated_type_decl())))
36+
37+
# Check that parameters types are the same
38+
and child_params.length == parent_params.length
39+
and stdlib.all([match_types(child_params[i].p_basic_decl().p_formal_type(),
40+
parent_params[i].p_basic_decl().p_formal_type())
41+
for i in range(child_params.length)])
42+
}
43+
}
44+
45+
@check(category="Style", subcategory="Readability", message="Missing overriding mark")
46+
fun overriding_marks(node) =
47+
|" Check that overriding subprograms are explicitly marked as such.
48+
|"
49+
|" This applies to all subprograms of a derived type that override a
50+
|" primitive operation of the type, for both tagged and untagged types. In
51+
|" particular, the declaration of a primitive operation of a type extension
52+
|" that overrides an inherited operation must carry an overriding
53+
|" indicator. Another case is the declaration of a function that overrides
54+
|" a predefined operator (such as an equality operator).
55+
|"
56+
|" .. attention:: This doesn't apply to primitives of multiple untagged
57+
|" types, and as such, won't ever flag such overriding primitives.
58+
|"
59+
|" .. rubric:: Example
60+
|"
61+
|" .. code-block:: ada
62+
|" :emphasize-lines: 7
63+
|"
64+
|" package Foo is
65+
|" type A is null record;
66+
|" procedure Prim (Self : A) is null;
67+
|"
68+
|" type B is new A;
69+
|"
70+
|" procedure Prim (Self : B) is null; -- FLAG
71+
|" end Foo;
72+
# Select primitives subprograms
73+
node is (BasicSubpDecl | BaseSubpBody) (
74+
p_subp_spec_or_null(): BaseSubpSpec(
75+
p_primitive_subp_first_type(): t@TypeDecl(
76+
p_base_type(): bt@TypeDecl(
77+
p_get_primitives(): primitives@(not null)
78+
when stdlib.any([p for p in primitives if match_signature(node, p, t, bt)])
79+
)
80+
)
81+
),
82+
f_overriding: OverridingUnspecified
83+
)
84+
85+
# Body stubs can also take an "overriding" indicator. In that case, check
86+
# the body.
87+
or node is SubpBodyStub(p_previous_part_for_decl(): dcl) when overriding_marks(dcl)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package Custom.Unchecked_Conversion is
2+
end Custom.Unchecked_Conversion;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
package Custom is
2+
end Custom;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
with Ada.Unchecked_Conversion; -- FLAG
2+
with Unchecked_Conversion; -- FLAG
3+
with Custom.Unchecked_Conversion; -- NOFLAG
4+
5+
procedure Test is
6+
begin
7+
null;
8+
end Test;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
test.adb:1:6: rule violation: Forbidden dependency
2+
1 | with Ada.Unchecked_Conversion; -- FLAG
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^
4+
5+
test.adb:2:6: rule violation: Forbidden dependency
6+
2 | with Unchecked_Conversion; -- FLAG
7+
| ^^^^^^^^^^^^^^^^^^^^
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
driver: 'checker'
2+
rule_name: no_dependence
3+
rule_arguments:
4+
no_dependence.unit_names: '["ada.unchecked_conversion", "unchecked_conversion"]'
5+
input_sources: ['test.adb']
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
separate (Test)
2+
procedure Prim3 (Self : T6; Other : Integer) is
3+
begin
4+
null;
5+
end Prim3;

0 commit comments

Comments
 (0)