File: ad-queries.adb

package info (click to toggle)
adabrowse 4.0.3-5
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 2,364 kB
  • sloc: ada: 29,770; makefile: 137; ansic: 4
file content (1013 lines) | stat: -rw-r--r-- 37,888 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
-------------------------------------------------------------------------------
--
--  This file is part of AdaBrowse.
--
-- <STRONG>Copyright (c) 2002 by Thomas Wolf.</STRONG>
-- <BLOCKQUOTE>
--    AdaBrowse is free software; you can redistribute it and/or modify it
--    under the terms of the  GNU General Public License as published by the
--    Free Software  Foundation; either version 2, or (at your option) any
--    later version. AdaBrowse is distributed in the hope that it will be
--    useful, but <EM>without any warranty</EM>; without even the implied
--    warranty of <EM>merchantability or fitness for a particular purpose.</EM>
--    See the GNU General Public License for  more details. You should have
--    received a copy of the GNU General Public License with this distribution,
--    see file "<A HREF="GPL.txt">GPL.txt</A>". If not, write to the Free
--    Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
--    USA.
-- </BLOCKQUOTE>
--
-- <DL><DT><STRONG>
-- Author:</STRONG><DD>
--   Thomas Wolf  (TW)
--   <ADDRESS><A HREF="mailto:twolf@acm.org">twolf@acm.org</A></ADDRESS></DL>
--
-- <DL><DT><STRONG>
-- Purpose:</STRONG><DD>
--   Complex ASIS queries.</DL>
--
-- <!--
-- Revision History
--
--   02-FEB-2002   TW  First release.
--   05-FEB-2002   TW  Added handling of subprogram renamings in
--                     'Primitive_Operations'.
--   02-MAR-2002   TW  Correction (parameter 'Handled' in 'Get_Primitive_Ops')
--                     to avoid handling types derived from Natural etc.
--                     ASIS-for-GNAT 3.14p doesn't give us the predefined ops,
--                     so it doesn't work as it should for these types.
--   04-MAR-2002   TW  Correction in 'Primitive_Operations': use first subtypes
--                     for type comparisons!
--                        Also added 'Full_Unit_Name'.
--   21-MAR-2002   TW  Corrected 'Type_Of': we need to explicitly maintain
--                     attributes like 'Class, 'Base, or 'Range, for which
--                     ASIS-for-GNAT 3.14p (and maybe other ASIS
--                     implementations, too) returns a Nil_Element because
--                     there is no declaration.
--   27-AUG-2002   TW  Added 'Real_Declaration' to find the explicit
--                     declarations of implicitly inherited enumeration
--                     literals.
--                       'Expand_Generic' loops now until the found element is
--                     not part of an instance anymore. This should handle
--                     nested generics properly.
--   22-NOV-2002   TW  New operation 'Container_Name'.
--   04-JUN-2003   TW  New operations 'Has_Private', 'Visible_Items' and.
--                     'Private_Items'.
--   05-JUN-2003   TW  Changed 'Enclosing_Declaration' such that it works
--                     for any element, not just defining names.
--   06-JUN-2003   TW  'Verify_Defining_Name', 'Name_Definition_Image', and
--                     'Name_Expression_Image' are new.
--                        Also changed 'Full_Unit_Name' to construct the unit
--                     name from scratch to get consistent casing.
--                        Moved 'Set_Standard_Units' and 'Crossref_To_Unit'
--                     to AD.Crossrefs.
--   08-JUL-2003   TW  New operation 'Fully_Qualified_Name'; change in function
--                     'Name_Expression_Image' to also handle attribute
--                     references.
--   18-JUL-2003   TW  Moved most operations to the @Asis2@ library.
-- -->
-------------------------------------------------------------------------------

pragma License (GPL);

with Ada.Unchecked_Deallocation;

with Asis;
with Asis.Compilation_Units.Relations;
with Asis.Definitions;
with Asis.Declarations;
with Asis.Elements;
with Asis.Expressions;

with Asis2.Declarations;
with Asis2.Naming;
with Asis2.Text;

with AD.Text_Utilities;

with GAL.Containers.Hash_Tables;
with GAL.Storage.Standard;
with GAL.Support.Hashing;

pragma Elaborate_All (GAL.Containers.Hash_Tables);

package body AD.Queries is

   package A_D   renames Asis.Declarations;
   package A_DEF renames Asis.Definitions;

   use Asis;
   use Asis.Definitions;
   use Asis.Declarations;
   use Asis.Elements;
   use Asis.Expressions;

   type Op_Ptr is access all Operation_List;

   procedure Free is
      new Ada.Unchecked_Deallocation (Operation_List, Op_Ptr);

   type Type_Attribute is (Base_Attr, Class_Attr, Range_Attr);
   --  Possible combinations:
   --  'Range'Base, 'Base'Range, 'Class, 'Base'Class (?)

   type Attribute_Set is array (Type_Attribute) of Boolean;
   pragma Pack (Attribute_Set);

   No_Attributes : constant Attribute_Set := (others => False);

   type Type_Descriptor is
      record
         Decl  : Asis.Declaration;
         Attrs : Attribute_Set;
      end record;

   No_Type : constant Type_Descriptor := (Nil_Element, No_Attributes);

   function Type_Of
     (Element : in Asis.Expression)
     return Type_Descriptor
   is
      --  Asis sometimes works ok with Corresponding_Name_Declaration, but
      --  in some other cases (e.g. for parameter types), it sometimes fails.
      --    Note that Asis doesn't seem to have a way to determine whether
      --  a particular type is a class-wide type! If 'Class_Wide_To_Specific'
      --  is True, we return in such cases the corresponding specific type,
      --  otherwise, we return a Nil_Element ('Corresponding_Expression_Type'
      --  is defined to return a Nil_Element for class-wide types).
      Result : Type_Descriptor := No_Type;
   begin
      case Expression_Kind (Element) is
         when A_Selected_Component =>
            Result.Decl := Corresponding_Name_Declaration (Selector (Element));

         when An_Identifier =>
            Result.Decl := Corresponding_Name_Declaration (Element);

         when An_Attribute_Reference =>
            case Attribute_Kind (Element) is
               when A_Class_Attribute =>
                  Result := Type_Of (Prefix (Element));
                  Result.Attrs (Class_Attr) := True;
               when A_Base_Attribute =>
                  Result := Type_Of (Prefix (Element));
                  Result.Attrs (Base_Attr) := True;
               when A_Range_Attribute =>
                  Result := Type_Of (Prefix (Element));
                  Result.Attrs (Range_Attr) := True;
               when others =>
                  Result.Decl := Corresponding_Expression_Type (Element);
            end case;

         when others =>
            Result.Decl := Corresponding_Expression_Type (Element);

      end case;
      return Result;
   exception
      when others =>
         return No_Type;
   end Type_Of;

   function First_Subtype
     (The_Type : in Type_Descriptor)
     return Type_Descriptor
   is
   begin
      if not Is_Nil (The_Type.Decl) and then
         The_Type.Attrs = No_Attributes
      then
         return (Corresponding_First_Subtype (The_Type.Decl), No_Attributes);
      else
         return The_Type;
      end if;
   end First_Subtype;

   function Ancestor_Type
     (The_Type : in Declaration)
     return Declaration
   is
      --  Return the ancestor type (if any) of the type declared by the given
      --  declaration. Returns a Nil_Element if the type has no ancestor.

      Def : constant Definition := Type_Declaration_View (The_Type);

   begin --  Ancestor_Type
      case Definition_Kind (Def) is
         when A_Subtype_Indication =>
            --  Can happen in generic instantiations, for the actual parameter
            --  of a formal derived type definition.
            return Type_Of (A_DEF.Subtype_Mark (Def)).Decl;

         when A_Private_Extension_Definition =>
            return
              Type_Of (A_DEF.Subtype_Mark
                         (Ancestor_Subtype_Indication (Def))).Decl;

         when A_Formal_Type_Definition =>
            if Formal_Type_Kind (Def) = A_Formal_Derived_Type_Definition then
               return Type_Of (A_DEF.Subtype_Mark (Def)).Decl;
            end if;

         when A_Type_Definition =>
            case Type_Kind (Def) is
               when A_Derived_Type_Definition |
                    A_Derived_Record_Extension_Definition =>
                  return Corresponding_Parent_Subtype (Def);
               when others =>
                  null;
            end case;

         when others =>
            null;

      end case;
      return Nil_Element;
   end Ancestor_Type;

   function Is_Ancestor
     (Ancestor : in Declaration;
      Child    : in Declaration)
     return Boolean
   is
      --  Return True if 'Child' is derived directly or indirectly from
      --  'Ancestor'.

   begin --  Is_Ancestor
      if Is_Nil (Child)             then return False; end if;
      if Is_Equal (Ancestor, Child) then return True;  end if;
      return Is_Ancestor (Ancestor, Ancestor_Type (Child));
   end Is_Ancestor;

   function Is_Tagged
     (The_Type : in Declaration)
     return Boolean
   is
      Def : constant Definition := Type_Declaration_View (The_Type);
   begin
      case Definition_Kind (Def) is
         when A_Subtype_Indication =>
            return Is_Tagged (Corresponding_First_Subtype (The_Type));

         when A_Private_Extension_Definition |
              A_Tagged_Private_Type_Definition =>
            return True;

         when A_Type_Definition =>
            case Type_Kind (Def) is
               when A_Derived_Record_Extension_Definition |
                    A_Tagged_Record_Type_Definition =>
                  return True;

               when others =>
                  null;

            end case;

         when others =>
            null;

      end case;
      return False;
   end Is_Tagged;

   function Is_Primitive
     (Decl     : in Declaration;
      The_Type : in Declaration)
     return Boolean
   is
      --  'Decl' is the declaration of a function or procedure. Returns
      --  True if it is a primitive operation of 'The_Type'.
      --
      --  Must have a parameter or a return type that equals 'The_Type'.

      Kind : constant Declaration_Kinds := Declaration_Kind (Decl);
   begin
      if (Kind = A_Function_Renaming_Declaration or else
          Kind = A_Procedure_Renaming_Declaration)
         and then
         Is_Tagged (The_Type) and then
         not Is_Dispatching_Operation (Decl)
      then
         return False;
      end if;
      if Kind = A_Function_Declaration or else
         Kind = A_Function_Renaming_Declaration
      then
         declare
            T : constant Type_Descriptor := Type_Of (Result_Profile (Decl));
         begin
            if T.Attrs = No_Attributes and then
               Is_Equal (T.Decl, The_Type)
            then
               return True;
            end if;
         end;
      end if;
      declare
         Params : constant Parameter_Specification_List :=
           Parameter_Profile (Decl);
      begin
         for I in Params'Range loop
            declare
               T : constant Type_Descriptor :=
                 Type_Of (Declaration_Subtype_Mark (Params (I)));
            begin
               if T.Attrs = No_Attributes and then
                  Is_Equal (T.Decl, The_Type)
               then
                  return True;
               end if;
            end;
         end loop;
      end;
      return False;
   end Is_Primitive;

   function Primitive_Operations
     (The_Type : in Declaration)
     return Operation_List
   is

      procedure Get_Primitive_Ops
        (The_Type : in     Declaration;
         Result   : in out Op_Ptr;
         Length   : in out Natural;
         Used     : in out Natural;
         Handled  :    out Boolean)
      is

         function Find
           (Table    : in Op_Ptr;
            Used     : in Natural;
            Decl     : in Declaration;
            The_Type : in Declaration)
           return Natural
         is

            function Equals
              (Original : in Operation_Description;
               New_One  : in Declaration;
               The_Type : in Declaration)
              return Boolean
            is
               --  Return true if the names are equal and all types and modes
               --  are equal for all parameters except those where 'New_One'
               --  has a 'The_Type', in which case only the mode must match.

               type Parameter_Description is
                  record
                     Mode      : Mode_Kinds;
                     Is_Access : Boolean;
                     The_Type  : Type_Descriptor;
                  end record;

               type Subprogram_Profile is
                 array (Positive range <>) of Parameter_Description;

               function Normalize
                 (List : in Parameter_Specification_List)
                 return Subprogram_Profile
               is
                  --  Two-pass strategy: first count the names, the reserve
                  --  a large-enough result array, then fill it in.

                  N : Natural;

               begin --  Normalize
                  if List'Last < List'First then
                     declare
                        Nil_Result : constant Subprogram_Profile (1 .. 0) :=
                          (others => (A_Default_In_Mode, False, No_Type));
                     begin
                        return Nil_Result;
                     end;
                  end if;
                  --  First pass:
                  N := 0;
                  for I in List'Range loop
                     declare
                        Names : constant Name_List := A_D.Names (List (I));
                     begin
                        N := N + Names'Length;
                     end;
                  end loop;
                  declare
                     Result : Subprogram_Profile (1 .. N);
                  begin
                     N := 1;
                     for I in List'Range loop
                        declare
                           Names : constant Name_List := A_D.Names (List (I));
                        begin
                           Result (N).Mode      :=
                             Mode_Kind (List (I));
                           if Result (N).Mode = An_In_Mode then
                              --  We don't care about this distinction!
                              Result (N).Mode := A_Default_In_Mode;
                           end if;
                           Result (N).Is_Access :=
                             (Trait_Kind (List (I)) =
                              An_Access_Definition_Trait);
                           declare
                              ST : constant Asis.Element :=
                                Declaration_Subtype_Mark (List (I));
                              T : constant Type_Descriptor :=
                                First_Subtype (Type_Of (ST));
                           begin
                              Result (N).The_Type := T;
                           end;
                           for J in N + 1 .. N + Names'Length - 1 loop
                              Result (N + 1) := Result (N);
                              N := N + 1;
                           end loop;
                           N := N + 1;
                        end;
                     end loop;
                     return Result;
                  end;
               end Normalize;

               function Is_Equal
                 (Old_Param : in Parameter_Description;
                  New_Param : in Parameter_Description;
                  The_Type  : in Declaration)
                 return Boolean
               is
               begin
                  if Old_Param.Mode /= New_Param.Mode or else
                     Old_Param.Is_Access /= New_Param.Is_Access or else
                     Old_Param.The_Type.Attrs /= New_Param.The_Type.Attrs
                  then
                     return False;
                  end if;
                  if Is_Equal (New_Param.The_Type.Decl, The_Type) and then
                     New_Param.The_Type.Attrs = No_Attributes
                  then
                     --  This is not 100% correct (we'd actually need to
                     --  do the substitutions on the way down and check here
                     --  that 'The_Type' is a direct descendant of the old
                     --  type), but it's good enough.
                     return Is_Ancestor (Old_Param.The_Type.Decl, The_Type);
                  else
                     return Is_Equal (New_Param.The_Type.Decl,
                                      Old_Param.The_Type.Decl);
                  end if;
               end Is_Equal;

               D_K_New     : constant Declaration_Kinds :=
                 Declaration_Kind (New_One);
               D_K_Old     : constant Declaration_Kinds :=
                 Declaration_Kind (Original.Decl);
               Is_Func_New : constant Boolean :=
                 D_K_New = A_Function_Declaration or else
                 D_K_New = A_Function_Renaming_Declaration;
               Is_Func_Old : constant Boolean :=
                 D_K_Old = A_Function_Declaration or else
                 D_K_Old = A_Function_Renaming_Declaration;

            begin --  Equals
               if Is_Func_New /= Is_Func_Old then
                  --  Either both are functions, or both are procedures.
                  return False;
               end if;
               --  Name equivalence:
               declare
                  use Asis2.Naming;
                  Name_Orig : constant Wide_String :=
                    Asis2.Text.To_Lower
                      (Name_Definition_Image (Get_Name (Original.Decl)));
                  Name_New  : constant Wide_String :=
                    Asis2.Text.To_Lower
                      (Name_Definition_Image (Get_Name (New_One)));
               begin
                  if Name_Orig /= Name_New then return False; end if;
               end;
               --  Parameters
               declare
                  New_Profile : constant Subprogram_Profile :=
                    Normalize (Parameter_Profile (New_One));
                  Old_Profile : constant Subprogram_Profile :=
                    Normalize (Parameter_Profile (Original.Decl));
                  J : Natural;
               begin
                  if New_Profile'Length /= Old_Profile'Length then
                     return False;
                  end if;
                  J := Old_Profile'First;
                  for I in New_Profile'Range loop
                     if not Is_Equal
                              (Old_Profile (J), New_Profile (I), The_Type)
                     then
                        return False;
                     end if;
                     J := J + 1;
                  end loop;
               end;
               --  Return type, if any.
               if Is_Func_New then
                  declare
                     New_Type   : constant Type_Descriptor :=
                       First_Subtype (Type_Of (Result_Profile (New_One)));
                     Old_Type   : constant Type_Descriptor :=
                       First_Subtype
                         (Type_Of (Result_Profile (Original.Decl)));
                  begin
                     if Is_Equal (The_Type, New_Type.Decl) and then
                        New_Type.Attrs = No_Attributes
                     then
                        return Original.Is_Controlling_Result;
                     else
                        return New_Type.Attrs = Old_Type.Attrs and then
                               Is_Equal (New_Type.Decl, Old_Type.Decl);
                     end if;
                  end;
               else
                  return True;
               end if;
            end Equals;

            Result : Natural := 0;

         begin --  Find
            for I in 1 .. Used loop
               if Equals (Table (I), Decl, The_Type) then
                  Result := I; exit;
               end if;
            end loop;
            return Result;
         end Find;

         procedure Check_Primitive
           (Decl     : in     Declaration;
            The_Type : in     Declaration;
            Result   : in out Op_Ptr;
            Length   : in out Natural;
            Used     : in out Natural)
         is
         begin
            if Is_Primitive (Decl, The_Type) then
               declare
                  J : constant Natural :=
                    Find (Result, Used, Decl, The_Type);
               begin
                  if J /= 0 then
                     --  Found!!
                     Result (J).Kind := Overridden_Operation;
                     Result (J).Decl := Decl;
                  else
                     --  Not found: this is a new operation
                     if Used = Length then
                        declare
                           Q : constant Op_Ptr :=
                             new Operation_List (1 .. Length + 10);
                        begin
                           if Result /= null then
                              Q (1 .. Length) := Result.all;
                              Free (Result);
                           end if;
                           Result := Q;
                           Length := Length + 10;
                        end;
                     end if;
                     Used := Used + 1;
                     Result (Used) :=
                       (False, New_Operation, Decl);
                     if Declaration_Kind (Decl) =
                        A_Function_Declaration
                     then
                        declare
                           Result_Type : constant Type_Descriptor :=
                             Type_Of (Result_Profile (Decl));
                        begin
                           Result (Used).Is_Controlling_Result :=
                             Result_Type.Attrs = No_Attributes and then
                             Is_Equal (Result_Type.Decl, The_Type);
                        end;
                     end if;
                  end if; --  Found?
               end;
            end if;
         end Check_Primitive;

         Def       : Definition;
         D_K       : Definition_Kinds;
         T_K       : Type_Kinds;
         Is_Tagged : Boolean          := False;

      begin --  Get_Primitive_Ops
         Handled := False;
         if Is_Nil (The_Type) then return; end if;
         Def := Type_Declaration_View (The_Type);
         D_K := Definition_Kind (Def);
         T_K := Type_Kind (Def);
         case D_K is
            when A_Private_Type_Definition |
                 A_Tagged_Private_Type_Definition =>
               Is_Tagged := D_K = A_Tagged_Private_Type_Definition;

            when A_Private_Extension_Definition =>
               --  Do the ancestor.
               Get_Primitive_Ops
                 (Ancestor_Type (The_Type), Result, Length, Used, Handled);
               Is_Tagged := True;

            when A_Formal_Type_Definition =>
               if
                  Formal_Type_Kind (Def) = A_Formal_Derived_Type_Definition
               then
                  --  Do the ancestor.
                  Get_Primitive_Ops
                    (Ancestor_Type (The_Type), Result, Length, Used, Handled);
               end if;
               return;

            when A_Subtype_Indication =>
               --  Actual parameter for a formal derived type in an
               --  instantiation
               Get_Primitive_Ops
                 (Ancestor_Type (The_Type), Result, Length, Used, Handled);
               --  Hmmm... if I do "subtype X is Some_Tagged_Type;" and then
               --  add operations to type 'X', GNAT treats them as primitive
               --  operations of 'Some_Tagged_Type' (they are inherited even
               --  if I do "type Other is new Some_Tagged_Type with null
               --  record;"), at least if they're both in the same package
               --  spec... Is this actually correct?
               return;

            when A_Type_Definition =>
               case T_K is
                  when A_Record_Type_Definition |
                       A_Tagged_Record_Type_Definition =>
                     Is_Tagged := T_K = A_Tagged_Record_Type_Definition;

                  when A_Derived_Type_Definition |
                       A_Derived_Record_Extension_Definition =>
                     --  Do the parent.
                     Get_Primitive_Ops
                       (Ancestor_Type (The_Type),
                        Result, Length, Used, Handled);
                     if not Handled then
                        --  It was a derived type from some type we don't
                        --  handle.
                        return;
                     end if;
                     Is_Tagged := T_K = A_Derived_Record_Extension_Definition;

                  when others =>
                     return;
                     --  Hook for future development: get it right for types
                     --  derived from standard types such as Natural.
                     --  null;

               end case;

            when others =>
               return;
               --  Hook for future development: get it right for types
               --  derived from standard types such as Natural.
               --  null;

         end case;
         --  We have only private types, (tagged) record types, and types
         --  derived from such types here. Note that we do not have formal
         --  types anymore, here! Do ourselves now.
         Handled := True;
         for I in 1 .. Used loop
            --  Hook for future development: get it right for types derived
            --  from standard types such as Natural.
            --  if Result (I).Kind = New_Operation then
            --   Result (I).Kind := Inherited_Original_Operation;
            --  elsif Result (I).Kind = Overridden_Operation then
            Result (I).Kind := Inherited_Operation;
            --  end if;
         end loop;
         Traverse_Declarations :
         declare
            Enclosing_Package : constant Element :=
              Enclosing_Element (The_Type);
            --  We *know* that the enclosing element must be a package: we
            --  only handle specs, and therefore all types are declared in
            --  a package, not within body declarative parts.
            Decls             : constant Declarative_Item_List :=
              Visible_Part_Declarative_Items (Enclosing_Package, False);
            --  Without pragmas and the like.
            Index             : Natural := 0;
         begin
            for I in Decls'Range loop
               if Is_Equal (Decls (I), The_Type) then
                  Index := I; exit;
               end if;
            end loop;
            if Index = 0 then
               --  Not found??? Hey, what's going on?
               raise Program_Error;
            end if;
            for I in Index + 1 .. Decls'Last loop
               if Element_Kind (Decls (I)) = A_Declaration then
                  case Declaration_Kind (Decls (I)) is
                     when A_Function_Declaration |
                          A_Procedure_Declaration |
                          A_Function_Renaming_Declaration |
                          A_Procedure_Renaming_Declaration =>
                        if not Is_Tagged or else
                           Is_Dispatching_Operation (Decls (I))
                        then
                           Check_Primitive
                             (Decls (I), The_Type, Result, Length, Used);
                        end if;

                     when others =>
                        --  Primitive operations of tagged types shall be
                        --  declared before the type is frozen; RM 3.9.2(13),
                        --  RM 13.14 (16). Therefore we can stop.
                        --    Note that the RM says "shall", and a compiler is
                        --  thus forced to issue an error message if the
                        --  source indeed has something looking like a
                        --  primitive operation of a tagged type after that
                        --  type already had been frozen.
                        --    As a consequence, we don't even have to check
                        --  for this condition!
                        null;

                  end case;
               end if;
            end loop;
         end Traverse_Declarations;
      end Get_Primitive_Ops;

      P       : Op_Ptr;
      Length  : Natural := 0;
      Used    : Natural := 0;
      Handled : Boolean;
   begin
      Get_Primitive_Ops (The_Type, P, Length, Used, Handled);
      if Handled and then P /= null then
         declare
            Result : constant Operation_List (1 .. Used) := P (1 .. Used);
         begin
            Free (P);
            return Result;
         end;
      else
         if P /= null then Free (P); end if;
         declare
            Nil_Result : constant Operation_List (1 .. 0) :=
              (others => (False, Inherited_Operation, Nil_Element));
         begin
            return Nil_Result;
         end;
      end if;
   end Primitive_Operations;

   ----------------------------------------------------------------------------

   function Hash_Unit
     (Unit : in Asis.Compilation_Unit)
     return GAL.Support.Hashing.Hash_Type
   is
   begin
      return GAL.Support.Hashing.Hash_Case_Insensitive
               (AD.Text_Utilities.To_String
                  (Asis2.Naming.Full_Unit_Name (Unit)));
   end Hash_Unit;

   package Unit_Tables is
     new GAL.Containers.Hash_Tables
           (Item   => Asis.Compilation_Unit,
            Memory => GAL.Storage.Standard,
            Hash   => Hash_Unit,
            "="    => Asis.Compilation_Units.Is_Equal);

   function Get_Dependents
     (Unit : in Asis.Compilation_Unit)
     return Asis.Compilation_Unit_List
   is
      use Asis.Compilation_Units;
      use Asis.Compilation_Units.Relations;

      Rel : constant Relationship :=
        Semantic_Dependence_Order
          (Compilation_Unit_List'(1 => (Unit)),
           Nil_Compilation_Unit_List,
           Enclosing_Context (Unit),
           Supporters);

      function Use_Unit
        (Unit : in Compilation_Unit)
        return Boolean
      is
      begin
         if Is_Nil (Unit) then return False; end if;
         if Unit_Origin (Unit) /= An_Application_Unit then
            return False;
         end if;
         case Unit_Class (Unit) is
            when A_Public_Declaration |
                 A_Public_Declaration_And_Body |
                 A_Private_Declaration =>
               declare
                  Kind : constant Unit_Kinds := Unit_Kind (Unit);
               begin
                  if Kind = Not_A_Unit or else
                     Kind >= A_Procedure_Body_Subunit
                  then
                     return False;
                  else
                     return True;
                  end if;
               end;
            when others =>
               null;
         end case;
         return False;
      end Use_Unit;

   begin
      --  There is a bug in ASIS-for-GNAT 3.16a: it doesn't include parents
      --  of withed units unless they are withed explicitly. Compensate for
      --  that. Note: we use a hash table to avoid repeatedly inserting parents
      --  that already exist. This is important, because the transitive
      --  closure of withed units may be large!
      if Rel.Consistent_Length < 1 then return Rel.Consistent; end if;
      declare
         Units : Unit_Tables.Hash_Table;
      begin
         Unit_Tables.Set_Resize (Units, 0.75);
         declare
            Linear_Growth : GAL.Support.Hashing.Linear_Growth_Policy (20);
         begin
            Unit_Tables.Set_Growth_Policy (Units, Linear_Growth);
         end;
         for I in 1 .. Rel.Consistent_Length loop
            declare
               Unit : Compilation_Unit := Rel.Consistent (I);
            begin
               while Use_Unit (Unit) loop
                  begin
                     Unit_Tables.Insert (Units, Unit);
                  exception
                     when Unit_Tables.Duplicate_Key =>
                        exit;
                  end;
                  Unit := Corresponding_Parent_Declaration (Unit);
               end loop;
            end;
         end loop;
         --  Ok, we have them all.
         declare
            Result : Compilation_Unit_List
              (1 .. Natural (Unit_Tables.Nof_Elements (Units)));
            N      : Natural := 1;

            procedure Add_Item
              (Value : in     Compilation_Unit;
               Quit  : in out Boolean)
            is
               pragma Warnings (Off, Quit); --  silence -gnatwa
            begin
               Result (N) := Value;
               N := N + 1;
            end Add_Item;

            procedure Collect is new Unit_Tables.Traverse_G (Add_Item);

         begin
            Collect (Units);
            return Result;
         end;
      end;
   end Get_Dependents;

   ----------------------------------------------------------------------------

   function Get_Pragmas
     (Unit : in Asis.Compilation_Unit)
     return Asis.Pragma_Element_List
   is

      procedure Filter
        (Decl     : in     Declaration;
         List     : in out Pragma_Element_List;
         N        :    out Natural)
      is
         --  Filter out anything that cannot possibly apply to a library unit
         --  declaration. In other words, keep all library unit pragmas, plus
         --  a few selected pragmas that can apply to library unit subprograms:
         --  Inline, Asynchronous, Convention, and Import and Export.

         function Check
           (Decl  : in Declaration;
            Param : in Association)
           return Boolean
         is
         begin
            return Asis2.Declarations.References
                     (Actual_Parameter (Param), Decl);
         end Check;

         procedure Swap is
            new GAL.Support.Swap (Asis.Element);

         I    : Natural := List'First;
         Keep : Boolean;

      begin --  Filter
         N := List'Last;
         while I <= N loop
            case Pragma_Kind (List (I)) is
               when An_All_Calls_Remote_Pragma |
                    An_Elaborate_Body_Pragma |
                    A_Preelaborate_Pragma |
                    A_Pure_Pragma |
                    A_Remote_Call_Interface_Pragma |
                    A_Remote_Types_Pragma |
                    A_Shared_Passive_Pragma |
                    An_Inline_Pragma |
                    An_Asynchronous_Pragma =>
                  declare
                     Params : constant Association_List :=
                       Pragma_Argument_Associations (List (I));
                  begin
                     if Params'Last = Params'First then
                        Keep := Check (Decl, Params (Params'First));
                     else
                        Keep := Params'Last < Params'First;
                     end if;
                  end;

               when A_Convention_Pragma |
                    An_Import_Pragma |
                    An_Export_Pragma =>
                  declare
                     Params : constant Association_List :=
                       Pragma_Argument_Associations (List (I));
                  begin
                     if Params'Last < Params'First + 1 then
                        Keep := False;
                     else
                        Keep := Check (Decl, Params (Params'First + 1));
                     end if;
                  end;

               when others =>
                  Keep := False;

            end case;
            if Keep then
               I := I + 1;
            else
               if I < N then Swap (List (I), List (N)); end if;
               N := N - 1;
            end if;
         end loop;
      end Filter;

      Outer : Pragma_Element_List := Compilation_Pragmas (Unit);

      N : Natural;

   begin
      Filter (Unit_Declaration (Unit), Outer, N);
      return Outer (Outer'First .. N);
   end Get_Pragmas;

   function Expand_Generic
     (Element  : in     Asis.Element;
      Reporter : access AD.Messages.Error_Reporter'Class)
     return Asis.Element
   is
      Result : Asis.Element := Element;
   begin
      while Is_Part_Of_Instance (Result) loop
         declare
            Temp : Asis.Element;
         begin
            begin
               Temp := Corresponding_Generic_Element (Result);
            exception
               when others =>
                  --  ASIS-for-GNAT 3.14p sometimes fails here. Happens for
                  --  instance in the simple_test, if ASIS chooses to use the
                  --  tree in 'test-ch.adt' to traverse the generic unit
                  --  'Test.Use_Signature'. It appears that in this case, the
                  --  selector 'X' in the parameter type 'X_Formal.X' has
                  --  'Is_Part_Of_Instance' set (which it hasn't if there is
                  --  only a tree file 'test-use_signature.adt'!), and that
                  --  seems to trigger an ASIS bug. It raises an exception
                  --  ('Inappropriate_Element') somewhere in Enclosing_Element,
                  --  which certainly is bogus.
                  --
                  --  We do the next best thing and just swallow the exception.
                  --  This means we'll just return the non-template name.
                  AD.Messages.Report_Error
                    (Reporter.all, "Cannot find item from generic template");
                  exit;
            end;
            Result := Temp;
         end;
      end loop;
      return Result;
   end Expand_Generic;

end AD.Queries;