~ubuntu-branches/ubuntu/jaunty/adacontrol/jaunty

« back to all changes in this revision

Viewing changes to src/rules-simplifiable_expressions.adb

  • Committer: Bazaar Package Importer
  • Author(s): Ludovic Brenta
  • Date: 2006-08-24 08:44:11 UTC
  • Revision ID: james.westby@ubuntu.com-20060824084411-1r15uio1h75lqgpx
Tags: upstream-1.4r20
ImportĀ upstreamĀ versionĀ 1.4r20

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
----------------------------------------------------------------------
 
2
--  Rules.Simplifiable_Expressions - Package body                   --
 
3
--                                                                  --
 
4
--  This software  is (c) The European Organisation  for the Safety --
 
5
--  of Air  Navigation (EUROCONTROL) and Adalog  2004-2005. The Ada --
 
6
--  Controller  is  free software;  you can redistribute  it and/or --
 
7
--  modify  it under  terms of  the GNU  General Public  License as --
 
8
--  published by the Free Software Foundation; either version 2, or --
 
9
--  (at your  option) any later version.  This  unit is distributed --
 
10
--  in the hope  that it will be useful,  but WITHOUT ANY WARRANTY; --
 
11
--  without even the implied warranty of MERCHANTABILITY or FITNESS --
 
12
--  FOR A  PARTICULAR PURPOSE.  See the GNU  General Public License --
 
13
--  for more details.   You should have received a  copy of the GNU --
 
14
--  General Public License distributed  with this program; see file --
 
15
--  COPYING.   If not, write  to the  Free Software  Foundation, 59 --
 
16
--  Temple Place - Suite 330, Boston, MA 02111-1307, USA.           --
 
17
--                                                                  --
 
18
--  As  a special  exception, if  other files  instantiate generics --
 
19
--  from the units  of this program, or if you  link this unit with --
 
20
--  other files  to produce  an executable, this  unit does  not by --
 
21
--  itself cause the resulting executable  to be covered by the GNU --
 
22
--  General  Public  License.   This  exception  does  not  however --
 
23
--  invalidate any  other reasons why the executable  file might be --
 
24
--  covered by the GNU Public License.                              --
 
25
--                                                                  --
 
26
--  This  software is  distributed  in  the hope  that  it will  be --
 
27
--  useful,  but WITHOUT  ANY  WARRANTY; without  even the  implied --
 
28
--  warranty  of  MERCHANTABILITY   or  FITNESS  FOR  A  PARTICULAR --
 
29
--  PURPOSE.                                                        --
 
30
----------------------------------------------------------------------
 
31
 
 
32
-- Ada
 
33
with
 
34
  Ada.Strings.Wide_Unbounded;
 
35
 
 
36
-- Asis
 
37
with
 
38
  Asis,
 
39
  Asis.Declarations,
 
40
  Asis.Definitions,
 
41
  Asis.Elements,
 
42
  Asis.Expressions,
 
43
  Asis.Statements;
 
44
 
 
45
-- Adalog
 
46
with
 
47
  A4G_Bugs,
 
48
  Thick_Queries,
 
49
  Utilities;
 
50
 
 
51
-- Adactl
 
52
with
 
53
  Framework.Language,
 
54
  Framework.Rules_Manager,
 
55
  Framework.Reports;
 
56
 
 
57
package body Rules.Simplifiable_expressions is
 
58
   use Framework, Ada.Strings.Wide_Unbounded;
 
59
 
 
60
   type Keywords is (K_Range, K_Logical_True, K_Logical_False, K_Parentheses, K_Logical);
 
61
   subtype To_Check is Keywords range Keywords'First .. Keywords'Pred (K_Logical);
 
62
 
 
63
   type Usage_Entry is
 
64
      record
 
65
         Used  : Boolean := False;
 
66
         Label : Unbounded_Wide_String;
 
67
         end record;
 
68
   type Usages is array (To_Check) of Usage_Entry;
 
69
 
 
70
   Context   : array (Rule_Types) of Usages;
 
71
   Rule_Used : Boolean;
 
72
   Save_Used : Boolean;
 
73
 
 
74
   -----------
 
75
   -- Image --
 
76
   -----------
 
77
 
 
78
   function Image (Check : To_Check) return Wide_String is
 
79
      use Utilities;
 
80
      Img : constant Wide_String := To_Lower (To_Check'Wide_Image (Check));
 
81
   begin
 
82
      -- Remove "K_"
 
83
      return Img (3 .. Img'Last);
 
84
   end Image;
 
85
 
 
86
   ----------
 
87
   -- Help --
 
88
   ----------
 
89
 
 
90
   procedure Help is
 
91
      use Utilities;
 
92
   begin
 
93
      User_Message ("Rule: " & Rule_Id);
 
94
      User_Message ("Parameter(s): ranges | logical | logical_true | logical_false");
 
95
      User_Message ("              | parentheses (optional, default=all)");
 
96
      User_Message ("Control occurrence of various forms of expressions that could be made simpler:");
 
97
      User_Message ("  T'FIRST .. T'LAST that can be replaced by T'RANGE or T.");
 
98
      User_Message ("  <expression> = (/=) True/False");
 
99
      User_Message ("  if (<expression>) or case (<expression>)");
 
100
   end Help;
 
101
 
 
102
   -------------
 
103
   -- Add_Use --
 
104
   -------------
 
105
 
 
106
   procedure Add_Use (Label         : in Wide_String;
 
107
                      Rule_Use_Type : in Rule_Types) is
 
108
      use Ada.Strings.Wide_Unbounded, Framework.Language;
 
109
 
 
110
      Key : Keywords;
 
111
      function Get_Check_Parameter is new Get_Flag_Parameter (Flags     => Keywords,
 
112
                                                               Allow_Any => False,
 
113
                                                               Prefix    => "K_");
 
114
 
 
115
      procedure Add_Check (Check : To_Check) is
 
116
      begin
 
117
         if Context (Rule_Use_Type)(Check).Used then
 
118
            Parameter_Error ("Check already given for rule " & Rule_Id
 
119
                             & ": " & Image (Check));
 
120
         else
 
121
            Context (Rule_Use_Type)(Check) := (Used => True, Label => To_Unbounded_Wide_String (Add_Use.Label));
 
122
         end if;
 
123
      end Add_Check;
 
124
   begin
 
125
      if Parameter_Exists then
 
126
         while Parameter_Exists loop
 
127
            Key := Get_Check_Parameter;
 
128
            if Key = K_Logical then
 
129
               Add_Check (K_Logical_True);
 
130
               Add_Check (K_Logical_False);
 
131
            else
 
132
               Add_Check (Key);
 
133
            end if;
 
134
         end loop;
 
135
      else
 
136
         Add_Check (K_Range);
 
137
         Add_Check (K_Logical_True);
 
138
         Add_Check (K_Logical_False);
 
139
         Add_Check (K_Parentheses);
 
140
      end if;
 
141
      Rule_Used  := True;
 
142
   end Add_Use;
 
143
 
 
144
   -------------
 
145
   -- Command --
 
146
   -------------
 
147
 
 
148
   procedure Command (Action : Framework.Rules_Manager.Rule_Action) is
 
149
      use Framework.Rules_Manager;
 
150
   begin
 
151
      case Action is
 
152
         when Clear =>
 
153
            --The following aggregate hangs Gnat, but the explicit loop is OK...
 
154
            --Context   := (others => (others => (Used => False, Label => Null_Unbounded_Wide_String)));
 
155
            for I in Context'Range loop
 
156
               for J in Usages'Range loop
 
157
                  Context (I)(J) := (Used => False, Label => Null_Unbounded_Wide_String);
 
158
               end loop;
 
159
            end loop;
 
160
            Rule_Used := False;
 
161
         when Suspend =>
 
162
            Save_Used := Rule_Used;
 
163
            Rule_Used := False;
 
164
         when Resume =>
 
165
            Rule_Used := Save_Used;
 
166
     end case;
 
167
   end Command;
 
168
 
 
169
   ------------------
 
170
   -- Process_Call --
 
171
   ------------------
 
172
 
 
173
   procedure Process_Call (Call : in Asis.Expression) is
 
174
      use Ada.Strings.Wide_Unbounded, Asis,
 
175
          Asis.Elements, Asis.Expressions, Framework.Reports, Thick_Queries;
 
176
 
 
177
      type Param_Kind is (Static_True, Static_False, Expr);
 
178
      function "+" (Left : Wide_String) return Unbounded_Wide_String renames To_Unbounded_Wide_String;
 
179
 
 
180
      -- Message_Table (Operator, Left, Right)
 
181
      Message_Table : constant array (Operator_Kinds range An_Equal_Operator .. A_Not_Equal_Operator,
 
182
                                      Param_Kind,
 
183
                                      Param_Kind) of Unbounded_Wide_String
 
184
        := (An_Equal_Operator =>
 
185
              (Static_True =>
 
186
                 (Static_True  => +"Simplify expression: statically True",                    -- True = True
 
187
                  Static_False => +"Simplify expression: statically False",                   -- True = False
 
188
                  Expr         => +"Simplify expression 'True = <expr>' to just '<expr>'"),   -- True = <Expr>
 
189
               Static_False =>
 
190
                 (Static_True  => +"Simplify expression: statically False",                   -- False = True
 
191
                  Static_False => +"Simplify expression: statically True",                    -- False = False
 
192
                  Expr         => +"Simplify expression 'False = <expr>' to 'not <expr>'"),   -- False = <Expr>
 
193
               Expr =>
 
194
                 (Static_True  => +"Simplify expression '<expr> = True' to just '<expr>'",    -- <Expr>  = True
 
195
                  Static_False => +"Simplify expression '<expr> = False' to 'not <expr>'",    -- <Expr> = False
 
196
                  Expr         => +"")),                                                      -- <Expr> = <Expr>
 
197
            A_Not_Equal_Operator =>
 
198
              (Static_True =>
 
199
                 (Static_True  => +"Simplify expression: statically False",                   -- True /= True
 
200
                  Static_False => +"Simplify expression: statically True",                    -- True /= False
 
201
                  Expr         => + "Simplify expression 'True /= <expr>' to 'not <expr>'"),  -- True /= <Expr>
 
202
               Static_False =>
 
203
                 (Static_True  => +"Simplify expression: statically True",                    -- False /= True
 
204
                  Static_False => +"Simplify expression: statically False",                   -- False /= False
 
205
                  Expr         => +"Simplify expression 'False /= <expr>' to just '<expr>'"), -- False /= <Expr>
 
206
               Expr =>
 
207
                 (Static_True  => +"Simplify expression '<expr> /= True' to 'not <expr>'",    -- <Expr> /= True
 
208
                  Static_False => +"Simplify expression '<expr> /= False' to just '<expr>'",  -- <Expr> /= False
 
209
                  Expr         => +"")));                                                     -- <Expr> /= <Expr>
 
210
      Op : constant Asis.Operator_Kinds := Operator_Kind (Prefix (Call));
 
211
 
 
212
      function Get_Kind (Param : Asis.Expression) return Param_Kind is
 
213
         use Utilities;
 
214
      begin
 
215
         if Expression_Kind (Param) = An_Enumeration_Literal
 
216
           and then To_Upper (Full_Name_Image (Param)) = "STANDARD.FALSE"
 
217
         then
 
218
            return Static_False;
 
219
         elsif Expression_Kind (Param) = An_Enumeration_Literal
 
220
           and then To_Upper (Full_Name_Image (Param)) = "STANDARD.TRUE"
 
221
         then
 
222
            return Static_True;
 
223
         else
 
224
            return Expr;
 
225
         end if;
 
226
      end Get_Kind;
 
227
   begin
 
228
      if not Rule_Used then
 
229
         return;
 
230
      end if;
 
231
      Rules_Manager.Enter (Rule_Id);
 
232
 
 
233
      if Op in  An_Equal_Operator .. A_Not_Equal_Operator then
 
234
         declare
 
235
            P : constant Asis.Association_List := Function_Call_Parameters (Call);
 
236
            L : constant Param_Kind := Get_Kind (Actual_Parameter (P(1)));
 
237
            R : constant Param_Kind := Get_Kind (Actual_Parameter (P(2)));
 
238
         begin
 
239
            if Message_Table (Op, L, R) /= Null_Unbounded_Wide_String then
 
240
               -- Report the highest priority from Check/Search
 
241
               if Context (Check)(K_Logical_False).Used and then (L = Static_False or R = Static_False) then
 
242
                  Report (Rule_Id,
 
243
                          To_Wide_String (Context (Check)(K_Logical_False).Label),
 
244
                          Check,
 
245
                          Get_Location (Call),
 
246
                          To_Wide_String (Message_Table (Op, L, R)));
 
247
               elsif Context (Check)(K_Logical_True).Used and then (L = Static_True or R = Static_True) then
 
248
                  Report (Rule_Id,
 
249
                          To_Wide_String (Context (Check)(K_Logical_True).Label),
 
250
                          Check,
 
251
                          Get_Location (Call),
 
252
                          To_Wide_String (Message_Table (Op, L, R)));
 
253
               elsif Context (Search)(K_Logical_False).Used and then (L = Static_False or R = Static_False) then
 
254
                  Report (Rule_Id,
 
255
                          To_Wide_String (Context (Search)(K_Logical_False).Label),
 
256
                          Search,
 
257
                          Get_Location (Call),
 
258
                          To_Wide_String (Message_Table (Op, L, R)));
 
259
               elsif Context (Search)(K_Logical_True).Used and then (L = Static_True or R = Static_True) then
 
260
                  Report (Rule_Id,
 
261
                          To_Wide_String (Context (Search) (K_Logical_True).Label),
 
262
                          Search,
 
263
                          Get_Location (Call),
 
264
                          To_Wide_String (Message_Table (Op, L, R)));
 
265
               end if;
 
266
 
 
267
               -- Always report Count
 
268
               if Context (Count)(K_Logical_False).Used and then (L = Static_False or R = Static_False) then
 
269
                  Report (Rule_Id,
 
270
                          To_Wide_String (Context (Count) (K_Logical_False).Label),
 
271
                          Count,
 
272
                          Get_Location (Call),
 
273
                          To_Wide_String (Message_Table (Op, L, R)));
 
274
               elsif Context (Count)(K_Logical_True).Used and then (L = Static_True or R = Static_True) then
 
275
                  Report (Rule_Id,
 
276
                          To_Wide_String (Context (Count) (K_Logical_True).Label),
 
277
                          Count,
 
278
                          Get_Location (Call),
 
279
                          To_Wide_String (Message_Table (Op, L, R)));
 
280
               end if;
 
281
            end if;
 
282
         end;
 
283
      end if;
 
284
   end Process_Call;
 
285
 
 
286
   -------------------
 
287
   -- Process_Range --
 
288
   -------------------
 
289
 
 
290
   procedure Process_Range (Definition : in Asis.Definition) is
 
291
      use Ada.Strings.Wide_Unbounded, Asis, Asis.Declarations, Asis.Definitions,
 
292
        Asis.Elements, Asis.Expressions, Framework.Reports, Thick_Queries, Utilities;
 
293
 
 
294
      procedure Do_Reports (Message : Wide_String) is
 
295
      begin
 
296
         if Context (Check)(K_Range).Used then
 
297
            Report (Rule_Id,
 
298
                    To_Wide_String (Context (Check)(K_Range).Label),
 
299
                    Check,
 
300
                    Get_Location (Definition),
 
301
                    Message);
 
302
         elsif Context (Search)(K_Range).Used then
 
303
            Report (Rule_Id,
 
304
                    To_Wide_String (Context (Search)(K_Range).Label),
 
305
                    Search,
 
306
                    Get_Location (Definition),
 
307
                    Message);
 
308
         end if;
 
309
 
 
310
         if Context (Count)(K_Range).Used then
 
311
            Report (Rule_Id,
 
312
                    To_Wide_String (Context (Count)(K_Range).Label),
 
313
                    Count,
 
314
                    Get_Location (Definition),
 
315
                    Message);
 
316
         end if;
 
317
      end Do_Reports;
 
318
   begin
 
319
      if not Rule_Used then
 
320
         return;
 
321
      end if;
 
322
      Rules_Manager.Enter (Rule_Id);
 
323
 
 
324
      case Discrete_Range_Kind (Definition) is
 
325
         when A_Discrete_Simple_Expression_Range =>
 
326
            declare
 
327
               LB : constant Expression := Lower_Bound (Definition);
 
328
               UB : constant Expression := Upper_Bound (Definition);
 
329
            begin
 
330
               if A4G_Bugs.Attribute_Kind (LB) /= A_First_Attribute
 
331
                 or A4G_Bugs.Attribute_Kind (UB) /= A_Last_Attribute
 
332
               then
 
333
                  return;
 
334
               end if;
 
335
 
 
336
               -- Must deal with the following cases when determining 'T':-
 
337
               -- 1) T'FIRST
 
338
               -- 2) T'BASE'FIRST
 
339
               -- 3) T (A)'FIRST
 
340
               -- 4) T'FIRST (B)
 
341
               -- We must also not fall into the trap of recommending
 
342
               -- T'BASE'FIRST .. T'LAST or T'FIRST (X) .. T'LAST (Y) for simplification.
 
343
               declare
 
344
                  -- First we remove the 'FIRST and 'LAST attributes.
 
345
                  LP  : Asis.Expression := Prefix (LB);
 
346
                  UP  : Asis.Expression := Prefix (UB);
 
347
                  ALB : constant Expression_List := Attribute_Designator_Expressions (LB);
 
348
                  AUB : constant Expression_List := Attribute_Designator_Expressions (UB);
 
349
               begin
 
350
                  -- Both the first and last attributes must have either no attribute designators expressions
 
351
                  -- or else have the same value.
 
352
                  -- Take the Wide_Value below for the case of the naughty user who wrote something like
 
353
                  ---Tab'First (10#1#) .. Tab'Last (1).
 
354
                  -- Note that attribute designator expressions can only ever have a length of 0 or 1,
 
355
                  --      and are satic integers.
 
356
                  if ALB'LENGTH /= AUB'LENGTH
 
357
                    or else (ALB'LENGTH = 1  -- Implies AUB'LENGTH = 1
 
358
                             and then Asis_Integer'Wide_Value (Value_Image (ALB (1))) /=
 
359
                             Asis_Integer'Wide_Value (Value_Image (AUB (1))))
 
360
                  then
 
361
                     return;
 
362
                  end if;
 
363
 
 
364
                  -- Remove the 'BASE attribute but only if it is applied to both attributes.
 
365
                  if Expression_Kind (LP) = An_Attribute_Reference then
 
366
                     if Expression_Kind (UP) = An_Attribute_Reference  then
 
367
                        LP := Prefix (LP);
 
368
                        UP := Prefix (UP);
 
369
                     else
 
370
                        return;
 
371
                     end if;
 
372
                  elsif Expression_Kind (UP) = An_Attribute_Reference then
 
373
                     return;
 
374
                  end if;
 
375
 
 
376
                  -- Remove indexings and selectors for record elements.
 
377
                  -- If in doubt, give up.
 
378
                  loop
 
379
                     case Expression_Kind (LP) is
 
380
                        when An_Identifier =>
 
381
                           exit;
 
382
 
 
383
                        when A_Selected_Component =>
 
384
                           case Declaration_Kind (Corresponding_Name_Declaration (Selector (LP))) is
 
385
                              when A_Component_Declaration | A_Discriminant_Specification =>
 
386
                                 if Expression_Kind (UP) /= A_Selected_Component then
 
387
                                    return;
 
388
                                 end if;
 
389
 
 
390
                                 -- It's a record field, a protected type field...
 
391
                                 if Is_Equal (Corresponding_Name_Declaration (Selector (LP)),
 
392
                                              Corresponding_Name_Declaration (Selector (UP)))
 
393
                                 then
 
394
                                    LP := Prefix (LP);
 
395
                                    UP := Prefix (UP);
 
396
                                 else
 
397
                                    return;
 
398
                                 end if;
 
399
                              when A_Variable_Declaration
 
400
                                | An_Object_Renaming_Declaration
 
401
                                | A_Subtype_Declaration
 
402
                                | An_Ordinary_Type_Declaration
 
403
                                =>
 
404
                                 -- Its a Pack.Var or Pack.T selector
 
405
                                 exit;
 
406
                              when others =>
 
407
                                 Failure ("Wrong selected component",
 
408
                                          Corresponding_Name_Declaration (Selector (LP)));
 
409
                           end case;
 
410
 
 
411
                        when An_Indexed_Component =>
 
412
                           if Expression_Kind (UP) /= An_Indexed_Component then
 
413
                              return;
 
414
                           end if;
 
415
 
 
416
                           -- Check that the indexing expressions are statically the same.
 
417
                           -- We currently recognize as identical indexing expressions that are:
 
418
                           --   - Integer litterals
 
419
                           --   - Enumeration litterals
 
420
                           --   - Identical constants and loop control parameters
 
421
                           declare
 
422
                              L_Indexers : constant Asis.Expression_List := Index_Expressions (LP);
 
423
                              U_Indexers : constant Asis.Expression_List := Index_Expressions (UP);
 
424
                           begin
 
425
                              if L_Indexers'Length /= U_Indexers'Length then
 
426
                                 return;
 
427
                              end if;
 
428
                              for I in L_Indexers'Range loop
 
429
                                 if Expression_Kind (L_Indexers (I)) /= Expression_Kind (U_Indexers (I)) then
 
430
                                    return;
 
431
                                 end if;
 
432
 
 
433
                                 case Expression_Kind (L_Indexers (I)) is
 
434
                                    when An_Integer_Literal =>
 
435
                                       if Asis_Integer'Wide_Value (Value_Image (L_Indexers (I)))
 
436
                                         /= Asis_Integer'Wide_Value (Value_Image (U_Indexers (I)))
 
437
                                       then
 
438
                                          return;
 
439
                                       end if;
 
440
                                    when An_Enumeration_Literal =>
 
441
                                       if To_Upper (Value_Image (L_Indexers (I)))
 
442
                                         /= To_Upper (Value_Image (U_Indexers (I)))
 
443
                                       then
 
444
                                          return;
 
445
                                       end if;
 
446
                                    when An_Identifier =>
 
447
                                       case Declaration_Kind (Corresponding_Name_Declaration (L_Indexers (I))) is
 
448
                                          when A_Constant_Declaration
 
449
                                            | A_Deferred_Constant_Declaration
 
450
                                            | A_Loop_Parameter_Specification
 
451
                                            =>
 
452
                                             if not Is_Equal (Corresponding_Name_Definition (L_Indexers (I)),
 
453
                                                              Corresponding_Name_Definition (U_Indexers (I)))
 
454
                                             then
 
455
                                                return;
 
456
                                             end if;
 
457
                                          when others =>
 
458
                                             return;
 
459
                                       end case;
 
460
                                    when others =>
 
461
                                       return;
 
462
                                 end case;
 
463
                              end loop;
 
464
                           end;
 
465
 
 
466
                           -- Here, both indexings are the same
 
467
                           LP := Prefix (LP);
 
468
                           UP := Prefix (UP);
 
469
 
 
470
                        when others =>
 
471
                          Failure ("Unexpected expression kind", LP);
 
472
                     end case;
 
473
 
 
474
                  end loop;
 
475
 
 
476
                  -- If we still have a selected name, the prefixes are packages
 
477
                  -- => Get rid of them
 
478
                  -- To be honnest: maybe not for UP, but then it will fail later
 
479
                  if Expression_Kind (LP) = A_Selected_Component then
 
480
                     LP := Selector (LP);
 
481
                  end if;
 
482
                  if Expression_Kind (UP) = A_Selected_Component then
 
483
                     UP := Selector (UP);
 
484
                  end if;
 
485
 
 
486
                  -- Here we have a "clean" name for lower/upper prefix
 
487
                  -- Check the full expanded names of both bounds.
 
488
                  if Full_Name_Image (LP) = Full_Name_Image (UP) then
 
489
                     case Declaration_Kind (Corresponding_Name_Declaration (LP)) is
 
490
                        when A_Subtype_Declaration
 
491
                          | An_Ordinary_Type_Declaration
 
492
                          | A_Formal_Type_Declaration
 
493
                          =>
 
494
                           Do_Reports ("(T)'First .. (T)'Last replaceable with (sub)type(T)");
 
495
                        when A_Variable_Declaration
 
496
                          | A_Constant_Declaration
 
497
                          | An_Object_Renaming_Declaration
 
498
                          | A_Deferred_Constant_Declaration
 
499
                          | A_Formal_Object_Declaration
 
500
                          | A_Parameter_Specification
 
501
                          | A_Component_Declaration
 
502
                          =>
 
503
                           Do_Reports ("(T)'First .. (T)'Last replaceable with (T)'Range");
 
504
                        when others =>
 
505
                           Failure ("Unexpected Element_Kind 1: " &
 
506
                                    Declaration_Kinds'WIDE_IMAGE (Declaration_Kind
 
507
                                                                  (Corresponding_Name_Declaration
 
508
                                                                   (LP))));
 
509
                     end case;
 
510
                  end if;
 
511
               end;
 
512
            end;
 
513
 
 
514
         when A_Discrete_Range_Attribute_Reference =>
 
515
            -- We are interested only in the case where the prefix is a (sub)type
 
516
            declare
 
517
               P : Asis.Expression := Prefix (Range_Attribute (Definition));
 
518
               Decl : Asis.Declaration;
 
519
               Def  : Asis.Definition;
 
520
            begin
 
521
               case Expression_Kind (P) is
 
522
                  when An_Identifier =>
 
523
                     null;
 
524
                  when A_Selected_Component =>
 
525
                     -- Could be Pack.T
 
526
                     P := Selector (P);
 
527
                  when others =>
 
528
                     -- Prefix cannot denote a (sub)type
 
529
                     return;
 
530
               end case;
 
531
 
 
532
               -- Get rid of subtypes
 
533
               Decl := Corresponding_Name_Declaration (P);
 
534
               if Declaration_Kind (Decl) = A_Subtype_Declaration then
 
535
                  Decl := Corresponding_First_Subtype (Decl);
 
536
               end if;
 
537
 
 
538
               case Declaration_Kind (Decl) is
 
539
                  when An_Ordinary_Type_Declaration
 
540
                    | A_Formal_Type_Declaration
 
541
                    =>
 
542
                     -- Get rid of derived types, including formal derived type
 
543
                     -- We can of course have a type derived from a formal derived type,
 
544
                     -- and conversely. To any depth.
 
545
                     Def := Type_Declaration_View (Decl);
 
546
                     loop
 
547
                        if Type_Kind (Def) in A_Derived_Type_Definition .. A_Derived_Record_Extension_Definition then
 
548
                           Def := Type_Declaration_View (Corresponding_Root_Type (Def));
 
549
                        elsif Formal_Type_Kind (Def) = A_Formal_Derived_Type_Definition then
 
550
                           Def := Type_Declaration_View (Corresponding_First_Subtype
 
551
                                                         (Corresponding_Name_Declaration
 
552
                                                          (Definitions.Subtype_Mark (Def))));
 
553
                        else
 
554
                           exit;
 
555
                        end if;
 
556
                     end loop;
 
557
 
 
558
                     case Type_Kind (Def) is
 
559
                        when An_Enumeration_Type_Definition
 
560
                          | A_Signed_Integer_Type_Definition
 
561
                          | A_Modular_Type_Definition
 
562
                          | A_Floating_Point_Definition
 
563
                          | An_Ordinary_Fixed_Point_Definition
 
564
                          | A_Decimal_Fixed_Point_Definition
 
565
                          =>
 
566
                           Do_Reports ("(T)'RANGE replaceable with (sub)type(T)");
 
567
                        when An_Unconstrained_Array_Definition
 
568
                          | A_Constrained_Array_Definition
 
569
                          =>
 
570
                           null;
 
571
                        when Not_A_Type_Definition =>
 
572
                           -- Can  be a formal type here
 
573
                           case Formal_Type_Kind (Def) is
 
574
                              when A_Formal_Discrete_Type_Definition
 
575
                                | A_Formal_Signed_Integer_Type_Definition
 
576
                                | A_Formal_Modular_Type_Definition
 
577
                                | A_Formal_Floating_Point_Definition
 
578
                                | A_Formal_Ordinary_Fixed_Point_Definition
 
579
                                | A_Formal_Decimal_Fixed_Point_Definition
 
580
                                =>
 
581
                                 Do_Reports ("(T)'RANGE replaceable with (sub)type(T)");
 
582
                              when A_Formal_Unconstrained_Array_Definition
 
583
                                | A_Formal_Constrained_Array_Definition
 
584
                                =>
 
585
                                 null;
 
586
                              when others =>
 
587
                                 Failure ("Unexpected formal type kind: "
 
588
                                          & Formal_Type_Kinds'Wide_Image(Formal_Type_Kind (Def)), P);
 
589
                           end case;
 
590
 
 
591
                        when others =>
 
592
                           Failure ("Unexpected type kind: " & Type_Kinds'Wide_Image(Type_Kind (Def)), P);
 
593
                     end case;
 
594
 
 
595
                  when A_Variable_Declaration
 
596
                    | A_Constant_Declaration
 
597
                    | A_Deferred_Constant_Declaration
 
598
                    | A_Formal_Object_Declaration
 
599
                    | A_Parameter_Specification
 
600
                    | A_Component_Declaration
 
601
                    | An_Object_Renaming_Declaration
 
602
                    =>
 
603
                     null;
 
604
                  when others =>
 
605
                     Failure ("Unexpected Element_Kind 2: " &
 
606
                              Declaration_Kinds'Wide_Image (Declaration_Kind
 
607
                                                            (Corresponding_Name_Declaration
 
608
                                                             (P))));
 
609
               end case;
 
610
            end;
 
611
 
 
612
         when A_Discrete_Subtype_Indication =>
 
613
            -- Nothing simplifiable here
 
614
            null;
 
615
 
 
616
         when Not_A_Discrete_Range =>
 
617
            Failure ("Not a discrete range");
 
618
      end case;
 
619
   end Process_Range;
 
620
 
 
621
   ------------------------
 
622
   -- Process_Case_Or_If --
 
623
   ------------------------
 
624
 
 
625
   procedure Process_Case_Or_If (Stmt : in Asis.Element) is
 
626
      use Asis, Asis.Elements, Asis.Statements, Utilities, Framework.Reports;
 
627
 
 
628
      Expr : Asis.Expression;
 
629
      Message : constant Wide_String := "Unnecessary parentheses in expression of ""if"" or ""case""";
 
630
   begin
 
631
      if not Rule_Used then
 
632
         return;
 
633
      end if;
 
634
      Rules_Manager.Enter (Rule_Id);
 
635
 
 
636
      if Statement_Kind (Stmt) = A_Case_Statement then
 
637
         Expr := Case_Expression (Stmt);
 
638
      elsif Path_Kind (Stmt) in An_If_Path .. An_Elsif_Path then
 
639
         Expr := Condition_Expression (Stmt);
 
640
      else
 
641
         Failure ("Not a case or if statement");
 
642
      end if;
 
643
 
 
644
      if Expression_Kind (Expr) = A_Parenthesized_Expression then
 
645
         if Context (Check)(K_Parentheses).Used then
 
646
            Report (Rule_Id,
 
647
                    To_Wide_String (Context (Check)(K_Parentheses).Label),
 
648
                    Check,
 
649
                    Get_Location (Stmt),
 
650
                    Message);
 
651
         elsif Context (Search)(K_Parentheses).Used then
 
652
            Report (Rule_Id,
 
653
                    To_Wide_String (Context (Search)(K_Parentheses).Label),
 
654
                    Search,
 
655
                    Get_Location (Stmt),
 
656
                    Message);
 
657
         end if;
 
658
 
 
659
         if Context (Count)(K_Parentheses).Used then
 
660
            Report (Rule_Id,
 
661
                    To_Wide_String (Context (Count)(K_Parentheses).Label),
 
662
                    Count,
 
663
                    Get_Location (Stmt),
 
664
                    Message);
 
665
         end if;
 
666
      end if;
 
667
   end Process_Case_Or_If;
 
668
 
 
669
begin
 
670
   Framework.Rules_Manager.Register (Rule_Id,
 
671
                                     Help    => Help'Access,
 
672
                                     Add_Use => Add_Use'Access,
 
673
                                     Command => Command'Access);
 
674
end Rules.Simplifiable_expressions;