1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT COMPILER COMPONENTS                         --
4--                                                                          --
5--                              E R R O U T C                               --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1992-2019, Free Software Foundation, Inc.         --
10--                                                                          --
11-- GNAT is free software;  you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17-- for  more details.  You should have  received  a copy of the GNU General --
18-- Public License  distributed with GNAT; see file COPYING3.  If not, go to --
19-- http://www.gnu.org/licenses for a complete copy of the license.          --
20--                                                                          --
21-- GNAT was originally developed  by the GNAT team at  New York University. --
22-- Extensive contributions were provided by Ada Core Technologies Inc.      --
23--                                                                          --
24------------------------------------------------------------------------------
25
26--  Warning: Error messages can be generated during Gigi processing by direct
27--  calls to error message routines, so it is essential that the processing
28--  in this body be consistent with the requirements for the Gigi processing
29--  environment, and that in particular, no disallowed table expansion is
30--  allowed to occur.
31
32with Atree;    use Atree;
33with Casing;   use Casing;
34with Csets;    use Csets;
35with Debug;    use Debug;
36with Err_Vars; use Err_Vars;
37with Fname;    use Fname;
38with Namet;    use Namet;
39with Opt;      use Opt;
40with Output;   use Output;
41with Sinput;   use Sinput;
42with Snames;   use Snames;
43with Stringt;  use Stringt;
44with Targparm;
45with Uintp;    use Uintp;
46with Widechar; use Widechar;
47
48package body Erroutc is
49
50   -----------------------
51   -- Local Subprograms --
52   -----------------------
53
54   function Matches (S : String; P : String) return Boolean;
55   --  Returns true if the String S patches the pattern P, which can contain
56   --  wild card chars (*). The entire pattern must match the entire string.
57   --  Case is ignored in the comparison (so X matches x).
58
59   ---------------
60   -- Add_Class --
61   ---------------
62
63   procedure Add_Class is
64   begin
65      if Class_Flag then
66         Class_Flag := False;
67         Set_Msg_Char (''');
68         Get_Name_String (Name_Class);
69         Set_Casing (Identifier_Casing (Flag_Source));
70         Set_Msg_Name_Buffer;
71      end if;
72   end Add_Class;
73
74   ----------------------
75   -- Buffer_Ends_With --
76   ----------------------
77
78   function Buffer_Ends_With (C : Character) return Boolean is
79   begin
80      return Msglen > 0 and then Msg_Buffer (Msglen) = C;
81   end Buffer_Ends_With;
82
83   function Buffer_Ends_With (S : String) return Boolean is
84      Len : constant Natural := S'Length;
85   begin
86      return Msglen > Len
87        and then Msg_Buffer (Msglen - Len) = ' '
88        and then Msg_Buffer (Msglen - Len + 1 .. Msglen) = S;
89   end Buffer_Ends_With;
90
91   -------------------
92   -- Buffer_Remove --
93   -------------------
94
95   procedure Buffer_Remove (C : Character) is
96   begin
97      if Buffer_Ends_With (C) then
98         Msglen := Msglen - 1;
99      end if;
100   end Buffer_Remove;
101
102   procedure Buffer_Remove (S : String) is
103   begin
104      if Buffer_Ends_With (S) then
105         Msglen := Msglen - S'Length;
106      end if;
107   end Buffer_Remove;
108
109   -----------------------------
110   -- Check_Duplicate_Message --
111   -----------------------------
112
113   procedure Check_Duplicate_Message (M1, M2 : Error_Msg_Id) is
114      L1, L2 : Error_Msg_Id;
115      N1, N2 : Error_Msg_Id;
116
117      procedure Delete_Msg (Delete, Keep : Error_Msg_Id);
118      --  Called to delete message Delete, keeping message Keep. Marks msg
119      --  Delete and all its continuations with deleted flag set to True.
120      --  Also makes sure that for the error messages that are retained the
121      --  preferred message is the one retained (we prefer the shorter one in
122      --  the case where one has an Instance tag). Note that we always know
123      --  that Keep has at least as many continuations as Delete (since we
124      --  always delete the shorter sequence).
125
126      ----------------
127      -- Delete_Msg --
128      ----------------
129
130      procedure Delete_Msg (Delete, Keep : Error_Msg_Id) is
131         D, K : Error_Msg_Id;
132
133      begin
134         D := Delete;
135         K := Keep;
136
137         loop
138            Errors.Table (D).Deleted := True;
139
140            --  Adjust error message count
141
142            if Errors.Table (D).Info then
143
144               if Errors.Table (D).Warn then
145                  Warning_Info_Messages := Warning_Info_Messages - 1;
146                  Warnings_Detected := Warnings_Detected - 1;
147               else
148                  Report_Info_Messages := Report_Info_Messages - 1;
149               end if;
150
151            elsif Errors.Table (D).Warn or else Errors.Table (D).Style then
152               Warnings_Detected := Warnings_Detected - 1;
153
154               --  Note: we do not need to decrement Warnings_Treated_As_Errors
155               --  because this only gets incremented if we actually output the
156               --  message, which we won't do if we are deleting it here!
157
158            elsif Errors.Table (D).Check then
159               Check_Messages := Check_Messages - 1;
160
161            else
162               Total_Errors_Detected := Total_Errors_Detected - 1;
163
164               if Errors.Table (D).Serious then
165                  Serious_Errors_Detected := Serious_Errors_Detected - 1;
166               end if;
167            end if;
168
169            --  Substitute shorter of the two error messages
170
171            if Errors.Table (K).Text'Length > Errors.Table (D).Text'Length then
172               Errors.Table (K).Text := Errors.Table (D).Text;
173            end if;
174
175            D := Errors.Table (D).Next;
176            K := Errors.Table (K).Next;
177
178            if D = No_Error_Msg or else not Errors.Table (D).Msg_Cont then
179               return;
180            end if;
181         end loop;
182      end Delete_Msg;
183
184   --  Start of processing for Check_Duplicate_Message
185
186   begin
187      --  Both messages must be non-continuation messages and not deleted
188
189      if Errors.Table (M1).Msg_Cont
190        or else Errors.Table (M2).Msg_Cont
191        or else Errors.Table (M1).Deleted
192        or else Errors.Table (M2).Deleted
193      then
194         return;
195      end if;
196
197      --  Definitely not equal if message text does not match
198
199      if not Same_Error (M1, M2) then
200         return;
201      end if;
202
203      --  Same text. See if all continuations are also identical
204
205      L1 := M1;
206      L2 := M2;
207
208      loop
209         N1 := Errors.Table (L1).Next;
210         N2 := Errors.Table (L2).Next;
211
212         --  If M1 continuations have run out, we delete M1, either the
213         --  messages have the same number of continuations, or M2 has
214         --  more and we prefer the one with more anyway.
215
216         if N1 = No_Error_Msg or else not Errors.Table (N1).Msg_Cont then
217            Delete_Msg (M1, M2);
218            return;
219
220         --  If M2 continuations have run out, we delete M2
221
222         elsif N2 = No_Error_Msg or else not Errors.Table (N2).Msg_Cont then
223            Delete_Msg (M2, M1);
224            return;
225
226         --  Otherwise see if continuations are the same, if not, keep both
227         --  sequences, a curious case, but better to keep everything.
228
229         elsif not Same_Error (N1, N2) then
230            return;
231
232         --  If continuations are the same, continue scan
233
234         else
235            L1 := N1;
236            L2 := N2;
237         end if;
238      end loop;
239   end Check_Duplicate_Message;
240
241   ------------------------
242   -- Compilation_Errors --
243   ------------------------
244
245   function Compilation_Errors return Boolean is
246   begin
247      return
248        Total_Errors_Detected /= 0
249          or else (Warnings_Detected - Warning_Info_Messages /= 0
250                    and then Warning_Mode = Treat_As_Error)
251          or else Warnings_Treated_As_Errors /= 0;
252   end Compilation_Errors;
253
254   ------------------
255   -- Debug_Output --
256   ------------------
257
258   procedure Debug_Output (N : Node_Id) is
259   begin
260      if Debug_Flag_1 then
261         Write_Str ("*** following error message posted on node id = #");
262         Write_Int (Int (N));
263         Write_Str (" ***");
264         Write_Eol;
265      end if;
266   end Debug_Output;
267
268   ----------
269   -- dmsg --
270   ----------
271
272   procedure dmsg (Id : Error_Msg_Id) is
273      E : Error_Msg_Object renames Errors.Table (Id);
274
275   begin
276      w ("Dumping error message, Id = ", Int (Id));
277      w ("  Text     = ", E.Text.all);
278      w ("  Next     = ", Int (E.Next));
279      w ("  Prev     = ", Int (E.Prev));
280      w ("  Sfile    = ", Int (E.Sfile));
281
282      Write_Str
283        ("  Sptr     = ");
284      Write_Location (E.Sptr);
285      Write_Eol;
286
287      Write_Str
288        ("  Optr     = ");
289      Write_Location (E.Optr);
290      Write_Eol;
291
292      w ("  Line     = ", Int (E.Line));
293      w ("  Col      = ", Int (E.Col));
294      w ("  Warn     = ", E.Warn);
295      w ("  Warn_Err = ", E.Warn_Err);
296      w ("  Warn_Chr = '" & E.Warn_Chr & ''');
297      w ("  Style    = ", E.Style);
298      w ("  Serious  = ", E.Serious);
299      w ("  Uncond   = ", E.Uncond);
300      w ("  Msg_Cont = ", E.Msg_Cont);
301      w ("  Deleted  = ", E.Deleted);
302      w ("  Node     = ", Int (E.Node));
303
304      Write_Eol;
305   end dmsg;
306
307   ------------------
308   -- Get_Location --
309   ------------------
310
311   function Get_Location (E : Error_Msg_Id) return Source_Ptr is
312   begin
313      return Errors.Table (E).Sptr;
314   end Get_Location;
315
316   ----------------
317   -- Get_Msg_Id --
318   ----------------
319
320   function Get_Msg_Id return Error_Msg_Id is
321   begin
322      return Cur_Msg;
323   end Get_Msg_Id;
324
325   ---------------------
326   -- Get_Warning_Tag --
327   ---------------------
328
329   function Get_Warning_Tag (Id : Error_Msg_Id) return String is
330      Warn     : constant Boolean    := Errors.Table (Id).Warn;
331      Warn_Chr : constant Character  := Errors.Table (Id).Warn_Chr;
332   begin
333      if Warn and then Warn_Chr /= ' ' then
334         if Warn_Chr = '?' then
335            return "[enabled by default]";
336         elsif Warn_Chr = '*' then
337            return "[restriction warning]";
338         elsif Warn_Chr = '$' then
339            return "[-gnatel]";
340         elsif Warn_Chr in 'a' .. 'z' then
341            return "[-gnatw" & Warn_Chr & ']';
342         else pragma Assert (Warn_Chr in 'A' .. 'Z');
343            return "[-gnatw." & Fold_Lower (Warn_Chr) & ']';
344         end if;
345      else
346         return "";
347      end if;
348   end Get_Warning_Tag;
349
350   -------------
351   -- Matches --
352   -------------
353
354   function Matches (S : String; P : String) return Boolean is
355      Slast : constant Natural := S'Last;
356      PLast : constant Natural := P'Last;
357
358      SPtr : Natural := S'First;
359      PPtr : Natural := P'First;
360
361   begin
362      --  Loop advancing through characters of string and pattern
363
364      SPtr := S'First;
365      PPtr := P'First;
366      loop
367         --  Return True if pattern is a single asterisk
368
369         if PPtr = PLast and then P (PPtr) = '*' then
370            return True;
371
372            --  Return True if both pattern and string exhausted
373
374         elsif PPtr > PLast and then SPtr > Slast then
375            return True;
376
377            --  Return False, if one exhausted and not the other
378
379         elsif PPtr > PLast or else SPtr > Slast then
380            return False;
381
382            --  Case where pattern starts with asterisk
383
384         elsif P (PPtr) = '*' then
385
386            --  Try all possible starting positions in S for match with the
387            --  remaining characters of the pattern. This is the recursive
388            --  call that implements the scanner backup.
389
390            for J in SPtr .. Slast loop
391               if Matches (S (J .. Slast), P (PPtr + 1 .. PLast)) then
392                  return True;
393               end if;
394            end loop;
395
396            return False;
397
398            --  Dealt with end of string and *, advance if we have a match
399
400         elsif Fold_Lower (S (SPtr)) = Fold_Lower (P (PPtr)) then
401            SPtr := SPtr + 1;
402            PPtr := PPtr + 1;
403
404            --  If first characters do not match, that's decisive
405
406         else
407            return False;
408         end if;
409      end loop;
410   end Matches;
411
412   -----------------------
413   -- Output_Error_Msgs --
414   -----------------------
415
416   procedure Output_Error_Msgs (E : in out Error_Msg_Id) is
417      P : Source_Ptr;
418      T : Error_Msg_Id;
419      S : Error_Msg_Id;
420
421      Flag_Num   : Pos;
422      Mult_Flags : Boolean := False;
423
424   begin
425      S := E;
426
427      --  Skip deleted messages at start
428
429      if Errors.Table (S).Deleted then
430         Set_Next_Non_Deleted_Msg (S);
431      end if;
432
433      --  Figure out if we will place more than one error flag on this line
434
435      T := S;
436      while T /= No_Error_Msg
437        and then Errors.Table (T).Line = Errors.Table (E).Line
438        and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
439      loop
440         if Errors.Table (T).Sptr > Errors.Table (E).Sptr then
441            Mult_Flags := True;
442         end if;
443
444         Set_Next_Non_Deleted_Msg (T);
445      end loop;
446
447      --  Output the error flags. The circuit here makes sure that the tab
448      --  characters in the original line are properly accounted for. The
449      --  eight blanks at the start are to match the line number.
450
451      if not Debug_Flag_2 then
452         Write_Str ("        ");
453         P := Line_Start (Errors.Table (E).Sptr);
454         Flag_Num := 1;
455
456         --  Loop through error messages for this line to place flags
457
458         T := S;
459         while T /= No_Error_Msg
460           and then Errors.Table (T).Line = Errors.Table (E).Line
461           and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
462         loop
463            declare
464               Src : Source_Buffer_Ptr
465                       renames Source_Text (Errors.Table (T).Sfile);
466
467            begin
468               --  Loop to output blanks till current flag position
469
470               while P < Errors.Table (T).Sptr loop
471
472                  --  Horizontal tab case, just echo the tab
473
474                  if Src (P) = ASCII.HT then
475                     Write_Char (ASCII.HT);
476                     P := P + 1;
477
478                  --  Deal with wide character case, but don't include brackets
479                  --  notation in this circuit, since we know that this will
480                  --  display unencoded (no one encodes brackets notation).
481
482                  elsif Src (P) /= '['
483                    and then Is_Start_Of_Wide_Char (Src, P)
484                  then
485                     Skip_Wide (Src, P);
486                     Write_Char (' ');
487
488                  --  Normal non-wide character case (or bracket)
489
490                  else
491                     P := P + 1;
492                     Write_Char (' ');
493                  end if;
494               end loop;
495
496               --  Output flag (unless already output, this happens if more
497               --  than one error message occurs at the same flag position).
498
499               if P = Errors.Table (T).Sptr then
500                  if (Flag_Num = 1 and then not Mult_Flags)
501                    or else Flag_Num > 9
502                  then
503                     Write_Char ('|');
504                  else
505                     Write_Char
506                       (Character'Val (Character'Pos ('0') + Flag_Num));
507                  end if;
508
509                  --  Skip past the corresponding source text character
510
511                  --  Horizontal tab case, we output a flag at the tab position
512                  --  so now we output a tab to match up with the text.
513
514                  if Src (P) = ASCII.HT then
515                     Write_Char (ASCII.HT);
516                     P := P + 1;
517
518                  --  Skip wide character other than left bracket
519
520                  elsif Src (P) /= '['
521                    and then Is_Start_Of_Wide_Char (Src, P)
522                  then
523                     Skip_Wide (Src, P);
524
525                  --  Skip normal non-wide character case (or bracket)
526
527                  else
528                     P := P + 1;
529                  end if;
530               end if;
531            end;
532
533            Set_Next_Non_Deleted_Msg (T);
534            Flag_Num := Flag_Num + 1;
535         end loop;
536
537         Write_Eol;
538      end if;
539
540      --  Now output the error messages
541
542      T := S;
543      while T /= No_Error_Msg
544        and then Errors.Table (T).Line = Errors.Table (E).Line
545        and then Errors.Table (T).Sfile = Errors.Table (E).Sfile
546      loop
547         Write_Str ("        >>> ");
548         Output_Msg_Text (T);
549
550         if Debug_Flag_2 then
551            while Column < 74 loop
552               Write_Char (' ');
553            end loop;
554
555            Write_Str (" <<<");
556         end if;
557
558         Write_Eol;
559         Set_Next_Non_Deleted_Msg (T);
560      end loop;
561
562      E := T;
563   end Output_Error_Msgs;
564
565   ------------------------
566   -- Output_Line_Number --
567   ------------------------
568
569   procedure Output_Line_Number (L : Logical_Line_Number) is
570      D     : Int;       -- next digit
571      C     : Character; -- next character
572      Z     : Boolean;   -- flag for zero suppress
573      N, M  : Int;       -- temporaries
574
575   begin
576      if L = No_Line_Number then
577         Write_Str ("        ");
578
579      else
580         Z := False;
581         N := Int (L);
582
583         M := 100_000;
584         while M /= 0 loop
585            D := Int (N / M);
586            N := N rem M;
587            M := M / 10;
588
589            if D = 0 then
590               if Z then
591                  C := '0';
592               else
593                  C := ' ';
594               end if;
595            else
596               Z := True;
597               C := Character'Val (D + 48);
598            end if;
599
600            Write_Char (C);
601         end loop;
602
603         Write_Str (". ");
604      end if;
605   end Output_Line_Number;
606
607   ---------------------
608   -- Output_Msg_Text --
609   ---------------------
610
611   procedure Output_Msg_Text (E : Error_Msg_Id) is
612      Offs : constant Nat := Column - 1;
613      --  Offset to start of message, used for continuations
614
615      Max : Integer;
616      --  Maximum characters to output on next line
617
618      Length : Nat;
619      --  Maximum total length of lines
620
621      Text  : constant String_Ptr := Errors.Table (E).Text;
622      Ptr   : Natural;
623      Split : Natural;
624      Start : Natural;
625
626   begin
627      declare
628         Tag : constant String := Get_Warning_Tag (E);
629         Txt : String_Ptr;
630         Len : Natural;
631
632      begin
633         --  Postfix warning tag to message if needed
634
635         if Tag /= "" and then Warning_Doc_Switch then
636            if Include_Subprogram_In_Messages then
637               Txt :=
638                 new String'
639                   (Subprogram_Name_Ptr (Errors.Table (E).Node) &
640                    ": " & Text.all & ' ' & Tag);
641            else
642               Txt := new String'(Text.all & ' ' & Tag);
643            end if;
644
645         elsif Include_Subprogram_In_Messages
646           and then (Errors.Table (E).Warn or else Errors.Table (E).Style)
647         then
648            Txt :=
649              new String'
650                (Subprogram_Name_Ptr (Errors.Table (E).Node) &
651                 ": " & Text.all);
652         else
653            Txt := Text;
654         end if;
655
656         --  Deal with warning case
657
658         if Errors.Table (E).Warn or else Errors.Table (E).Info then
659
660            --  For info messages, prefix message with "info: "
661
662            if Errors.Table (E).Info then
663               Txt := new String'("info: " & Txt.all);
664
665            --  Warning treated as error
666
667            elsif Errors.Table (E).Warn_Err then
668
669               --  We prefix with "error:" rather than warning: and postfix
670               --  [warning-as-error] at the end.
671
672               Warnings_Treated_As_Errors := Warnings_Treated_As_Errors + 1;
673               Txt := new String'("error: " & Txt.all & " [warning-as-error]");
674
675            --  Normal case, prefix with "warning: "
676
677            else
678               Txt := new String'("warning: " & Txt.all);
679            end if;
680
681            --  No prefix needed for style message, "(style)" is there already
682
683         elsif Errors.Table (E).Style then
684            null;
685
686            --  No prefix needed for check message, severity is there already
687
688         elsif Errors.Table (E).Check then
689            null;
690
691            --  All other cases, add "error: " if unique error tag set
692
693         elsif Opt.Unique_Error_Tag then
694            Txt := new String'("error: " & Txt.all);
695         end if;
696
697         --  Set error message line length and length of message
698
699         if Error_Msg_Line_Length = 0 then
700            Length := Nat'Last;
701         else
702            Length := Error_Msg_Line_Length;
703         end if;
704
705         Max := Integer (Length - Column + 1);
706         Len := Txt'Length;
707
708         --  Here we have to split the message up into multiple lines
709
710         Ptr := 1;
711         loop
712            --  Make sure we do not have ludicrously small line
713
714            Max := Integer'Max (Max, 20);
715
716            --  If remaining text fits, output it respecting LF and we are done
717
718            if Len - Ptr < Max then
719               for J in Ptr .. Len loop
720                  if Txt (J) = ASCII.LF then
721                     Write_Eol;
722                     Write_Spaces (Offs);
723                  else
724                     Write_Char (Txt (J));
725                  end if;
726               end loop;
727
728               return;
729
730            --  Line does not fit
731
732            else
733               Start := Ptr;
734
735               --  First scan forward looking for a hard end of line
736
737               for Scan in Ptr .. Ptr + Max - 1 loop
738                  if Txt (Scan) = ASCII.LF then
739                     Split := Scan - 1;
740                     Ptr := Scan + 1;
741                     goto Continue;
742                  end if;
743               end loop;
744
745               --  Otherwise scan backwards looking for a space
746
747               for Scan in reverse Ptr .. Ptr + Max - 1 loop
748                  if Txt (Scan) = ' ' then
749                     Split := Scan - 1;
750                     Ptr := Scan + 1;
751                     goto Continue;
752                  end if;
753               end loop;
754
755               --  If we fall through, no space, so split line arbitrarily
756
757               Split := Ptr + Max - 1;
758               Ptr := Split + 1;
759            end if;
760
761            <<Continue>>
762            if Start <= Split then
763               Write_Line (Txt (Start .. Split));
764               Write_Spaces (Offs);
765            end if;
766
767            Max := Integer (Length - Column + 1);
768         end loop;
769      end;
770   end Output_Msg_Text;
771
772   ---------------------
773   -- Prescan_Message --
774   ---------------------
775
776   procedure Prescan_Message (Msg : String) is
777      J : Natural;
778
779   begin
780      --  Nothing to do for continuation line
781
782      if Msg (Msg'First) = '\' then
783         return;
784      end if;
785
786      --  Set initial values of globals (may be changed during scan)
787
788      Is_Serious_Error     := True;
789      Is_Unconditional_Msg := False;
790      Is_Warning_Msg       := False;
791      Has_Double_Exclam    := False;
792
793      --  Check style message
794
795      Is_Style_Msg :=
796        Msg'Length > 7 and then Msg (Msg'First .. Msg'First + 6) = "(style)";
797
798      --  Check info message
799
800      Is_Info_Msg :=
801        Msg'Length > 6 and then Msg (Msg'First .. Msg'First + 5) = "info: ";
802
803      --  Check check message
804
805      Is_Check_Msg :=
806        (Msg'Length > 8 and then Msg (Msg'First .. Msg'First + 7) = "medium: ")
807        or else
808          (Msg'Length > 6 and then Msg (Msg'First .. Msg'First + 5) = "high: ")
809        or else
810          (Msg'Length > 5 and then Msg (Msg'First .. Msg'First + 4) = "low: ");
811
812      --  Loop through message looking for relevant insertion sequences
813
814      J := Msg'First;
815      while J <= Msg'Last loop
816
817         --  If we have a quote, don't look at following character
818
819         if Msg (J) = ''' then
820            J := J + 2;
821
822         --  Warning message (? or < insertion sequence)
823
824         elsif Msg (J) = '?' or else Msg (J) = '<' then
825            Is_Warning_Msg := Msg (J) = '?' or else Error_Msg_Warn;
826            Warning_Msg_Char := ' ';
827            J := J + 1;
828
829            if Is_Warning_Msg then
830               declare
831                  C : constant Character := Msg (J - 1);
832               begin
833                  if J <= Msg'Last then
834                     if Msg (J) = C then
835                        Warning_Msg_Char := '?';
836                        J := J + 1;
837
838                     elsif J < Msg'Last and then Msg (J + 1) = C
839                       and then (Msg (J) in 'a' .. 'z' or else
840                                 Msg (J) in 'A' .. 'Z' or else
841                                 Msg (J) = '*'         or else
842                                 Msg (J) = '$')
843                     then
844                        Warning_Msg_Char := Msg (J);
845                        J := J + 2;
846                     end if;
847                  end if;
848               end;
849            end if;
850
851            --  Bomb if untagged warning message. This code can be uncommented
852            --  for debugging when looking for untagged warning messages.
853
854            --  if Is_Warning_Msg and then Warning_Msg_Char = ' ' then
855            --     raise Program_Error;
856            --  end if;
857
858         --  Unconditional message (! insertion)
859
860         elsif Msg (J) = '!' then
861            Is_Unconditional_Msg := True;
862            J := J + 1;
863
864            if J <= Msg'Last and then Msg (J) = '!' then
865               Has_Double_Exclam := True;
866               J := J + 1;
867            end if;
868
869         --  Non-serious error (| insertion)
870
871         elsif Msg (J) = '|' then
872            Is_Serious_Error := False;
873            J := J + 1;
874
875         else
876            J := J + 1;
877         end if;
878      end loop;
879
880      if Is_Info_Msg or Is_Warning_Msg or Is_Style_Msg or Is_Check_Msg then
881         Is_Serious_Error := False;
882      end if;
883   end Prescan_Message;
884
885   --------------------
886   -- Purge_Messages --
887   --------------------
888
889   procedure Purge_Messages (From : Source_Ptr; To : Source_Ptr) is
890      E : Error_Msg_Id;
891
892      function To_Be_Purged (E : Error_Msg_Id) return Boolean;
893      --  Returns True for a message that is to be purged. Also adjusts
894      --  error counts appropriately.
895
896      ------------------
897      -- To_Be_Purged --
898      ------------------
899
900      function To_Be_Purged (E : Error_Msg_Id) return Boolean is
901      begin
902         if E /= No_Error_Msg
903           and then Errors.Table (E).Sptr > From
904           and then Errors.Table (E).Sptr < To
905         then
906            if Errors.Table (E).Warn or else Errors.Table (E).Style then
907               Warnings_Detected := Warnings_Detected - 1;
908
909            else
910               Total_Errors_Detected := Total_Errors_Detected - 1;
911
912               if Errors.Table (E).Serious then
913                  Serious_Errors_Detected := Serious_Errors_Detected - 1;
914               end if;
915            end if;
916
917            return True;
918
919         else
920            return False;
921         end if;
922      end To_Be_Purged;
923
924   --  Start of processing for Purge_Messages
925
926   begin
927      while To_Be_Purged (First_Error_Msg) loop
928         First_Error_Msg := Errors.Table (First_Error_Msg).Next;
929      end loop;
930
931      E := First_Error_Msg;
932      while E /= No_Error_Msg loop
933         while To_Be_Purged (Errors.Table (E).Next) loop
934            Errors.Table (E).Next :=
935              Errors.Table (Errors.Table (E).Next).Next;
936         end loop;
937
938         E := Errors.Table (E).Next;
939      end loop;
940   end Purge_Messages;
941
942   ----------------
943   -- Same_Error --
944   ----------------
945
946   function Same_Error (M1, M2 : Error_Msg_Id) return Boolean is
947      Msg1 : constant String_Ptr := Errors.Table (M1).Text;
948      Msg2 : constant String_Ptr := Errors.Table (M2).Text;
949
950      Msg2_Len : constant Integer := Msg2'Length;
951      Msg1_Len : constant Integer := Msg1'Length;
952
953   begin
954      return
955        Msg1.all = Msg2.all
956          or else
957            (Msg1_Len - 10 > Msg2_Len
958               and then
959             Msg2.all = Msg1.all (1 .. Msg2_Len)
960               and then
961             Msg1 (Msg2_Len + 1 .. Msg2_Len + 10) = ", instance")
962          or else
963            (Msg2_Len - 10 > Msg1_Len
964               and then
965             Msg1.all = Msg2.all (1 .. Msg1_Len)
966               and then
967             Msg2 (Msg1_Len + 1 .. Msg1_Len + 10) = ", instance");
968   end Same_Error;
969
970   -------------------
971   -- Set_Msg_Blank --
972   -------------------
973
974   procedure Set_Msg_Blank is
975   begin
976      if Msglen > 0
977        and then Msg_Buffer (Msglen) /= ' '
978        and then Msg_Buffer (Msglen) /= '('
979        and then Msg_Buffer (Msglen) /= '-'
980        and then not Manual_Quote_Mode
981      then
982         Set_Msg_Char (' ');
983      end if;
984   end Set_Msg_Blank;
985
986   -------------------------------
987   -- Set_Msg_Blank_Conditional --
988   -------------------------------
989
990   procedure Set_Msg_Blank_Conditional is
991   begin
992      if Msglen > 0
993        and then Msg_Buffer (Msglen) /= ' '
994        and then Msg_Buffer (Msglen) /= '('
995        and then Msg_Buffer (Msglen) /= '"'
996        and then not Manual_Quote_Mode
997      then
998         Set_Msg_Char (' ');
999      end if;
1000   end Set_Msg_Blank_Conditional;
1001
1002   ------------------
1003   -- Set_Msg_Char --
1004   ------------------
1005
1006   procedure Set_Msg_Char (C : Character) is
1007   begin
1008
1009      --  The check for message buffer overflow is needed to deal with cases
1010      --  where insertions get too long (in particular a child unit name can
1011      --  be very long).
1012
1013      if Msglen < Max_Msg_Length then
1014         Msglen := Msglen + 1;
1015         Msg_Buffer (Msglen) := C;
1016      end if;
1017   end Set_Msg_Char;
1018
1019   ---------------------------------
1020   -- Set_Msg_Insertion_File_Name --
1021   ---------------------------------
1022
1023   procedure Set_Msg_Insertion_File_Name is
1024   begin
1025      if Error_Msg_File_1 = No_File then
1026         null;
1027
1028      elsif Error_Msg_File_1 = Error_File_Name then
1029         Set_Msg_Blank;
1030         Set_Msg_Str ("<error>");
1031
1032      else
1033         Set_Msg_Blank;
1034         Get_Name_String (Error_Msg_File_1);
1035         Set_Msg_Quote;
1036         Set_Msg_Name_Buffer;
1037         Set_Msg_Quote;
1038      end if;
1039
1040      --  The following assignments ensure that the second and third {
1041      --  insertion characters will correspond to the Error_Msg_File_2 and
1042      --  Error_Msg_File_3 values and We suppress possible validity checks in
1043      --  case operating in -gnatVa mode, and Error_Msg_File_2 or
1044      --  Error_Msg_File_3 is not needed and has not been set.
1045
1046      declare
1047         pragma Suppress (Range_Check);
1048      begin
1049         Error_Msg_File_1 := Error_Msg_File_2;
1050         Error_Msg_File_2 := Error_Msg_File_3;
1051      end;
1052   end Set_Msg_Insertion_File_Name;
1053
1054   -----------------------------------
1055   -- Set_Msg_Insertion_Line_Number --
1056   -----------------------------------
1057
1058   procedure Set_Msg_Insertion_Line_Number (Loc, Flag : Source_Ptr) is
1059      Sindex_Loc  : Source_File_Index;
1060      Sindex_Flag : Source_File_Index;
1061      Fname       : File_Name_Type;
1062      Int_File    : Boolean;
1063
1064      procedure Set_At;
1065      --  Outputs "at " unless last characters in buffer are " from ". Certain
1066      --  messages read better with from than at.
1067
1068      ------------
1069      -- Set_At --
1070      ------------
1071
1072      procedure Set_At is
1073      begin
1074         if Msglen < 6
1075           or else Msg_Buffer (Msglen - 5 .. Msglen) /= " from "
1076         then
1077            Set_Msg_Str ("at ");
1078         end if;
1079      end Set_At;
1080
1081   --  Start of processing for Set_Msg_Insertion_Line_Number
1082
1083   begin
1084      Set_Msg_Blank;
1085
1086      if Loc = No_Location then
1087         Set_At;
1088         Set_Msg_Str ("unknown location");
1089
1090      elsif Loc = System_Location then
1091         Set_Msg_Str ("in package System");
1092         Set_Msg_Insertion_Run_Time_Name;
1093
1094      elsif Loc = Standard_Location then
1095         Set_Msg_Str ("in package Standard");
1096
1097      elsif Loc = Standard_ASCII_Location then
1098         Set_Msg_Str ("in package Standard.ASCII");
1099
1100      else
1101         --  Add "at file-name:" if reference is to other than the source
1102         --  file in which the error message is placed. Note that we check
1103         --  full file names, rather than just the source indexes, to
1104         --  deal with generic instantiations from the current file.
1105
1106         Sindex_Loc  := Get_Source_File_Index (Loc);
1107         Sindex_Flag := Get_Source_File_Index (Flag);
1108
1109         if Full_File_Name (Sindex_Loc) /= Full_File_Name (Sindex_Flag) then
1110            Set_At;
1111            Fname := Reference_Name (Get_Source_File_Index (Loc));
1112            Int_File := Is_Internal_File_Name (Fname);
1113            Get_Name_String (Fname);
1114            Set_Msg_Name_Buffer;
1115
1116            if not (Int_File and Debug_Flag_Dot_K) then
1117               Set_Msg_Char (':');
1118               Set_Msg_Int (Int (Get_Logical_Line_Number (Loc)));
1119            end if;
1120
1121         --  If in current file, add text "at line "
1122
1123         else
1124            Set_At;
1125            Set_Msg_Str ("line ");
1126            Int_File := False;
1127            Set_Msg_Int (Int (Get_Logical_Line_Number (Loc)));
1128         end if;
1129
1130         --  Deal with the instantiation case. We may have a reference to,
1131         --  e.g. a type, that is declared within a generic template, and
1132         --  what we are really referring to is the occurrence in an instance.
1133         --  In this case, the line number of the instantiation is also of
1134         --  interest, and we add a notation:
1135
1136         --    , instance at xxx
1137
1138         --  where xxx is a line number output using this same routine (and
1139         --  the recursion can go further if the instantiation is itself in
1140         --  a generic template).
1141
1142         --  The flag location passed to us in this situation is indeed the
1143         --  line number within the template, but as described in Sinput.L
1144         --  (file sinput-l.ads, section "Handling Generic Instantiations")
1145         --  we can retrieve the location of the instantiation itself from
1146         --  this flag location value.
1147
1148         --  Note: this processing is suppressed if Suppress_Instance_Location
1149         --  is set True. This is used to prevent redundant annotations of the
1150         --  location of the instantiation in the case where we are placing
1151         --  the messages on the instantiation in any case.
1152
1153         if Instantiation (Sindex_Loc) /= No_Location
1154           and then not Suppress_Instance_Location
1155         then
1156            Set_Msg_Str (", instance ");
1157            Set_Msg_Insertion_Line_Number (Instantiation (Sindex_Loc), Flag);
1158         end if;
1159      end if;
1160   end Set_Msg_Insertion_Line_Number;
1161
1162   ----------------------------
1163   -- Set_Msg_Insertion_Name --
1164   ----------------------------
1165
1166   procedure Set_Msg_Insertion_Name is
1167   begin
1168      if Error_Msg_Name_1 = No_Name then
1169         null;
1170
1171      elsif Error_Msg_Name_1 = Error_Name then
1172         Set_Msg_Blank;
1173         Set_Msg_Str ("<error>");
1174
1175      else
1176         Set_Msg_Blank_Conditional;
1177         Get_Unqualified_Decoded_Name_String (Error_Msg_Name_1);
1178
1179         --  Remove %s or %b at end. These come from unit names. If the
1180         --  caller wanted the (unit) or (body), then they would have used
1181         --  the $ insertion character. Certainly no error message should
1182         --  ever have %b or %s explicitly occurring.
1183
1184         if Name_Len > 2
1185           and then Name_Buffer (Name_Len - 1) = '%'
1186           and then (Name_Buffer (Name_Len) = 'b'
1187                       or else
1188                     Name_Buffer (Name_Len) = 's')
1189         then
1190            Name_Len := Name_Len - 2;
1191         end if;
1192
1193         --  Remove upper case letter at end, again, we should not be getting
1194         --  such names, and what we hope is that the remainder makes sense.
1195
1196         if Name_Len > 1 and then Name_Buffer (Name_Len) in 'A' .. 'Z' then
1197            Name_Len := Name_Len - 1;
1198         end if;
1199
1200         --  If operator name or character literal name, just print it as is
1201         --  Also print as is if it ends in a right paren (case of x'val(nnn))
1202
1203         if Name_Buffer (1) = '"'
1204           or else Name_Buffer (1) = '''
1205           or else Name_Buffer (Name_Len) = ')'
1206         then
1207            Set_Msg_Name_Buffer;
1208
1209         --  Else output with surrounding quotes in proper casing mode
1210
1211         else
1212            Set_Casing (Identifier_Casing (Flag_Source));
1213            Set_Msg_Quote;
1214            Set_Msg_Name_Buffer;
1215            Set_Msg_Quote;
1216         end if;
1217      end if;
1218
1219      --  The following assignments ensure that the second and third percent
1220      --  insertion characters will correspond to the Error_Msg_Name_2 and
1221      --  Error_Msg_Name_3 as required. We suppress possible validity checks in
1222      --  case operating in -gnatVa mode, and Error_Msg_Name_1/2 is not needed
1223      --  and has not been set.
1224
1225      declare
1226         pragma Suppress (Range_Check);
1227      begin
1228         Error_Msg_Name_1 := Error_Msg_Name_2;
1229         Error_Msg_Name_2 := Error_Msg_Name_3;
1230      end;
1231   end Set_Msg_Insertion_Name;
1232
1233   ------------------------------------
1234   -- Set_Msg_Insertion_Name_Literal --
1235   ------------------------------------
1236
1237   procedure Set_Msg_Insertion_Name_Literal is
1238   begin
1239      if Error_Msg_Name_1 = No_Name then
1240         null;
1241
1242      elsif Error_Msg_Name_1 = Error_Name then
1243         Set_Msg_Blank;
1244         Set_Msg_Str ("<error>");
1245
1246      else
1247         Set_Msg_Blank;
1248         Get_Name_String (Error_Msg_Name_1);
1249         Set_Msg_Quote;
1250         Set_Msg_Name_Buffer;
1251         Set_Msg_Quote;
1252      end if;
1253
1254      --  The following assignments ensure that the second and third % or %%
1255      --  insertion characters will correspond to the Error_Msg_Name_2 and
1256      --  Error_Msg_Name_3 values and We suppress possible validity checks in
1257      --  case operating in -gnatVa mode, and Error_Msg_Name_2 or
1258      --  Error_Msg_Name_3 is not needed and has not been set.
1259
1260      declare
1261         pragma Suppress (Range_Check);
1262      begin
1263         Error_Msg_Name_1 := Error_Msg_Name_2;
1264         Error_Msg_Name_2 := Error_Msg_Name_3;
1265      end;
1266   end Set_Msg_Insertion_Name_Literal;
1267
1268   -------------------------------------
1269   -- Set_Msg_Insertion_Reserved_Name --
1270   -------------------------------------
1271
1272   procedure Set_Msg_Insertion_Reserved_Name is
1273   begin
1274      Set_Msg_Blank_Conditional;
1275      Get_Name_String (Error_Msg_Name_1);
1276      Set_Msg_Quote;
1277      Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case);
1278      Set_Msg_Name_Buffer;
1279      Set_Msg_Quote;
1280   end Set_Msg_Insertion_Reserved_Name;
1281
1282   -------------------------------------
1283   -- Set_Msg_Insertion_Reserved_Word --
1284   -------------------------------------
1285
1286   procedure Set_Msg_Insertion_Reserved_Word
1287     (Text : String;
1288      J    : in out Integer)
1289   is
1290   begin
1291      Set_Msg_Blank_Conditional;
1292      Name_Len := 0;
1293
1294      while J <= Text'Last and then Text (J) in 'A' .. 'Z' loop
1295         Add_Char_To_Name_Buffer (Text (J));
1296         J := J + 1;
1297      end loop;
1298
1299      --  Here is where we make the special exception for RM
1300
1301      if Name_Len = 2 and then Name_Buffer (1 .. 2) = "RM" then
1302         Set_Msg_Name_Buffer;
1303
1304      --  We make a similar exception for SPARK
1305
1306      elsif Name_Len = 5 and then Name_Buffer (1 .. 5) = "SPARK" then
1307         Set_Msg_Name_Buffer;
1308
1309      --  Neither RM nor SPARK: case appropriately and add surrounding quotes
1310
1311      else
1312         Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case);
1313         Set_Msg_Quote;
1314         Set_Msg_Name_Buffer;
1315         Set_Msg_Quote;
1316      end if;
1317   end Set_Msg_Insertion_Reserved_Word;
1318
1319   -------------------------------------
1320   -- Set_Msg_Insertion_Run_Time_Name --
1321   -------------------------------------
1322
1323   procedure Set_Msg_Insertion_Run_Time_Name is
1324   begin
1325      if Targparm.Run_Time_Name_On_Target /= No_Name then
1326         Set_Msg_Blank_Conditional;
1327         Set_Msg_Char ('(');
1328         Get_Name_String (Targparm.Run_Time_Name_On_Target);
1329         Set_Casing (Mixed_Case);
1330         Set_Msg_Str (Name_Buffer (1 .. Name_Len));
1331         Set_Msg_Char (')');
1332      end if;
1333   end Set_Msg_Insertion_Run_Time_Name;
1334
1335   ----------------------------
1336   -- Set_Msg_Insertion_Uint --
1337   ----------------------------
1338
1339   procedure Set_Msg_Insertion_Uint is
1340   begin
1341      Set_Msg_Blank;
1342      UI_Image (Error_Msg_Uint_1);
1343
1344      for J in 1 .. UI_Image_Length loop
1345         Set_Msg_Char (UI_Image_Buffer (J));
1346      end loop;
1347
1348      --  The following assignment ensures that a second caret insertion
1349      --  character will correspond to the Error_Msg_Uint_2 parameter. We
1350      --  suppress possible validity checks in case operating in -gnatVa mode,
1351      --  and Error_Msg_Uint_2 is not needed and has not been set.
1352
1353      declare
1354         pragma Suppress (Range_Check);
1355      begin
1356         Error_Msg_Uint_1 := Error_Msg_Uint_2;
1357      end;
1358   end Set_Msg_Insertion_Uint;
1359
1360   -----------------
1361   -- Set_Msg_Int --
1362   -----------------
1363
1364   procedure Set_Msg_Int (Line : Int) is
1365   begin
1366      if Line > 9 then
1367         Set_Msg_Int (Line / 10);
1368      end if;
1369
1370      Set_Msg_Char (Character'Val (Character'Pos ('0') + (Line rem 10)));
1371   end Set_Msg_Int;
1372
1373   -------------------------
1374   -- Set_Msg_Name_Buffer --
1375   -------------------------
1376
1377   procedure Set_Msg_Name_Buffer is
1378   begin
1379      Set_Msg_Str (Name_Buffer (1 .. Name_Len));
1380   end Set_Msg_Name_Buffer;
1381
1382   -------------------
1383   -- Set_Msg_Quote --
1384   -------------------
1385
1386   procedure Set_Msg_Quote is
1387   begin
1388      if not Manual_Quote_Mode then
1389         Set_Msg_Char ('"');
1390      end if;
1391   end Set_Msg_Quote;
1392
1393   -----------------
1394   -- Set_Msg_Str --
1395   -----------------
1396
1397   procedure Set_Msg_Str (Text : String) is
1398   begin
1399      --  Do replacement for special x'Class aspect names
1400
1401      if Text = "_Pre" then
1402         Set_Msg_Str ("Pre'Class");
1403
1404      elsif Text = "_Post" then
1405         Set_Msg_Str ("Post'Class");
1406
1407      elsif Text = "_Type_Invariant" then
1408         Set_Msg_Str ("Type_Invariant'Class");
1409
1410      elsif Text = "_pre" then
1411         Set_Msg_Str ("pre'class");
1412
1413      elsif Text = "_post" then
1414         Set_Msg_Str ("post'class");
1415
1416      elsif Text = "_type_invariant" then
1417         Set_Msg_Str ("type_invariant'class");
1418
1419      elsif Text = "_PRE" then
1420         Set_Msg_Str ("PRE'CLASS");
1421
1422      elsif Text = "_POST" then
1423         Set_Msg_Str ("POST'CLASS");
1424
1425      elsif Text = "_TYPE_INVARIANT" then
1426         Set_Msg_Str ("TYPE_INVARIANT'CLASS");
1427
1428      --  Normal case with no replacement
1429
1430      else
1431         for J in Text'Range loop
1432            Set_Msg_Char (Text (J));
1433         end loop;
1434      end if;
1435   end Set_Msg_Str;
1436
1437   ------------------------------
1438   -- Set_Next_Non_Deleted_Msg --
1439   ------------------------------
1440
1441   procedure Set_Next_Non_Deleted_Msg (E : in out Error_Msg_Id) is
1442   begin
1443      if E = No_Error_Msg then
1444         return;
1445
1446      else
1447         loop
1448            E := Errors.Table (E).Next;
1449            exit when E = No_Error_Msg or else not Errors.Table (E).Deleted;
1450         end loop;
1451      end if;
1452   end Set_Next_Non_Deleted_Msg;
1453
1454   ------------------------------
1455   -- Set_Specific_Warning_Off --
1456   ------------------------------
1457
1458   procedure Set_Specific_Warning_Off
1459     (Loc    : Source_Ptr;
1460      Msg    : String;
1461      Reason : String_Id;
1462      Config : Boolean;
1463      Used   : Boolean := False)
1464   is
1465   begin
1466      Specific_Warnings.Append
1467        ((Start      => Loc,
1468          Msg        => new String'(Msg),
1469          Stop       => Source_Last (Get_Source_File_Index (Loc)),
1470          Reason     => Reason,
1471          Open       => True,
1472          Used       => Used,
1473          Config     => Config));
1474   end Set_Specific_Warning_Off;
1475
1476   -----------------------------
1477   -- Set_Specific_Warning_On --
1478   -----------------------------
1479
1480   procedure Set_Specific_Warning_On
1481     (Loc : Source_Ptr;
1482      Msg : String;
1483      Err : out Boolean)
1484   is
1485   begin
1486      for J in 1 .. Specific_Warnings.Last loop
1487         declare
1488            SWE : Specific_Warning_Entry renames Specific_Warnings.Table (J);
1489
1490         begin
1491            if Msg = SWE.Msg.all
1492              and then Loc > SWE.Start
1493              and then SWE.Open
1494              and then Get_Source_File_Index (SWE.Start) =
1495                       Get_Source_File_Index (Loc)
1496            then
1497               SWE.Stop := Loc;
1498               SWE.Open := False;
1499               Err := False;
1500
1501               --  If a config pragma is specifically cancelled, consider
1502               --  that it is no longer active as a configuration pragma.
1503
1504               SWE.Config := False;
1505               return;
1506            end if;
1507         end;
1508      end loop;
1509
1510      Err := True;
1511   end Set_Specific_Warning_On;
1512
1513   ---------------------------
1514   -- Set_Warnings_Mode_Off --
1515   ---------------------------
1516
1517   procedure Set_Warnings_Mode_Off (Loc : Source_Ptr; Reason : String_Id) is
1518   begin
1519      --  Don't bother with entries from instantiation copies, since we will
1520      --  already have a copy in the template, which is what matters.
1521
1522      if Instantiation (Get_Source_File_Index (Loc)) /= No_Location then
1523         return;
1524      end if;
1525
1526      --  If all warnings are suppressed by command line switch, this can
1527      --  be ignored, unless we are in GNATprove_Mode which requires pragma
1528      --  Warnings to be stored for the formal verification backend.
1529
1530      if Warning_Mode = Suppress
1531        and then not GNATprove_Mode
1532      then
1533         return;
1534      end if;
1535
1536      --  If last entry in table already covers us, this is a redundant pragma
1537      --  Warnings (Off) and can be ignored.
1538
1539      if Warnings.Last >= Warnings.First
1540        and then Warnings.Table (Warnings.Last).Start <= Loc
1541        and then Loc <= Warnings.Table (Warnings.Last).Stop
1542      then
1543         return;
1544      end if;
1545
1546      --  If none of those special conditions holds, establish a new entry,
1547      --  extending from the location of the pragma to the end of the current
1548      --  source file. This ending point will be adjusted by a subsequent
1549      --  corresponding pragma Warnings (On).
1550
1551      Warnings.Append
1552        ((Start  => Loc,
1553          Stop   => Source_Last (Get_Source_File_Index (Loc)),
1554          Reason => Reason));
1555   end Set_Warnings_Mode_Off;
1556
1557   --------------------------
1558   -- Set_Warnings_Mode_On --
1559   --------------------------
1560
1561   procedure Set_Warnings_Mode_On (Loc : Source_Ptr) is
1562   begin
1563      --  Don't bother with entries from instantiation copies, since we will
1564      --  already have a copy in the template, which is what matters.
1565
1566      if Instantiation (Get_Source_File_Index (Loc)) /= No_Location then
1567         return;
1568      end if;
1569
1570      --  If all warnings are suppressed by command line switch, this can
1571      --  be ignored, unless we are in GNATprove_Mode which requires pragma
1572      --  Warnings to be stored for the formal verification backend.
1573
1574      if Warning_Mode = Suppress
1575        and then not GNATprove_Mode
1576      then
1577         return;
1578      end if;
1579
1580      --  If the last entry in the warnings table covers this pragma, then
1581      --  we adjust the end point appropriately.
1582
1583      if Warnings.Last >= Warnings.First
1584        and then Warnings.Table (Warnings.Last).Start <= Loc
1585        and then Loc <= Warnings.Table (Warnings.Last).Stop
1586      then
1587         Warnings.Table (Warnings.Last).Stop := Loc;
1588      end if;
1589   end Set_Warnings_Mode_On;
1590
1591   --------------------------------
1592   -- Validate_Specific_Warnings --
1593   --------------------------------
1594
1595   procedure Validate_Specific_Warnings (Eproc : Error_Msg_Proc) is
1596   begin
1597      if not Warn_On_Warnings_Off then
1598         return;
1599      end if;
1600
1601      for J in Specific_Warnings.First .. Specific_Warnings.Last loop
1602         declare
1603            SWE : Specific_Warning_Entry renames Specific_Warnings.Table (J);
1604
1605         begin
1606            if not SWE.Config then
1607
1608               --  Warn for unmatched Warnings (Off, ...)
1609
1610               if SWE.Open then
1611                  Eproc.all
1612                    ("?W?pragma Warnings Off with no matching Warnings On",
1613                     SWE.Start);
1614
1615               --  Warn for ineffective Warnings (Off, ..)
1616
1617               elsif not SWE.Used
1618
1619                 --  Do not issue this warning for -Wxxx messages since the
1620                 --  back-end doesn't report the information. Note that there
1621                 --  is always an asterisk at the start of every message.
1622
1623                 and then not
1624                   (SWE.Msg'Length > 3 and then SWE.Msg (2 .. 3) = "-W")
1625               then
1626                  Eproc.all
1627                    ("?W?no warning suppressed by this pragma", SWE.Start);
1628               end if;
1629            end if;
1630         end;
1631      end loop;
1632   end Validate_Specific_Warnings;
1633
1634   -------------------------------------
1635   -- Warning_Specifically_Suppressed --
1636   -------------------------------------
1637
1638   function Warning_Specifically_Suppressed
1639     (Loc : Source_Ptr;
1640      Msg : String_Ptr;
1641      Tag : String := "") return String_Id
1642   is
1643   begin
1644      --  Loop through specific warning suppression entries
1645
1646      for J in Specific_Warnings.First .. Specific_Warnings.Last loop
1647         declare
1648            SWE : Specific_Warning_Entry renames Specific_Warnings.Table (J);
1649
1650         begin
1651            --  Pragma applies if it is a configuration pragma, or if the
1652            --  location is in range of a specific non-configuration pragma.
1653
1654            if SWE.Config
1655              or else (SWE.Start <= Loc and then Loc <= SWE.Stop)
1656            then
1657               if Matches (Msg.all, SWE.Msg.all)
1658                 or else Matches (Tag, SWE.Msg.all)
1659               then
1660                  SWE.Used := True;
1661                  return SWE.Reason;
1662               end if;
1663            end if;
1664         end;
1665      end loop;
1666
1667      return No_String;
1668   end Warning_Specifically_Suppressed;
1669
1670   ------------------------------
1671   -- Warning_Treated_As_Error --
1672   ------------------------------
1673
1674   function Warning_Treated_As_Error (Msg : String) return Boolean is
1675   begin
1676      for J in 1 .. Warnings_As_Errors_Count loop
1677         if Matches (Msg, Warnings_As_Errors (J).all) then
1678            return True;
1679         end if;
1680      end loop;
1681
1682      return False;
1683   end Warning_Treated_As_Error;
1684
1685   -------------------------
1686   -- Warnings_Suppressed --
1687   -------------------------
1688
1689   function Warnings_Suppressed (Loc : Source_Ptr) return String_Id is
1690   begin
1691      --  Loop through table of ON/OFF warnings
1692
1693      for J in Warnings.First .. Warnings.Last loop
1694         if Warnings.Table (J).Start <= Loc
1695           and then Loc <= Warnings.Table (J).Stop
1696         then
1697            return Warnings.Table (J).Reason;
1698         end if;
1699      end loop;
1700
1701      if Warning_Mode = Suppress then
1702         return Null_String_Id;
1703      else
1704         return No_String;
1705      end if;
1706   end Warnings_Suppressed;
1707
1708end Erroutc;
1709