52
52
Framework.Language,
53
53
Framework.Rules_Manager,
55
pragma Elaborate (Framework.Language);
56
57
package body Rules.Parameter_Aliasing is
57
58
use Framework, Utilities;
60
-- The heart of the algorithm is the Split procedure. It takes the expression corresponding to
61
-- an [in] out parameter, and splits it into the true variable on one side, a string that represents
62
-- the various selectors and/or indexings applied to the variable on the other side.
63
-- Split is applied between any pair of [in] out parameters (conveniently called Left and Right)
64
-- When applied to the Left parameter, any indexing in the string of selectors/indexing is replaced by
66
-- When applied to the Right parameter, any indexing in the string of selectors/indexing is replaced by
67
-- '2' if we are searching for "certain" aliasing, and by '1' if we are searching for "possible" or
68
-- "unlikely" aliasing.
69
-- There is aliasing if the variables are the same and the strings are the same, or one is identical to
70
-- the beginning of the other one.
72
-- The technique for replacing the indexing is actually "assume the best" for "certain" (we assume that
73
-- indexings are different) and "assume the worst" for "possible" and "unlikely" (we assume that indexings
76
-- We attempt however to diagnose simple cases of static indexing. If *all* indexings for both Left and
77
-- Right are integer litterals or enumeration litterals (we know that as a result of Split), we split
78
-- the variables again, but this time we replace all indexings by the value of the index. This way,
79
-- the strings will differ if the indexings are statically different.
81
-- The situation is somewhat complicated by access types. We keep track of the rightmost dereference.
82
-- For "certain" and "possible", we assume the best, i.e. that dereferences designate different objects,
83
-- and therefore compare only the part before the dereference.
84
-- For "unlikely", we assume the worst (that the dereferences allways designate the same object).
85
-- Currently, we don't take into account the type of the dereferenced object. There is still room for
89
-- Order of declaration is important:
90
type Rule_Detail is (Certain, Possible, Unlikely);
61
-- Simply determine the "proximity" between each pair of [in] out parameters.
62
-- See Thick_Queries.Variables_Proximity for the definition of proximity.
64
subtype Rule_Detail is Thick_Queries.Result_Confidence;
66
package Detail_Flags_Utilities is new Framework.Language.Flag_Utilities (Rule_Detail);
67
use Detail_Flags_Utilities;
91
69
type Usage is array (Rule_Detail) of Boolean;
92
70
Rule_Used : Usage := (others => False);
194
171
procedure Process_Call (Call : in Asis.Statement) is
195
172
use Asis, Asis.Declarations, Asis.Elements, Asis.Expressions, Asis.Statements;
196
use Framework.Reports, Ada.Strings.Wide_Unbounded;
198
function Are_Aliased (Left, Right : Asis.Expression;
199
Detail : Rule_Detail) return Boolean
201
-- Determines if there is aliasing (Certain, Possible or Unlikely) between Left and Right.
202
-- Left and Right are the actuals to an [in] out parameter, they are therefore
203
-- variables, and they can't be defaulted parameters.
205
-- Case 1: None of the expressions includes (explicit or implicit) dereferences
206
-- There is aliasing if both are exactly the same, or one is a subcomponent of
208
-- - Certain if there are no indexed components
209
-- - Possible otherwise
210
-- Case 2: At least one of the expressions includes (explicit or implicit) dereferences
211
-- The "true" full variable (not considering subcomponents) is the target of the
212
-- rightmost dereference.
213
-- - There is aliasing if everything appearing left of this rightmost dereference
214
-- is identical in Left and Right
215
-- - Certain if there are no indexed components nor function calls
216
-- - Possible otherwise
217
-- - Otherwise, aliasing is Unlikely.
219
type Inx_State is (None, Static, Dynamic);
221
L_Variable : Asis.Definition;
222
L_Selectors : Unbounded_Wide_String;
224
L_Inx_Found : Inx_State;
225
R_Variable : Asis.Definition;
226
R_Selectors : Unbounded_Wide_String;
228
R_Inx_Found : Inx_State;
230
procedure Split (Name : in Asis.Expression;
231
Variable : out Asis.Definition;
232
Selectors : out Unbounded_Wide_String;
233
Last_Deref : out Natural;
234
Inx_Found : out Inx_State;
235
Indicator : in Wide_Character;
236
Static_Inx : in Boolean)
238
-- Given the original Name (possibly cleaned-up from a view conversion):
239
-- Returns in Variable the true variable declaration (after following
240
-- possible renamings)
242
-- Returns in Selectors the string of all selectors. A '.' is added in the end to
243
-- avoid the matching of fields where one is identical to the beginning of the other.
244
-- However, if there are any indexed components, the string is truncated at the
245
-- selected component. Indicator is an arbitrary character so that indexed components
246
-- from Left and Right are not equal. Assuming Indicator is '1':
248
-- V.X(3).Y => "X(1)"
250
-- If Static is True, the Name is assumed to contain only litterals for the indexing
251
-- of arrays, and the actual value is used in place of the indicator.
253
-- Returns in Last_Deref the position of the last character of the ".all"
254
-- corresponding to the right-most dereference if any, or 0.
255
-- Returns True in Static_Inx if some indexing were found, but they are all integer or
256
-- enumeration litterals; returns False otherwise.
259
function Build_Indicator (Expr : Asis.Expression) return Wide_String is
260
-- Returns the indicator for indexed expressions and slices
261
-- If the expression is an enumeration or integer litteral, we can use a
262
-- (normalized) representation as indicator; this will enable us to not report
263
-- aliasing between X(1) and X(2).
264
-- For anything else, return the provided Indicator, prepended with a '_' to
265
-- distinguish from an allowed value.
266
-- This function can be made more clever in the future if we can recognize more
267
-- cases of static expressions.
268
Good_Expr: Asis.Expression;
270
if Expression_Kind (Expr) = A_Selected_Component then
271
Good_Expr := Selector (Expr);
277
case Expression_Kind (Good_Expr) is
278
when An_Integer_Literal =>
279
-- We make a round-trip through Value/Image below for the case of the naughty
280
-- user who wrote something like P(Tab (10#1#), Tab (1)).
281
-- The indicators must be the same!
282
return Asis_Integer'Wide_Image (Asis_Integer'Wide_Value (Value_Image (Good_Expr)));
283
when An_Enumeration_Literal =>
284
return To_Upper (Name_Image (Good_Expr));
286
Failure ("Non static index in static Build_Indicator");
289
case Expression_Kind (Good_Expr) is
290
when An_Integer_Literal
291
| An_Enumeration_Literal =>
292
if Inx_Found /= Dynamic then
296
Inx_Found := Dynamic;
298
return (1 => Indicator);
302
procedure Add_Selector (Sel : Wide_String) is
304
Selectors := Sel & Selectors;
305
if Last_Deref /= 0 then
306
Last_Deref := Last_Deref + Sel'Length;
310
E : Asis.Element := Name;
311
Temp_Sel : Unbounded_Wide_String;
312
Temp_Deref : Natural;
313
Temp_Found : Inx_State;
314
Variable_Enclosing : Asis.Element;
319
Selectors := Null_Unbounded_Wide_String;
321
case Expression_Kind (E) is
322
when An_Identifier =>
325
when A_Selected_Component =>
326
case Declaration_Kind (Corresponding_Name_Declaration (Selector (E))) is
327
when A_Component_Declaration | A_Discriminant_Specification =>
328
-- It's a record field, a protected type field...
329
Add_Selector (To_Upper (Name_Image (Selector (E))) & '.');
331
when A_Variable_Declaration | An_Object_Renaming_Declaration =>
332
-- Its a Pack.Var selector
336
Failure ("Wrong selected component", E);
339
when An_Indexed_Component =>
342
Indexers : constant Asis.Expression_List := Index_Expressions (E);
344
Add_Selector (Build_Indicator (Indexers (Indexers'Last)));
345
for I in reverse Indexers'First .. Indexers'Last - 1 loop
346
Add_Selector (Build_Indicator (Indexers (I)) & ',');
353
-- Well, it could be the whole object as well...
354
-- Simply ignore the slice
355
-- (Too complicated to check for static matching)
358
when A_Function_Call =>
359
-- a Function_Call can appear only as the first
360
-- element, and if it returns an access value,
361
-- or a composite object used for one of its
362
-- access subcomponents.
363
Add_Selector("_CALL_" & Indicator & '.');
365
if Expression_Kind (E) = A_Selected_Component then
370
when An_Explicit_Dereference =>
371
-- "all." will be added below, since the prefix is necessarily
375
when A_Type_Conversion =>
376
E := Converted_Or_Qualified_Expression (E);
379
Failure ("Wrong variable name", E);
382
-- Add a "all." if the *type* is an access type
383
-- This allows explicit and implicit dereferences to match
384
if Expression_Type_Kind (E) = An_Access_Type_Definition then
385
Add_Selector ("all.");
386
if Last_Deref = 0 then
387
Last_Deref := 3; -- Points to the last character of "all"
392
-- Return the "true" definion of Variable, after following all renamings
393
-- But the renaming can be a complicated expression like:
394
-- A : T renames Rec.X.Y(3);
395
Variable := Corresponding_Name_Definition (E);
397
Variable_Enclosing := Enclosing_Element (Variable);
398
exit when Declaration_Kind (Variable_Enclosing) not in A_Renaming_Declaration;
399
Split (Name => Renamed_Entity (Variable_Enclosing),
400
Variable => Variable,
401
Selectors => Temp_Sel,
402
Last_Deref => Temp_Deref,
403
Inx_Found => Temp_Found,
404
Indicator => Indicator,
405
Static_Inx => Static_Inx);
406
Add_Selector (To_Wide_String (Temp_Sel));
407
if Last_Deref = 0 then
408
Last_Deref := Temp_Deref;
410
Inx_Found := Inx_State'Max (Inx_Found, Temp_Found);
414
R_Indicator : Wide_Character;
416
Split (Left, L_Variable, L_Selectors, L_Deref, L_Inx_Found,
418
Static_Inx => False);
423
when Possible | Unlikely =>
424
-- Use the same indicator as for Left
425
-- => all indexings and function calls will match
428
Split (Right, R_Variable, R_Selectors, R_Deref, R_Inx_Found,
429
Indicator => R_Indicator,
430
Static_Inx => False);
432
if L_Inx_Found = Static and R_Inx_Found = Static then
433
-- Both are indexed, and only with static indices
434
-- => Resplit with the actual values of indices
435
Split (Left, L_Variable, L_Selectors, L_Deref, L_Inx_Found,
438
Split (Right, R_Variable, R_Selectors, R_Deref, R_Inx_Found,
439
Indicator => R_Indicator,
444
-- X_Head is the part of the selectors up to and including the last ".all"
445
-- X_Tail is the remaining of the string
446
L_Head : constant Wide_String := Slice (L_Selectors, 1, L_Deref);
447
L_Tail : constant Wide_String := Slice (L_Selectors, L_Deref+1, Length (L_Selectors));
448
R_Head : constant Wide_String := Slice (R_Selectors, 1, R_Deref);
449
R_Tail : constant Wide_String := Slice (R_Selectors, R_Deref+1, Length (R_Selectors));
451
if Is_Equal (L_Variable, R_Variable) and L_Head = R_Head then
452
if L_Tail'Length > R_Tail'Length then
453
return L_Tail (L_Tail'First .. L_Tail'First + R_Tail'Length - 1) = R_Tail;
455
return R_Tail (R_Tail'First .. R_Tail'First + L_Tail'Length - 1) = L_Tail;
459
when Certain | Possible =>
462
return L_Head /= "" or R_Head /= "";
173
use Thick_Queries, Framework.Reports, Ada.Strings.Wide_Unbounded;
469
176
if Rule_Used = (Rule_Detail => False) then
494
207
if Element_Kind (Name) = A_Defining_Name then
495
208
return '"' & Defining_Name_Image (Name) & " => "
496
& Trim (Element_Image (Actual_Parameter (Actuals (Position))), Both) & '"';
209
& Trim (Element_Image (Actual_Parameter (Actuals (Position))), Both) & '"';
498
211
return '"' & Name_Image (Name) & " => "
499
& Trim (Element_Image (Actual_Parameter (Actuals (Position))), Both) & '"';
212
& Trim (Element_Image (Actual_Parameter (Actuals (Position))), Both) & '"';
501
214
end Association_Image;
503
216
Mode : Mode_Kinds;
504
TCP_Top : Asis_Natural := To_Check_Parameters'First - 1;
217
TCP_Top : ASIS_Natural := To_Check_Parameters'First - 1;
506
219
pragma Warnings (Off, To_Check_Parameters);
507
220
-- GNAT warns that To_Check_Parameters may be used before it has a value,
508
-- but the algorithm ensures that this does not happen
221
-- but the algorithm ensures that this does not happen, because the loop on J
222
-- is not executed the first time.
224
Param_Proximity : Proximity;
510
226
for I in Actuals'Range loop
511
227
Mode := Mode_Kind (Enclosing_Element (Formal_Name (Call, I)));
513
229
if Mode in An_Out_Mode .. An_In_Out_Mode then
514
for J in To_Check_Parameters'First .. TCP_Top loop
515
for Detail in Rule_Detail loop
516
if Rule_Used (Detail) and then
517
Are_Aliased (Actual_Parameter (Actuals (To_Check_Parameters (J))),
518
Actual_Parameter (Actuals (I)),
522
To_Wide_String (Rule_Label (Detail)),
525
Choose (Detail = Certain,
527
Choose (Detail = Possible,
530
& " aliasing between parameters "
531
& Association_Image (To_Check_Parameters (J))
533
& Association_Image (I)
536
-- If we found a stronger aliasing, don't check weaker ones
230
for J in List_Index range To_Check_Parameters'First .. TCP_Top loop
231
Param_Proximity := Variables_Proximity (Actual_Parameter (Actuals (To_Check_Parameters (J))),
232
Actual_Parameter (Actuals (I)));
233
if Rule_Used (Param_Proximity.Confidence) and then Param_Proximity.Overlap /= None then
235
To_Wide_String (Rule_Label (Param_Proximity.Confidence)),
236
Rule_Type (Param_Proximity.Confidence),
238
Choose (Param_Proximity.Confidence = Certain,
240
Choose (Param_Proximity.Confidence = Possible,
243
& " aliasing between parameters "
244
& Association_Image (To_Check_Parameters (J))
246
& Association_Image (I)
542
251
TCP_Top := TCP_Top + 1;