liblal_refactor_25.0.0_15241508/src/lal_refactor-auto_import.adb

   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
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
--
--  Copyright (C) 2023-2024, AdaCore
--
--  SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
--

with Ada.Characters.Latin_1;
with Ada.Containers.Hashed_Maps;
with Ada.Containers.Hashed_Sets;
with Ada.Containers.Vectors;
with Ada.Strings.Wide_Wide_Unbounded;

with Langkit_Support.Errors;

with Libadalang.Common; use Libadalang.Common;
with GNATCOLL.Traces;   use GNATCOLL.Traces;

package body LAL_Refactor.Auto_Import is

   Me : constant Trace_Handle := Create ("LAL_REFACTOR.AUTO_IMPORT", Off);

   package Basic_Decl_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Positive,
      Element_Type => Basic_Decl,
      "="          => "=");

   subtype Basic_Decl_Vector is Basic_Decl_Vectors.Vector;

   package Defining_Name_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Positive,
      Element_Type => Defining_Name,
      "="          => "=");

   subtype Defining_Name_Vector is Defining_Name_Vectors.Vector;

   function Defining_Name_Hash
     (Defining_Name : Libadalang.Analysis.Defining_Name)
      return Ada.Containers.Hash_Type
   is (Hash (Defining_Name.As_Ada_Node));
   --  Casts Basic_Decl as Ada_Node and use Hash from Libadalang.Analysis

   package Defining_Name_Hashed_Sets is new Ada.Containers.Hashed_Sets
     (Element_Type        => Defining_Name,
      Hash                => Defining_Name_Hash,
      Equivalent_Elements => "=",
      "="                 => "=");

   subtype Defining_Name_Hashed_Set is Defining_Name_Hashed_Sets.Set;

   package Defining_Name_To_Defining_Name_Vector_Hashed_Maps is new
     Ada.Containers.Hashed_Maps
       (Key_Type        => Defining_Name,
        Element_Type    => Defining_Name_Vector,
        Hash            => Defining_Name_Hash,
        Equivalent_Keys => "=",
        "="             => Defining_Name_Vectors."=");

   subtype Defining_Name_To_Defining_Name_Vector_Hashed_Map is
     Defining_Name_To_Defining_Name_Vector_Hashed_Maps.Map;

   procedure Append_Reachable_Declarations
     (Name                  : Libadalang.Analysis.Name;
      Unit                  : Compilation_Unit;
      Reachable_Definitions : in out Defining_Name_Hashed_Set;
      Reachable_Renames     :
        in out Defining_Name_To_Defining_Name_Vector_Hashed_Map);
   --  Finds all declarations on Unit that can be visible by Name with the
   --  with clause or qualifier. These are stored in Reachable_Declarations.
   --  Additionally, also updates Reachable_Renames with all rename
   --  declarations seen.

   function Create_Available_Imports
     (Name_To_Import         : Name;
      Reachable_Declarations : Defining_Name_Hashed_Set;
      Reachable_Renames      :
        Defining_Name_To_Defining_Name_Vector_Hashed_Map)
      return Import_Type_Ordered_Set;
   --  For every declaration of Reachable_Declarations, creates at least one
   --  Import_Type object that is added to the returned set.
   --  While creating the Import_Type, if there is any alias that can be used
   --  in Reachable_Renames, a new Import_Type is created and also added
   --  to the returned set.
   --
   --  Example:
   --  Given Reachable_Declarations = [A, A.B] and
   --  Reachable_Renames = {A: [C], B: [D]}, the following imports will be
   --  generated:
   --  A
   --  C (an alias of A)
   --  A.B
   --  A.D (an alias of D)
   --  C.B (an alias of A)
   --  C.D (an alias of A and an alias of D)
   --
   --  Name_To_Import is used to compare the original qualifier (which only
   --  exits if its parent is a Dotted_Name) with the one that that needs to be
   --  added. If they're the same, it means that it does not need to be added
   --  (only imported), therefore, the Import_Type object added to the set will
   --  contain an empty qualifier.

   function Get_Appropriate_Enclosing_Name
     (Base_Id : Libadalang.Analysis.Base_Id)
      return Name;
   --  Returns the top dotted name parent suffix if Base_Id is part of one,
   --  or Base_Id casted as Name if it is not.

   function Get_Available_Imports
     (Name  : Libadalang.Analysis.Name;
      Units : Analysis_Unit_Array)
      return Import_Type_Ordered_Set;
   --  Returns an ordered set with all the with clause and qualifiers that make
   --  Name resolvable.

   function Get_Generic_Package_Internal
     (Instantiation : Generic_Package_Instantiation)
      return Generic_Package_Internal;
   --  Finds the Generic_Package_Internal node given a
   --  Generic_Package_Instantiation. Note that P_Designated_Generic_Decl
   --  sometimes raises an unexpected exception when Gen_Pkg_Instantiation is
   --  a node from an Ada runtime file. At the moment, this is considered
   --  a LAL bug: [T814-031]

   function Get_Insert_New_Dependency_Location
     (Unit           : Compilation_Unit'Class;
      New_Dependency : Langkit_Support.Text.Text_Type)
      return Source_Location;
   --  Source_Location where to insert a with clause for New_Dependency.
   --
   --  The insert location is determined assuming that the prelude (ignoring
   --  pragmas) is already sorted alphabetically and New_Dependency will be
   --  inserted accordingly. In this case, the returned Source_Location will
   --  always be the Start_Sloc of an existing clause. This implies that if the
   --  with clause is a list and if any package is alphabetically greater than
   --  New_Dependency, then this function returns the Start_Sloc of that with
   --  clause.
   --
   --  Examples, considering New_Dependency = "B":
   --
   --  with A;
   --  with C;
   --  ^
   --
   --  with A, C;
   --  ^
   --  with D;
   --
   --  ^ represents the Source_Location returned by this function.
   --
   --  If Unit does not have a prelude, or if it is empty, then returns the
   --  Start_Sloc of Unit's body to ensure that New_Dependency is inserted
   --  after any comments like copyright headers.

   function Get_Parent_Packages
     (Defining_Name : Libadalang.Analysis.Defining_Name'Class)
      return Basic_Decl_Vector;
   --  Returns a vector with all the parent package needed to fully qualify
   --  Node.

   function List_And_Expand_Package_Declarations
     (Package_Decl    : Base_Package_Decl'Class;
      Include_Private : Boolean)
      return Basic_Decl_Vector;
   --  Add every declaration of Package_Decl to a list. If any declaration is
   --  also a Base_Package_Decl (in case of nested packages) then the nested
   --  package is also expanded and its declarations are added to the same
   --  list. Include_Private controls whether or not private declarations are
   --  also added.

   ---------
   -- "<" --
   ---------

   function "<" (Left, Right : Import_Type) return Boolean
   is
      use Ada.Strings.Wide_Wide_Unbounded;

   begin
      return
        (if Left.Import = Right.Import then
            Left.Qualifier < Right.Qualifier
         else
            Left.Import < Right.Import);
   end "<";

   -----------------------------------
   -- Append_Reachable_Declarations --
   -----------------------------------

   procedure Append_Reachable_Declarations
     (Name                  : Libadalang.Analysis.Name;
      Unit                  : Compilation_Unit;
      Reachable_Definitions : in out Defining_Name_Hashed_Set;
      Reachable_Renames     :
        in out Defining_Name_To_Defining_Name_Vector_Hashed_Map)
   is
      Name_Text : constant Langkit_Support.Text.Text_Type :=
        Langkit_Support.Text.To_Lower (Name.Text);

      procedure Append_Declaration
        (Declaration     : Basic_Decl;
         Include_Private : Boolean);
      --  Appends the canonical part of all definitions in Declaration to
      --  Reachable_Definitions if and only if the definition has the name
      --  text as Name. If Declaration is a Package_Renaming_Decl, then it's
      --  also appended to Reachable_Renames.
      --  If Declaration is a package declaration, then the package is also
      --  visited and this function is recursively called on all its
      --  declarations.
      --  Include_Private controls if declarations inside the private part of
      --  a package are considered or not.

      procedure Process_Own_Unit;

      ------------------------
      -- Append_Declaration --
      ------------------------

      procedure Append_Declaration
        (Declaration     : Basic_Decl;
         Include_Private : Boolean)
      is
         procedure Append (Declaration : Basic_Decl);
         --  Appends the canonical part of all definitions in Declaration to
         --  Reachable_Definitions if and only if the definition has the name
         --  text as Name.

         procedure Append_Rename (Package_Renaming : Package_Renaming_Decl);
         --  If Reachable_Renames already has a key equal to Package_Renaming
         --  then its set is expanded with Declaration, otherwise and new set
         --  is created with Declaration as the only element.

         ------------
         -- Append --
         ------------

         procedure Append (Declaration : Basic_Decl) is
         begin
            if Declaration.Is_Null then
               return;
            end if;

            for Defining_Name of Declaration.P_Defining_Names loop
               if Defining_Name.F_Name.Kind in Ada_Dotted_Name then
                  declare
                     Text : constant Langkit_Support.Text.Text_Type :=
                       Langkit_Support.Text.To_Lower
                         (Defining_Name.F_Name.As_Dotted_Name.F_Suffix.Text);

                  begin
                     if Text = Name_Text then
                        Reachable_Definitions.Include
                          (Defining_Name.P_Canonical_Part);
                     end if;

                  end;

               else
                  declare
                     Text : constant Langkit_Support.Text.Text_Type :=
                       Langkit_Support.Text.To_Lower
                         (Defining_Name.F_Name.Text);

                  begin
                     if Text = Name_Text then
                        Reachable_Definitions.Include
                          (Defining_Name.P_Canonical_Part);
                     end if;
                  end;
               end if;
            end loop;
         end Append;

         -------------------
         -- Append_Rename --
         -------------------

         procedure Append_Rename (Package_Renaming : Package_Renaming_Decl) is
            Renamed_Definition : constant Defining_Name :=
              (declare
                 Renamed_Package : constant Basic_Decl :=
                   Package_Renaming.P_Renamed_Package;
               begin
                 (if Renamed_Package.Is_Null then No_Defining_Name
                  else Renamed_Package.P_Defining_Name));

         begin
            if Renamed_Definition.Is_Null then
               LAL_Refactor.Refactor_Trace.Trace
                 ("Renamed package of "
                  & Package_Renaming.Image
                  & " has an unexpected null definition");

               return;
            end if;

            if Reachable_Renames.Contains (Renamed_Definition) then
               Reachable_Renames
                 .Reference (Renamed_Definition)
                 .Append (Package_Renaming.P_Defining_Name);
            else
               Reachable_Renames
                 .Insert
                    (Renamed_Definition, [Package_Renaming.P_Defining_Name]);
            end if;
         end Append_Rename;

      begin
         if Declaration.Kind in Ada_Error_Decl then
            null;

         elsif Declaration.Kind = Ada_Package_Decl then
            Append (Declaration.As_Basic_Decl);

            --  Nodes of type Package_Decl are nested packages and need to be
            --  expanded since they contain more declarations

            for Inner_Declaration of
              List_And_Expand_Package_Declarations
                (Declaration.As_Package_Decl,
                 Include_Private)
            loop
               Append (Inner_Declaration);

               if Inner_Declaration.Kind in
                 Ada_Package_Renaming_Decl_Range
               then
                  Append_Rename (Inner_Declaration.As_Package_Renaming_Decl);
               end if;
            end loop;

         elsif Declaration.Kind = Ada_Generic_Package_Instantiation then
            Append (Declaration.As_Basic_Decl);

            for Inner_Declaration of List_And_Expand_Package_Declarations
              (Get_Generic_Package_Internal
                 (Declaration.As_Generic_Package_Instantiation),
               False)
            loop
               Append (Inner_Declaration);

               if Inner_Declaration.Kind in
                 Ada_Package_Renaming_Decl_Range
               then
                  Append_Rename (Inner_Declaration.As_Package_Renaming_Decl);
               end if;
            end loop;

         else
            Append (Declaration.As_Basic_Decl);

            if Declaration.Kind in Ada_Package_Renaming_Decl_Range then
               Append_Rename (Declaration.As_Package_Renaming_Decl);
               --  ??? The same code should be applicable to
               --  Ada_Generic_Renaming_Decl and
               --  Ada_Subp_Renaming_Decl_Range. However, for now, LAL does
               --  not provide property P_Renamed_Package for such types.
            end if;
         end if;
      end Append_Declaration;

      ----------------------
      -- Process_Own_Unit --
      ----------------------

      procedure Process_Own_Unit is
         Stop_Decl : Basic_Decl := No_Basic_Decl;

      begin
         for Parent of Name.Parent.Parents loop
            if Parent.Kind in Ada_Basic_Decl
              and then Stop_Decl.Is_Null
            then
               Stop_Decl := Parent.As_Basic_Decl;
            end if;

            if Parent.Kind = Ada_Handled_Stmts then
               --  If this Handled_Statements has a corresponding
               --  Declarative_Part, add all those declarations to Map.
               --  If a declaration is a package declaration, then only add
               --  the visible declarations.

               declare
                  Declarative_Part :
                    constant Libadalang.Analysis.Declarative_Part :=
                      Laltools.Common.Get_Declarative_Part
                        (Parent.As_Handled_Stmts);
               begin
                  if not Declarative_Part.Is_Null then
                     for Declaration of Declarative_Part.F_Decls loop
                        if Declaration.Kind in Ada_Basic_Decl then
                           Append_Declaration
                             (Declaration     => Declaration.As_Basic_Decl,
                              Include_Private => False);
                        end if;
                     end loop;
                  end if;
               end;

            elsif Parent.Kind = Ada_Declarative_Part then
               declare
                  Declarative_Part :
                    constant Libadalang.Analysis.Declarative_Part :=
                      Parent.As_Declarative_Part;

               begin
                  if not Declarative_Part.Is_Null then
                     --  For a node in a Declarative_Part of a Package_Body
                     --  only the previously declared declarations are
                     --  visible.

                     Decl_Loop :
                     for Declaration of Declarative_Part.F_Decls loop
                        --  Allow recursion for declarations that are not
                        --  packages

                        if Declaration.Kind in Ada_Basic_Decl
                          and then Declaration.As_Basic_Decl = Stop_Decl
                        then
                           if not (Declaration.Kind in
                                     Ada_Package_Decl_Range
                                     | Ada_Package_Body_Range)
                           then
                              Append_Declaration
                                (Declaration     =>
                                   Declaration.As_Basic_Decl,
                                 Include_Private => False);
                           end if;

                           exit Decl_Loop;
                        end if;

                        --  Do not add Package_Body

                        if Declaration.Kind in Ada_Basic_Decl
                          and then Declaration.Kind not in
                            Ada_Package_Body_Range
                        then
                           Append_Declaration
                             (Declaration         =>
                                Declaration.As_Basic_Decl,
                              Include_Private     => False);
                        end if;
                     end loop Decl_Loop;
                  end if;
               end;

            elsif Parent.Kind in Ada_Package_Body_Range then
               --  If Node is inside a Package_Body then all private
               --  declarations found in the Package_Declaration are
               --  added to Map.

               if not Parent
                        .As_Basic_Decl
                        .P_Defining_Name
                        .P_Canonical_Part
                        .P_Basic_Decl.Is_Null
               then
                  Append_Declaration
                    (Declaration     =>
                       Parent
                         .As_Basic_Decl
                         .P_Defining_Name.P_Canonical_Part
                         .P_Basic_Decl,
                     Include_Private => True);
               end if;
            end if;
         end loop;
      end Process_Own_Unit;

   begin
      if Name.P_Enclosing_Compilation_Unit = Unit then
         Process_Own_Unit;

      else
         Append_Declaration
           (Declaration     => Unit.P_Decl,
            Include_Private =>
              Name.P_Enclosing_Compilation_Unit = Unit.P_Other_Part);
      end if;
   end Append_Reachable_Declarations;

   --------------------------
   -- Create_Auto_Importer --
   --------------------------

   function Create_Auto_Importer
     (Unit     : Analysis_Unit;
      Location : Source_Location;
      Import   : Import_Type)
      return Auto_Importer
   is
      Node           : constant Ada_Node := Unit.Root.Lookup (Location);
      Enclosing_Name : constant Name :=
        Get_Appropriate_Enclosing_Name (Node.As_Base_Id);

   begin
      return Create_Auto_Importer (Enclosing_Name, Import);
   end Create_Auto_Importer;

   --------------------------
   -- Create_Auto_Importer --
   --------------------------

   function Create_Auto_Importer
     (Name   : Libadalang.Analysis.Name;
      Import : Import_Type)
      return Auto_Importer
   is (Auto_Importer'(Name, Import));
   pragma Inline (Create_Auto_Importer);

   ------------------------------
   -- Create_Available_Imports --
   ------------------------------

   function Create_Available_Imports
     (Name_To_Import         : Name;
      Reachable_Declarations : Defining_Name_Hashed_Set;
      Reachable_Renames      :
        Defining_Name_To_Defining_Name_Vector_Hashed_Map)
     return Import_Type_Ordered_Set
   is
      use Ada.Strings.Wide_Wide_Unbounded;
      use Langkit_Support.Text;

      Original_Qualifier : constant Unbounded_Text_Type :=
         (if Name_To_Import.Parent.Kind in Ada_Dotted_Name then
            To_Unbounded_Text
              (Name_To_Import.Parent.As_Dotted_Name.F_Prefix.Text)
          else
            Null_Unbounded_Wide_Wide_String);

      function Is_Top_Level_Declaration
        (Defining_Name : Libadalang.Analysis.Defining_Name)
         return Boolean
      is (Defining_Name.P_Parent_Basic_Decl =
            Defining_Name.P_Enclosing_Compilation_Unit.P_Decl);
      --  True is Declaration is the top level declaration of it's enclosing
      --  compilation unit.

      function Make_Imports
        (Defining_Name : Libadalang.Analysis.Defining_Name)
         return Import_Type_Ordered_Set;
      --  Computes the package name that needs to be withed and the qualifier
      --  that needs to be used in order to resolve Definition. Multiple
      --  solutions can be found if Definition's parent packages have renames.

      ------------------
      -- Make_Imports --
      ------------------

      function Make_Imports
        (Defining_Name : Libadalang.Analysis.Defining_Name)
         return Import_Type_Ordered_Set
      is
         Parent_Packages : constant Basic_Decl_Vector :=
           Get_Parent_Packages (Defining_Name);

         Final_Imports : Import_Type_Ordered_Set :=
           [Import_Type'
              (Import    => Null_Unbounded_Wide_Wide_String,
               Qualifier => Null_Unbounded_Wide_Wide_String)];

         procedure Update_Imports
           (Imports          : in out Import_Type_Ordered_Set;
            Parent_Qualifier : Unbounded_Text_Type;
            Last_Parent      : Boolean);
         --  Adds Parent_Qualifier as a qualifier to every Import_Type object
         --  of Final_Imports. If Last_Parent is True, then adds
         --  Parent_Qualifier as the unit to import too. These new Import_Type
         --  objects are added to Imports.

         --------------------
         -- Update_Imports --
         --------------------

         procedure Update_Imports
           (Imports          : in out Import_Type_Ordered_Set;
            Parent_Qualifier : Unbounded_Text_Type;
            Last_Parent      : Boolean)
         is
         begin
            for Import of Final_Imports loop
               declare
                  New_Import : Import_Type :=
                    ((if Last_Parent
                      then Parent_Qualifier
                      else Null_Unbounded_Wide_Wide_String),
                     (if Langkit_Support.Text."="
                           (Import.Qualifier,
                            Null_Unbounded_Wide_Wide_String)
                      then Parent_Qualifier
                      else Parent_Qualifier & "." & Import.Qualifier));

               begin
                  if Langkit_Support.Text."="
                       (New_Import.Qualifier, Original_Qualifier)
                  then
                     New_Import.Qualifier :=
                       Null_Unbounded_Wide_Wide_String;
                  end if;
                  Imports.Include (New_Import);
               end;
            end loop;
         end Update_Imports;

      begin
         if Is_Top_Level_Declaration (Defining_Name) then
            declare
               Updated_Imports  : Import_Type_Ordered_Set := [];
               Declaration_Name : constant Unbounded_Text_Type :=
                 To_Unbounded_Text (Defining_Name.Text);

            begin
               declare
                  New_Import : Import_Type :=
                    (Declaration_Name, Declaration_Name);
               begin
                  Updated_Imports.Include (New_Import);
                  if Langkit_Support.Text."="
                     (New_Import.Qualifier, Original_Qualifier)
                  then
                     New_Import.Qualifier :=
                       Null_Unbounded_Wide_Wide_String;
                  end if;
                  if Reachable_Renames.Contains (Defining_Name) then
                     for Alias of
                       Reachable_Renames.Constant_Reference (Defining_Name)
                     loop
                        declare
                           Alias_Name : constant Unbounded_Text_Type :=
                             To_Unbounded_Text (Alias.Text);

                        begin
                           Update_Imports
                             (Updated_Imports,
                              Alias_Name,
                              True);
                        end;
                     end loop;
                  end if;
               end;
               Final_Imports := Updated_Imports;
            end;
            return Final_Imports;
         end if;

         for Parent of Parent_Packages loop
            declare
               Updated_Imports  : Import_Type_Ordered_Set := [];
               Parent_Qualifier : constant Unbounded_Text_Type :=
                 To_Unbounded_Text (Parent.P_Defining_Name.Text);

            begin
               Update_Imports
                 (Updated_Imports,
                  Parent_Qualifier,
                  Parent = Parent_Packages.Last_Element);
               if Reachable_Renames.Contains (Parent.P_Defining_Name) then
                  for Alias of
                    Reachable_Renames.Constant_Reference
                      (Parent.P_Defining_Name)
                  loop
                     declare
                        Alias_Name : constant Unbounded_Text_Type :=
                          To_Unbounded_Text (Alias.Text);
                     begin
                        Update_Imports
                          (Updated_Imports,
                           Alias_Name,
                           Parent = Parent_Packages.Last_Element);
                     end;
                  end loop;
               end if;
               Final_Imports := Updated_Imports;
            end;
         end loop;

         return Final_Imports;
      end Make_Imports;

      Result : Import_Type_Ordered_Set;

   begin
      for Reachable_Declaration of Reachable_Declarations loop
         Result.Union (Make_Imports (Reachable_Declaration));
      end loop;

      return Result;
   end Create_Available_Imports;

   ------------------------------------
   -- Get_Appropriate_Enclosing_Name --
   ------------------------------------

   function Get_Appropriate_Enclosing_Name
     (Base_Id : Libadalang.Analysis.Base_Id)
      return Name
   is
      Aux : Ada_Node := Base_Id.As_Ada_Node;

   begin
      while Aux.Parent.Kind in Ada_Dotted_Name loop
         Aux := Aux.Parent;
      end loop;

      if Aux.Kind in Ada_Dotted_Name then
         return Aux.As_Dotted_Name.F_Suffix.As_Name;
      else
         return Base_Id.As_Name;
      end if;

   end Get_Appropriate_Enclosing_Name;

   ---------------------------
   -- Get_Available_Imports --
   ---------------------------

   function Get_Available_Imports
     (Name  : Libadalang.Analysis.Name;
      Units : Analysis_Unit_Array)
      return Import_Type_Ordered_Set
   is
      use Ada.Strings.Wide_Wide_Unbounded;

      Reachable_Declarations : Defining_Name_Hashed_Set;
      Reachable_Renames      :
        Defining_Name_To_Defining_Name_Vector_Hashed_Map;
      Original_Qualifier_Text : constant
        Langkit_Support.Text.Text_Type :=
          (if not Name.Parent.Is_Null
           and then Name.Parent.Kind in Ada_Dotted_Name
           then
              Name.Parent.As_Dotted_Name.F_Prefix.Text
           else
              "");
      Lowered_Original_Qualifier : constant
        Langkit_Support.Text.Unbounded_Text_Type :=
          Langkit_Support.Text.To_Unbounded_Text
            (Langkit_Support.Text.To_Lower (Original_Qualifier_Text));
      Original_Qualifier : constant
        Langkit_Support.Text.Unbounded_Text_Type :=
          Langkit_Support.Text.To_Unbounded_Text
            (Original_Qualifier_Text);

      Available_Imports : Import_Type_Ordered_Set;
      Stop_Iteration : Boolean := False;

      procedure Process_Compilation_Unit
        (Comp_Unit : Compilation_Unit);
      --  Process the given compilation unit.
      --  If we have no specified qualifier, look for all the reachable
      --  declarations that match Name in the given unit.
      --  Otherwise, just create an import clause for this unit if
      --  the user speficied this unit in the qualifier.

      function Get_Fully_Qualified_Name
        (Comp_Unit : Compilation_Unit)
         return Langkit_Support.Text.Unbounded_Text_Type;
      --  Return the fully qualified name of the given unit
      --  (e.g: "ada.text_io").
      --  The returned qualified name is in lower-case.

      ------------------------------
      -- Get_Fully_Qualified_Name --
      ------------------------------

      function Get_Fully_Qualified_Name
        (Comp_Unit : Compilation_Unit) return
        Langkit_Support.Text.Unbounded_Text_Type
      is
         Names : constant Unbounded_Text_Type_Array :=
           Comp_Unit.P_Syntactic_Fully_Qualified_Name;
         Fully_Qualified_Name : Langkit_Support.Text.Unbounded_Text_Type;
      begin
         for Name of Names loop
            Fully_Qualified_Name :=
              (if Ada.Strings.Wide_Wide_Unbounded."/="
                 (Fully_Qualified_Name, Null_Unbounded_Wide_Wide_String)
               then
                  Fully_Qualified_Name & "." & Name
               else Name);
         end loop;

         return Fully_Qualified_Name;
      end Get_Fully_Qualified_Name;

      ------------------------------
      -- Process_Compilation_Unit --
      ------------------------------

      procedure Process_Compilation_Unit
        (Comp_Unit : Compilation_Unit) is
      begin
         Stop_Iteration := False;

         if Comp_Unit.Is_Null then
            return;
         end if;

         if Original_Qualifier = Null_Unbounded_Wide_Wide_String then
            Append_Reachable_Declarations
              (Name                   => Name,
               Unit                   => Comp_Unit,
               Reachable_Definitions  => Reachable_Declarations,
               Reachable_Renames      => Reachable_Renames);

         elsif Lowered_Original_Qualifier
           = Get_Fully_Qualified_Name (Comp_Unit)
         then
            --  The qualified specified by the user matches the unit name: stop
            --  processing other units and create an import directive for this
            --  unit.
            Available_Imports.Include
              ((Import    => Original_Qualifier,
                Qualifier => Null_Unbounded_Wide_Wide_String));
            Stop_Iteration := True;
         end if;
      end Process_Compilation_Unit;

   begin
      for Unit of Units loop
         if not Unit.Root.Is_Null
           and then not Unit.Has_Diagnostics
         then
            case Unit.Root.Kind is
               when Ada_Compilation_Unit_Range =>
                  Process_Compilation_Unit  (Unit.Root.As_Compilation_Unit);

                  if Stop_Iteration then
                     return Available_Imports;
                  end if;

               when Ada_Compilation_Unit_List_Range =>
                  for Comp_Unit of Unit.Root.As_Compilation_Unit_List loop
                     Process_Compilation_Unit (Comp_Unit.As_Compilation_Unit);

                     if Stop_Iteration then
                        return Available_Imports;
                     end if;
                  end loop;
               when others =>
                  null;
            end case;
         end if;
      end loop;

      Available_Imports := Create_Available_Imports
        (Name_To_Import         => Name,
         Reachable_Declarations => Reachable_Declarations,
         Reachable_Renames      => Reachable_Renames);

      return Available_Imports;
   end Get_Available_Imports;

   ----------------------------------
   -- Get_Generic_Package_Internal --
   ----------------------------------

   function Get_Generic_Package_Internal
     (Instantiation : Generic_Package_Instantiation)
      return Generic_Package_Internal is
   begin
      return Instantiation.P_Designated_Generic_Decl.
        As_Generic_Package_Decl.F_Package_Decl.As_Generic_Package_Internal;

   exception
      when Langkit_Support.Errors.Property_Error =>
         return No_Generic_Package_Internal;
   end Get_Generic_Package_Internal;

   ----------------------------------------
   -- Get_Insert_New_Dependency_Location --
   ----------------------------------------

   function Get_Insert_New_Dependency_Location
     (Unit           : Compilation_Unit'Class;
      New_Dependency : Langkit_Support.Text.Text_Type)
      return Source_Location
   is
      use Langkit_Support.Text;

      New_Dependency_Lower : constant Text_Type := To_Lower (New_Dependency);

   begin
      if Unit.F_Prelude.Is_Null
        or else Unit.F_Prelude.Children_Count = 0
      then
         --  If there is no prelude, simply insert right before Unit's body.
         --  This ensures that the with clause is inserted after any comments
         --  like copyright headers.
         return Start_Sloc (Unit.F_Body.Sloc_Range);

      else
         for Prelude_Node of Unit.F_Prelude loop
            if Prelude_Node.Kind in Ada_With_Clause_Range then
               --  Handle list of packages: "with A, B, C;"
               for Withed_Package of
                 Prelude_Node.As_With_Clause.F_Packages
               loop
                  if New_Dependency_Lower < To_Lower (Withed_Package.Text) then
                     --  Assuming the prelude is sorted alphabetically,
                     --  the insert location is before the first clause greater
                     --  than New_Dependency.
                     --  With clause lists are not separated. Inseated, the
                     --  insertion is done before the list.
                     return Start_Sloc (Prelude_Node.Sloc_Range);
                  end if;
               end loop;
            end if;
         end loop;

         --  Insert as the last element of the prelude
         return Source_Location'(Unit.F_Prelude.Sloc_Range.End_Line + 1, 1);
      end if;
   end Get_Insert_New_Dependency_Location;

   ------------------------
   -- Get_Parent_Package --
   ------------------------

   function Get_Parent_Packages
     (Defining_Name : Libadalang.Analysis.Defining_Name'Class)
      return Basic_Decl_Vector
   is
      Parent_Packages : Basic_Decl_Vector;

   begin
      for Parent of Defining_Name.Parents (With_Self => False) loop
         if Parent.Kind in
           Ada_Package_Body
           | Ada_Package_Decl
           | Ada_Generic_Package_Decl
         then
            Parent_Packages.Append (Parent.As_Basic_Decl);
         end if;
      end loop;

      declare
         Top_Level_Declaration : constant Basic_Decl :=
           Defining_Name.P_Enclosing_Compilation_Unit.P_Decl;

      begin
         if not Top_Level_Declaration.Is_Null
           and then Top_Level_Declaration.Kind in Ada_Subp_Body_Range
         then
            --  Inside a main file: add the main name as the last element
            --  it will be used to filter out all the elements defined inside
            --  the unit itself.
            Parent_Packages.Append (Top_Level_Declaration);
         end if;
      end;

      if not Parent_Packages.Is_Empty
        and then
          Parent_Packages
            .Last_Element
            .P_Parent_Basic_Decl
            .Kind in Ada_Generic_Package_Instantiation
      then
         declare
            Instantiation : constant Basic_Decl :=
              Parent_Packages.Last_Element.P_Parent_Basic_Decl;

         begin
            Parent_Packages.Delete_Last;
            Parent_Packages.Append (Instantiation);
            Parent_Packages.Append
              (Get_Parent_Packages (Instantiation.P_Defining_Name));
         end;
      end if;

      return Parent_Packages;
   end Get_Parent_Packages;

   -----------
   -- Image --
   -----------

   function Image (Object : Import_Type) return String is
      use Langkit_Support.Text;

   begin
      return
        "Import: "
        & To_UTF8 (To_Text (Object.Import))
        & "; Qualifier: " & To_UTF8 (To_Text (Object.Qualifier));
   end Image;

   ------------------------------
   -- Is_Auto_Import_Available --
   ------------------------------

   function Is_Auto_Import_Available
     (Unit              : Analysis_Unit;
      Location          : Source_Location;
      Units             : not null access function return Analysis_Unit_Array;
      Name              : out Libadalang.Analysis.Name;
      Available_Imports : out Import_Type_Ordered_Set)
      return Boolean
   is
      Node : constant Ada_Node := Unit.Root.Lookup (Location);

   begin
      Available_Imports := [];

      if Node.Kind not in Ada_Base_Id then
         return False;
      end if;

      declare
         Ignore         : Libadalang.Common.Ref_Result_Kind;
         Enclosing_Name : constant Libadalang.Analysis.Name :=
           Get_Appropriate_Enclosing_Name (Node.As_Base_Id);
         Resolved_Name  : constant Defining_Name :=
           Laltools.Common.Resolve_Name
             (Enclosing_Name, LAL_Refactor.Refactor_Trace, Ignore);

      begin
         if Resolved_Name.Is_Null then
            Me.Trace
              ("Can't resolve name "
               & Enclosing_Name.Image
               & ": check for available imports");

            Name := Enclosing_Name;
            Available_Imports :=
              Get_Available_Imports
                (Name  => Enclosing_Name,
                 Units => Units.all);

            return not Available_Imports.Is_Empty;

         else
            Me.Trace
              (Enclosing_Name.Image
               & " can be resolved (result kind: "
               & Ignore'Img
               & "): "
               & "no need to check for missing imports");
            return False;
         end if;
      end;
   end Is_Auto_Import_Available;

   ------------------------------------------
   -- List_And_Expand_Package_Declarations --
   ------------------------------------------

   function List_And_Expand_Package_Declarations
     (Package_Decl    : Base_Package_Decl'Class;
      Include_Private : Boolean)
      return Basic_Decl_Vector
   is
      Result : Basic_Decl_Vector;

      procedure Explore_Declarative_Part (Decls : Ada_Node_List);
      --  Iterates through Decls adding each declaration to All_Decls. If
      --  a declaration is a Package_Decl then it is expanded and nested
      --  declarations are also added to All_Decls

      ------------------------------
      -- Explore_Declarative_Part --
      ------------------------------

      procedure Explore_Declarative_Part (Decls : Ada_Node_List) is
      begin
         for Node of Decls loop
            if Node.Kind in Ada_Basic_Decl then
               --  F_Decls can return nodes that not not inherit from
               --  Basic_Decl type, so ignore those.

               if Node.Kind = Ada_Package_Decl then
                  --  Expand again nested Package_Decl nodes.

                  for Nested_Node of List_And_Expand_Package_Declarations
                    (Node.As_Package_Decl, False)
                  loop
                     Result.Append (Nested_Node.As_Basic_Decl);
                  end loop;
               elsif Node.Kind = Ada_Generic_Package_Instantiation
               then
                  --  Expand again nested Generic_Package_Instantiation nodes.

                  for Nested_Node of List_And_Expand_Package_Declarations
                    (Get_Generic_Package_Internal
                       (Node.As_Generic_Package_Instantiation),
                     False)
                  loop
                     Result.Append (Nested_Node.As_Basic_Decl);
                  end loop;
               else
                  --  All other nodes that inherit from Basic_Decl type except
                  --  Package_Decl and Generic_Package_Instantiation can
                  --  be added.

                  Result.Append (Node.As_Basic_Decl);
               end if;
            end if;
         end loop;
      end Explore_Declarative_Part;

   begin
      --  Return an empty set if Package_Decl is null
      if Package_Decl.Is_Null then
         return Result;
      end if;

      if not Package_Decl.F_Public_Part.Is_Null then
         begin
            Explore_Declarative_Part (Package_Decl.F_Public_Part.F_Decls);

         exception
            --  Ignore Constraint_Error thrown by F_Decls due to invalid code.
            --  Continue execution since Pkg_Decl.F_Private_Part.F_Decls might
            --  not have the same issue.
            --  This has been seen in V516-059.
            when Constraint_Error =>
               null;
         end;
      end if;

      if Include_Private and then not Package_Decl.F_Private_Part.Is_Null then
         begin
            Explore_Declarative_Part (Package_Decl.F_Private_Part.F_Decls);

         exception
            --  Ignore Constraint_Error thrown by F_Decls due to invalid code.
            --  Continue execution since Pkg_Decl.F_Public_Part.F_Decls might
            --  have executed correctly.
            --  This has been seen in V516-059.
            when Constraint_Error =>
               null;
         end;
      end if;

      return Result;
   end List_And_Expand_Package_Declarations;

   --------------
   -- Refactor --
   --------------

   overriding
   function Refactor
     (Self           : Auto_Importer;
      Analysis_Units : access function return Analysis_Unit_Array)
      return Refactoring_Edits
   is
      use Langkit_Support.Text;

      Result : Refactoring_Edits;

      procedure Add_Import;
      --  Creates a Text_Edit with the necessary import and adds it to result

      procedure Add_Qualifier;
      --  Creates a Text_Edit with the necessary qualifier and adds it to
      --  result.

      ----------------
      -- Add_Import --
      ----------------

      procedure Add_Import is
         function Has_With_Clause (Unit : Compilation_Unit) return Boolean
           with Pre => not Unit.Is_Null;
         --  Checks if Unit has a With_Clause with the
         --  Self.Import.Import package.

         function Has_Use_Clause (Unit : Compilation_Unit) return Boolean
           with Pre => not Unit.Is_Null;
         --  Checks if Unit has a Use_Package_Clause with the
         --  Self.Import.Import package.

         procedure Remove_With_Clause (Unit : Compilation_Unit)
           with Pre => not Unit.Is_Null;
         --  Creates a Text_Edit that removes the With_Clause with the
         --  Self.Import.Import package from Unit.

         procedure Add_With_Clause (Unit : Compilation_Unit)
           with Pre => not Unit.Is_Null;
         --  Creates a Text_Edit that adds a With_Clause with the
         --  Self.Import.Import package from Unit.

         ---------------------
         -- Has_With_Clause --
         ---------------------

         function Has_With_Clause (Unit : Compilation_Unit) return Boolean
         is
            Import : constant Text_Type :=
              To_Lower (To_Text (Self.Import.Import));

         begin
            for Node of Unit.F_Prelude loop
               if Node.Kind in Ada_With_Clause_Range then
                  for With_Clause of Node.As_With_Clause.F_Packages loop
                     if Import = To_Lower (With_Clause.Text) then
                        return True;
                     end if;
                  end loop;
               end if;
            end loop;

            return False;
         end Has_With_Clause;

         --------------------
         -- Has_Use_Clause --
         --------------------

         function Has_Use_Clause (Unit : Compilation_Unit) return Boolean
         is
            Import : constant Text_Type :=
              To_Lower (To_Text (Self.Import.Import));

         begin
            for Node of Unit.F_Prelude loop
               if Node.Kind in Ada_Use_Package_Clause_Range then
                  for Use_Clause of Node.As_Use_Package_Clause.F_Packages loop
                     if Import = To_Lower (Use_Clause.Text) then
                        return True;
                     end if;
                  end loop;
               end if;
            end loop;

            return False;
         end Has_Use_Clause;

         ------------------------
         -- Remove_With_Clause --
         ------------------------

         procedure Remove_With_Clause (Unit : Compilation_Unit)
         is
            Import : constant Text_Type :=
              To_Lower (To_Text (Self.Import.Import));

            procedure Remove_With_Clause (Clause : Name'Class)
              with Pre =>
                not Clause.Is_Null
                and then Clause.Parent.Kind in Ada_Name_List_Range
                and then Clause.Parent.Parent.Kind in Ada_With_Clause_Range;
            --  Creates a Text_Edit that removes Clause from it's parent
            --  which is a list of the withed packages.

            ------------------------
            -- Remove_With_Clause --
            ------------------------

            procedure Remove_With_Clause (Clause : Name'Class) is
               Individual : constant Boolean :=
                 Clause.Previous_Sibling.Is_Null
                 and then Clause.Next_Sibling.Is_Null;
               First      : constant Boolean :=
                 not Individual
                 and then Clause.Previous_Sibling.Is_Null;

            begin
               if Individual then
                  declare
                     With_Clause              :
                       constant Libadalang.Analysis.With_Clause :=
                         Clause.Parent.Parent.As_With_Clause;
                     With_Clause_Sloc_Range   :
                       constant Source_Location_Range :=
                         With_Clause.Sloc_Range;
                     With_Clause_Next_Sibling : constant Ada_Node :=
                       With_Clause.Next_Sibling;
                     Remove_Whole_Line : constant Boolean :=
                       With_Clause_Next_Sibling.Is_Null
                       or else With_Clause_Next_Sibling.Sloc_Range.Start_Line
                                 /= With_Clause_Sloc_Range.End_Line;

                     Edit : constant Text_Edit :=
                       (Location =>
                          (if Remove_Whole_Line then
                             Make_Range
                               (Start_Sloc (With_Clause_Sloc_Range),
                                (Line   => With_Clause_Sloc_Range.End_Line + 1,
                                 Column => 1))

                           else
                             Make_Range
                               (Start_Sloc (With_Clause_Sloc_Range),
                                Start_Sloc
                                  (With_Clause_Next_Sibling.Sloc_Range))),
                        Text     => Null_Unbounded_String);

                  begin
                     Safe_Insert
                       (Result.Text_Edits, Unit.Unit.Get_Filename, Edit);
                  end;

               elsif First then
                  declare
                     Edit : constant Text_Edit :=
                       (Location =>
                          Make_Range
                            (Start_Sloc (Clause.Sloc_Range),
                             Start_Sloc (Clause.Next_Sibling.Sloc_Range)),
                        Text     => Null_Unbounded_String);

                  begin
                     Safe_Insert
                       (Result.Text_Edits, Unit.Unit.Get_Filename, Edit);
                  end;

               else
                  declare
                     Edit : constant Text_Edit :=
                       (Location =>
                          Make_Range
                            (Start_Sloc (Clause.Previous_Sibling.Sloc_Range),
                             Start_Sloc (Clause.Sloc_Range)),
                        Text     => Null_Unbounded_String);

                  begin
                     Safe_Insert
                       (Result.Text_Edits, Unit.Unit.Get_Filename, Edit);
                  end;
               end if;

            end Remove_With_Clause;

         begin
            for Node of Unit.F_Prelude loop
               if Node.Kind in Ada_With_Clause_Range then
                  for With_Clause of Node.As_With_Clause.F_Packages loop
                     if Import = To_Lower (With_Clause.Text) then
                        Remove_With_Clause (With_Clause);

                        return;
                     end if;
                  end loop;
               end if;
            end loop;
         end Remove_With_Clause;

         ---------------------
         -- Add_With_Clause --
         ---------------------

         procedure Add_With_Clause (Unit : Compilation_Unit)
         is
            Insert_Location    : constant Source_Location :=
              Get_Insert_New_Dependency_Location
                (Unit, To_Text (Self.Import.Import));

            Edit : constant Text_Edit :=
              (Location =>
                 Make_Range (Insert_Location, Insert_Location),
               Text     =>
                 ("with "
                  & To_Unbounded_String
                      (To_UTF8 (To_Text (Self.Import.Import)))
                  & ";"
                  & Ada.Characters.Latin_1.LF));

         begin
            Safe_Insert (Result.Text_Edits, Unit.Unit.Get_Filename, Edit);
         end Add_With_Clause;

         Unit : constant Compilation_Unit :=
           Self.Name.P_Enclosing_Compilation_Unit;
         Kind : constant Analysis_Unit_Kind := Unit.P_Unit_Kind;

      begin
         case Kind is
            when Unit_Specification =>
               declare
                  Other_Unit : constant Compilation_Unit := Unit.P_Other_Part;

               begin
                  if Has_With_Clause (Unit) then
                     null;
                  else
                     Add_With_Clause (Unit);
                     if not Other_Unit.Is_Null
                       and then Has_With_Clause (Other_Unit)
                       and then not Has_Use_Clause (Other_Unit)
                     then
                        Remove_With_Clause (Other_Unit);
                     end if;
                  end if;
               end;

            when Unit_Body =>
               declare
                  Other_Unit : constant Compilation_Unit := Unit.P_Other_Part;

               begin
                  if Has_With_Clause (Unit)
                    or (not Other_Unit.Is_Null
                        and then Has_With_Clause (Other_Unit))
                  then
                     null;

                  else
                     Add_With_Clause (Unit);
                  end if;
               end;

         end case;
      end Add_Import;

      -------------------
      -- Add_Qualifier --
      -------------------

      procedure Add_Qualifier is
         use Ada.Strings.Wide_Wide_Unbounded;

         Qualifier : constant Text_Edit :=
           (if Self.Name.Parent.Kind in Ada_Dotted_Name then
              Text_Edit'
                (Location =>
                   Self.Name.Parent.As_Dotted_Name.F_Prefix.Sloc_Range,
                 Text     =>
                   To_Unbounded_String
                     (To_UTF8 (To_Text (Self.Import.Qualifier))))
            else
              Text_Edit'
                (Location =>
                   Make_Range
                     (Start_Sloc (Self.Name.Sloc_Range),
                      Start_Sloc (Self.Name.Sloc_Range)),
                 Text     =>
                   (if Ada.Strings.Wide_Wide_Unbounded."="
                         (Self.Import.Qualifier,
                          Null_Unbounded_Wide_Wide_String)
                    then
                       To_Unbounded_String
                         (To_UTF8 (To_Text (Self.Import.Qualifier)))
                    else
                       To_Unbounded_String
                         (To_UTF8 (To_Text (Self.Import.Qualifier)))
                       & ".")));

      begin
         Safe_Insert
           (Result.Text_Edits, Self.Name.Unit.Get_Filename, Qualifier);
      end Add_Qualifier;

   begin
      Add_Import;
      if not Ada.Strings.Wide_Wide_Unbounded."="
        (Self.Import.Qualifier,
         Ada.Strings.Wide_Wide_Unbounded.Null_Unbounded_Wide_Wide_String)
      then
         Add_Qualifier;
      end if;

      return Result;
   end Refactor;

end LAL_Refactor.Auto_Import;