Skip to content

Commit 2327c89

Browse files
committed
Checks: Add Ada Checks ADA95_5.4.1, ADA95_5.4.5, ADA95_5.4.6
1 parent 9fc256e commit 2327c89

8 files changed

Lines changed: 1054 additions & 0 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
ERR1 = "Use a more constrained subtype for discriminant"
2+
3+
_PREDEFINED_SCALAR = frozenset(
4+
{
5+
"integer",
6+
"long_integer",
7+
"short_integer",
8+
"long_long_integer",
9+
"short_short_integer",
10+
"natural",
11+
"positive",
12+
"float",
13+
"long_float",
14+
"short_float",
15+
"duration",
16+
}
17+
)
18+
19+
_SCALAR_KEYWORDS = ("range", "digits", "delta", "mod ")
20+
21+
22+
def ids():
23+
return ("ADA95_5.4.1",)
24+
25+
26+
def name(id):
27+
return {
28+
"ADA95_5.4.1": "Published Standards/Ada 95/5.4.1 Discriminated Records",
29+
}[id]
30+
31+
32+
def tags(id):
33+
return {
34+
"ADA95_5.4.1": [
35+
"Language: Ada",
36+
"Standard: Ada 95",
37+
"Data Structures",
38+
],
39+
}.get(id, [])
40+
41+
42+
def detailed_description(id):
43+
return {
44+
"ADA95_5.4.1": """
45+
<p><b>Guideline</b></p>
46+
<p>&#8226; When declaring a discriminant, use as constrained a subtype as possible (i.e., subtype with as specific a range constraint as possible).</p>
47+
<p>&#8226; Use a discriminated record rather than a constrained array to represent an array whose actual values are unconstrained.</p>
48+
49+
<p><b>Example</b></p>
50+
<p>An object of type <code>Number_Holder_2</code> imposes a more reasonable restriction on the length of its string component:</p>
51+
<pre><code language="Ada">type Number_List is array (Integer range &lt;&gt;) of Integer;
52+
type Number_Holder_1 (Current_Length : Natural := 0) is
53+
record
54+
Numbers : Number_List (1 .. Current_Length);
55+
end record;
56+
</code></pre>
57+
58+
<pre><code language="Ada">type Number_List is array (Integer range &lt;&gt;) of Integer;
59+
subtype Max_Numbers is Natural range 0 .. 42;
60+
type Number_Holder_2 (Current_Length : Max_Numbers := 0) is
61+
record
62+
Numbers : Number_List (1 .. Current_Length);
63+
end record;
64+
</code></pre>
65+
66+
<p><b>Rationale</b></p>
67+
<p>When you use the discriminant to constrain an array inside a discriminated record, the larger the range of values the discriminant can assume, the more space an object of the type might require. Although your program may compile and link, it will fail at execution when the run-time system is unable to create an object of the potential size required.</p>
68+
<p>The discriminated record captures the intent of an array whose bounds may vary at run-time. A simple constrained array definition (e.g., <code>type Number_List is array (1 .. 42) of Integer;</code>) does not capture the intent that there are at most 42 possible numbers in the list.</p>
69+
70+
<p><b>Developer's Note</b></p>
71+
<p>Guideline 1 (use as constrained a subtype as possible for discriminants) is automated: this check flags discriminants whose declared type is a predefined unconstrained scalar type (Integer, Natural, Positive, Float, etc.) or a user-defined subtype that carries no range constraint. Discriminants typed with an enumeration type or with a user subtype that includes a range, digits, delta, or modular constraint are not flagged.</p>
72+
<p>Guideline 2 (prefer discriminated record over constrained array) is not automatable: it requires knowing the programmer's intent — whether the array bounds are meant to vary at run-time — which cannot be determined statically.</p>
73+
""",
74+
}[id]
75+
76+
77+
def test_entity(file):
78+
return file.kind().check("Ada File")
79+
80+
81+
def _type_str(ent):
82+
return str(ent.type()).lower() if ent and ent.type() else ""
83+
84+
85+
def check(check, file):
86+
seen_discriminants = set()
87+
88+
for disc_ref in file.filerefs("Declare", "Discriminant"):
89+
disc_ent = disc_ref.ent()
90+
91+
if disc_ent.id() in seen_discriminants:
92+
continue
93+
94+
seen_discriminants.add(disc_ent.id())
95+
96+
type_ref = disc_ent.ref("Typed", "Type")
97+
98+
if not type_ref:
99+
type_name = _type_str(disc_ent)
100+
type_first = type_name.split()[0] if type_name else ""
101+
102+
if type_first in _PREDEFINED_SCALAR:
103+
check.violation(
104+
disc_ent, file, disc_ref.line(), disc_ref.column(), ERR1
105+
)
106+
107+
continue
108+
109+
type_ent = type_ref.ent()
110+
111+
if type_ent.kind().check("Enumeration, Access"):
112+
continue
113+
114+
type_ent_name = str(type_ent.name()).lower()
115+
116+
if type_ent_name in _PREDEFINED_SCALAR:
117+
check.violation(disc_ent, file, disc_ref.line(), disc_ref.column(), ERR1)
118+
continue
119+
120+
if any(kw in _type_str(type_ent) for kw in _SCALAR_KEYWORDS):
121+
continue
122+
123+
base_ref = type_ent.ref("Subtypefrom", "Type")
124+
125+
if not base_ref:
126+
continue
127+
128+
base_ent = base_ref.ent()
129+
130+
if str(base_ent.name()).lower() in _PREDEFINED_SCALAR:
131+
check.violation(disc_ent, file, disc_ref.line(), disc_ref.column(), ERR1)
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package Test_5_4_1 is
2+
3+
type Number_List is array (Integer range <>) of Integer;
4+
5+
-- Violation: discriminant typed with unconstrained Natural
6+
type Number_Holder_1 (Current_Length : Natural := 0) is -- UndCC_Violation
7+
record
8+
Numbers : Number_List (1 .. Current_Length);
9+
end record;
10+
11+
-- Valid: discriminant typed with constrained subtype
12+
subtype Max_Numbers is Natural range 0 .. 42;
13+
type Number_Holder_2 (Current_Length : Max_Numbers := 0) is -- UndCC_Valid
14+
record
15+
Numbers : Number_List (1 .. Current_Length);
16+
end record;
17+
18+
-- Violation: discriminant typed with unconstrained Integer
19+
type Holder_3 (Size : Integer := 0) is -- UndCC_Violation
20+
record
21+
Data : Number_List (1 .. Size);
22+
end record;
23+
24+
-- Valid: discriminant typed with constrained subtype of Integer
25+
subtype Small_Size is Integer range 1 .. 100;
26+
type Holder_4 (Size : Small_Size := 1) is -- UndCC_Valid
27+
record
28+
Data : Number_List (1 .. Size);
29+
end record;
30+
31+
-- Violation: discriminant typed with Positive (predefined, unconstrained)
32+
type Holder_5 (Count : Positive := 1) is -- UndCC_Violation
33+
record
34+
Data : Number_List (1 .. Count);
35+
end record;
36+
37+
-- Valid: discriminant typed with enumeration (already constrained by values)
38+
type Color is (Red, Green, Blue);
39+
type Color_Record (C : Color := Red) is -- UndCC_Valid
40+
record
41+
Value : Integer;
42+
end record;
43+
44+
-- Valid: discriminant typed with a Float subtype with digits constraint
45+
subtype Precise_Float is Float digits 6;
46+
type Float_Record (F : Precise_Float := 0.0) is -- UndCC_Valid
47+
record
48+
Value : Integer;
49+
end record;
50+
51+
-- Violation: discriminant typed with unconstrained Float
52+
type Float_Holder (F : Float := 0.0) is -- UndCC_Violation
53+
record
54+
Value : Integer;
55+
end record;
56+
57+
-- Violation: discriminant typed with Long_Integer (predefined, unconstrained)
58+
type Long_Holder (N : Long_Integer := 0) is -- UndCC_Violation
59+
record
60+
Value : Integer;
61+
end record;
62+
63+
-- Violation: discriminant typed with Duration (predefined, unconstrained)
64+
type Timed_Record (T : Duration := 0.0) is -- UndCC_Violation
65+
record
66+
Value : Integer;
67+
end record;
68+
69+
-- Valid: discriminant typed with Boolean (predefined enumeration)
70+
type Bool_Record (Flag : Boolean := False) is -- UndCC_Valid
71+
record
72+
Value : Integer;
73+
end record;
74+
75+
-- Valid: discriminant typed with Character (predefined enumeration)
76+
type Char_Record (Ch : Character := ' ') is -- UndCC_Valid
77+
record
78+
Value : Integer;
79+
end record;
80+
81+
-- Valid: named constrained subtype of Integer used as discriminant
82+
subtype Tiny_Int is Integer range 1 .. 10;
83+
type Int_Constrained (D : Tiny_Int := 1) is -- UndCC_Valid
84+
record
85+
Value : Integer;
86+
end record;
87+
88+
-- Valid: fixed-point subtype with delta+range constraint
89+
subtype Small_Delta is Duration delta 0.001 range 0.0 .. 1.0;
90+
type Delta_Record (T : Small_Delta := 0.0) is -- UndCC_Valid
91+
record
92+
Value : Integer;
93+
end record;
94+
95+
-- Valid: variant record with enumeration discriminant
96+
type Shape_Kind is (Circle, Rectangle);
97+
type Shape (Kind : Shape_Kind := Circle) is -- UndCC_Valid
98+
record
99+
case Kind is
100+
when Circle =>
101+
Radius : Float;
102+
when Rectangle =>
103+
Width : Float;
104+
Height : Float;
105+
end case;
106+
end record;
107+
108+
-- Multiple discriminants: one unconstrained, one constrained
109+
subtype Max_Cols is Natural range 1 .. 80;
110+
type Grid (Rows : Natural := 0; Cols : Max_Cols := 1) is -- UndCC_Violation
111+
record
112+
Value : Integer;
113+
end record;
114+
115+
-- Violation: tagged type with unconstrained discriminant
116+
type Tagged_T (N : Natural := 0) is tagged -- UndCC_Violation
117+
record
118+
Value : Integer;
119+
end record;
120+
121+
-- Violation: unconstrained subtype of Integer resolves to "Integer"
122+
subtype Bare_Int is Integer;
123+
type Bare_Holder (D : Bare_Int := 0) is -- UndCC_Violation
124+
record
125+
Value : Integer;
126+
end record;
127+
128+
-- Violation: discriminant with no default value, unconstrained Natural
129+
type No_Default_Holder (N : Natural) is -- UndCC_Violation
130+
record
131+
Data : Number_List (1 .. N);
132+
end record;
133+
134+
-- Valid: discriminant with no default value, constrained subtype
135+
type No_Default_Constrained (N : Max_Numbers) is -- UndCC_Valid
136+
record
137+
Data : Number_List (1 .. N);
138+
end record;
139+
140+
-- Valid: modular type discriminant (constrained by its modulus)
141+
type Byte is mod 256;
142+
type Byte_Record (B : Byte := 0) is -- UndCC_Valid
143+
record
144+
Value : Integer;
145+
end record;
146+
147+
-- Valid: discriminant typed with a user modular subtype
148+
subtype Half_Byte is Byte range 0 .. 15;
149+
type Half_Record (H : Half_Byte := 0) is -- UndCC_Valid
150+
record
151+
Value : Integer;
152+
end record;
153+
154+
-- Violation: multiple unconstrained discriminants — each is flagged
155+
type Multi_Bad (Rows : Natural := 0; Cols : Natural := 0) is -- UndCC_Violation
156+
record
157+
Value : Integer;
158+
end record;
159+
160+
-- Valid: multiple discriminants all constrained
161+
subtype Row_Count is Natural range 1 .. 100;
162+
subtype Col_Count is Natural range 1 .. 200;
163+
type Multi_Good (Rows : Row_Count := 1; Cols : Col_Count := 1) is -- UndCC_Valid
164+
record
165+
Value : Integer;
166+
end record;
167+
168+
private
169+
170+
-- Violation: private type with unconstrained discriminant
171+
type Private_Holder (N : Natural := 0) is -- UndCC_Violation
172+
record
173+
Value : Integer;
174+
end record;
175+
176+
-- Valid: private type with constrained discriminant
177+
subtype Private_Size is Natural range 0 .. 10;
178+
type Private_Constrained (N : Private_Size := 0) is -- UndCC_Valid
179+
record
180+
Value : Integer;
181+
end record;
182+
183+
end Test_5_4_1;

0 commit comments

Comments
 (0)