| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E R R O U T C -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2025, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| -- Warning: Error messages can be generated during Gigi processing by direct |
| -- calls to error message routines, so it is essential that the processing |
| -- in this body be consistent with the requirements for the Gigi processing |
| -- environment, and that in particular, no disallowed table expansion is |
| -- allowed to occur. |
| |
| with Atree; use Atree; |
| with Casing; use Casing; |
| with Csets; use Csets; |
| with Debug; use Debug; |
| with Err_Vars; use Err_Vars; |
| with Fname; use Fname; |
| with Namet; use Namet; |
| with Opt; use Opt; |
| with Output; use Output; |
| with Sinfo.Nodes; |
| with Sinput; use Sinput; |
| with Snames; use Snames; |
| with Stringt; use Stringt; |
| with Stylesw; use Stylesw; |
| with Targparm; |
| with Uintp; use Uintp; |
| with Widechar; use Widechar; |
| with Warnsw; use Warnsw; |
| |
| package body Erroutc is |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function Sloc_In_Range (Loc, Start, Stop : Source_Ptr) return Boolean; |
| -- Return whether Loc is in the range Start .. Stop, taking instantiation |
| -- locations of Loc into account. This is useful for suppressing warnings |
| -- from generic instantiations by using pragma Warnings around generic |
| -- instances, as needed in GNATprove. |
| |
| function Has_Switch_Tag (Id : Error_Msg_Id) return Boolean; |
| function Has_Switch_Tag (E_Msg : Error_Msg_Object) return Boolean; |
| -- Returns True if the E_Msg is Warning, Style or Info and has a non-empty |
| -- Warn_Char. |
| |
| --------------- |
| -- Add_Class -- |
| --------------- |
| |
| procedure Add_Class is |
| begin |
| if Class_Flag then |
| Class_Flag := False; |
| Set_Msg_Char ('''); |
| Get_Name_String (Name_Class); |
| Set_Casing (Identifier_Casing (Flag_Source)); |
| Set_Msg_Name_Buffer; |
| end if; |
| end Add_Class; |
| |
| ---------------------- |
| -- Buffer_Ends_With -- |
| ---------------------- |
| |
| function Buffer_Ends_With (C : Character) return Boolean is |
| begin |
| return Msglen > 0 and then Msg_Buffer (Msglen) = C; |
| end Buffer_Ends_With; |
| |
| function Buffer_Ends_With (S : String) return Boolean is |
| Len : constant Natural := S'Length; |
| begin |
| return Msglen > Len |
| and then Msg_Buffer (Msglen - Len) = ' ' |
| and then Msg_Buffer (Msglen - Len + 1 .. Msglen) = S; |
| end Buffer_Ends_With; |
| |
| ------------------- |
| -- Buffer_Remove -- |
| ------------------- |
| |
| procedure Buffer_Remove (C : Character) is |
| begin |
| if Buffer_Ends_With (C) then |
| Msglen := Msglen - 1; |
| end if; |
| end Buffer_Remove; |
| |
| procedure Buffer_Remove (S : String) is |
| begin |
| if Buffer_Ends_With (S) then |
| Msglen := Msglen - S'Length; |
| end if; |
| end Buffer_Remove; |
| |
| ----------------------------- |
| -- Check_Duplicate_Message -- |
| ----------------------------- |
| |
| procedure Check_Duplicate_Message (M1, M2 : Error_Msg_Id) is |
| L1, L2 : Error_Msg_Id; |
| N1, N2 : Error_Msg_Id; |
| |
| procedure Delete_Msg (Delete, Keep : Error_Msg_Id); |
| -- Called to delete message Delete, keeping message Keep. Marks msg |
| -- Delete and all its continuations with deleted flag set to True. |
| -- Also makes sure that for the error messages that are retained the |
| -- preferred message is the one retained (we prefer the shorter one in |
| -- the case where one has an Instance tag). Note that we always know |
| -- that Keep has at least as many continuations as Delete (since we |
| -- always delete the shorter sequence). |
| |
| ---------------- |
| -- Delete_Msg -- |
| ---------------- |
| |
| procedure Delete_Msg (Delete, Keep : Error_Msg_Id) is |
| D, K : Error_Msg_Id; |
| |
| begin |
| D := Delete; |
| K := Keep; |
| |
| loop |
| Errors.Table (D).Deleted := True; |
| |
| Decrease_Error_Msg_Count (Errors.Table (D)); |
| |
| -- Substitute shorter of the two error messages |
| |
| if Errors.Table (K).Text'Length > Errors.Table (D).Text'Length then |
| Errors.Table (K).Text := Errors.Table (D).Text; |
| end if; |
| |
| D := Errors.Table (D).Next; |
| K := Errors.Table (K).Next; |
| |
| if D = No_Error_Msg or else not Errors.Table (D).Msg_Cont then |
| return; |
| end if; |
| end loop; |
| end Delete_Msg; |
| |
| -- Start of processing for Check_Duplicate_Message |
| |
| begin |
| -- Both messages must be non-continuation messages and not deleted |
| |
| if Errors.Table (M1).Msg_Cont |
| or else Errors.Table (M2).Msg_Cont |
| or else Errors.Table (M1).Deleted |
| or else Errors.Table (M2).Deleted |
| then |
| return; |
| end if; |
| |
| -- Definitely not equal if message text does not match |
| |
| if not Same_Error (M1, M2) then |
| return; |
| end if; |
| |
| -- Same text. See if all continuations are also identical |
| |
| L1 := M1; |
| L2 := M2; |
| |
| loop |
| N1 := Errors.Table (L1).Next; |
| N2 := Errors.Table (L2).Next; |
| |
| -- If M1 continuations have run out, we delete M1, either the |
| -- messages have the same number of continuations, or M2 has |
| -- more and we prefer the one with more anyway. |
| |
| if N1 = No_Error_Msg or else not Errors.Table (N1).Msg_Cont then |
| Delete_Msg (M1, M2); |
| return; |
| |
| -- If M2 continuations have run out, we delete M2 |
| |
| elsif N2 = No_Error_Msg or else not Errors.Table (N2).Msg_Cont then |
| Delete_Msg (M2, M1); |
| return; |
| |
| -- Otherwise see if continuations are the same, if not, keep both |
| -- sequences, a curious case, but better to keep everything. |
| |
| elsif not Same_Error (N1, N2) then |
| return; |
| |
| -- If continuations are the same, continue scan |
| |
| else |
| L1 := N1; |
| L2 := N2; |
| end if; |
| end loop; |
| end Check_Duplicate_Message; |
| |
| ------------------------ |
| -- Compilation_Errors -- |
| ------------------------ |
| |
| function Compilation_Errors return Boolean is |
| begin |
| return Total_Errors_Detected /= 0 |
| or else Warnings_Treated_As_Errors /= 0; |
| end Compilation_Errors; |
| |
| ------------------------------ |
| -- Decrease_Error_Msg_Count -- |
| ------------------------------ |
| |
| procedure Decrease_Error_Msg_Count (E : Error_Msg_Object) is |
| |
| begin |
| case E.Kind is |
| when Info => |
| Info_Messages := Info_Messages - 1; |
| |
| when Warning | Style => |
| Warnings_Detected := Warnings_Detected - 1; |
| |
| if E.Warn_Err /= None then |
| Warnings_Treated_As_Errors := Warnings_Treated_As_Errors - 1; |
| end if; |
| |
| when High_Check | Medium_Check | Low_Check => |
| Check_Messages := Check_Messages - 1; |
| |
| when Error => |
| Total_Errors_Detected := Total_Errors_Detected - 1; |
| Serious_Errors_Detected := Serious_Errors_Detected - 1; |
| |
| when Non_Serious_Error => |
| Total_Errors_Detected := Total_Errors_Detected - 1; |
| end case; |
| end Decrease_Error_Msg_Count; |
| |
| ------------------ |
| -- Debug_Output -- |
| ------------------ |
| |
| procedure Debug_Output (N : Node_Id) is |
| begin |
| if Debug_Flag_1 then |
| Write_Str ("*** following error message posted on node id = #"); |
| Write_Int (Int (N)); |
| Write_Str (" ***"); |
| Write_Eol; |
| end if; |
| end Debug_Output; |
| |
| ---------- |
| -- dmsg -- |
| ---------- |
| |
| procedure dmsg (Id : Error_Msg_Id) is |
| E : Error_Msg_Object renames Errors.Table (Id); |
| |
| begin |
| w ("Dumping error message, Id = ", Int (Id)); |
| w (" Text = ", E.Text.all); |
| w (" Next = ", Int (E.Next)); |
| w (" Prev = ", Int (E.Prev)); |
| w (" Sfile = ", Int (E.Sfile)); |
| |
| Write_Str |
| (" Sptr = "); |
| Write_Location (E.Sptr.Ptr); -- ??? Do not write the full span for now |
| Write_Eol; |
| |
| Write_Str |
| (" Optr = "); |
| Write_Location (E.Optr.Ptr); |
| Write_Eol; |
| |
| Write_Str |
| (" Insertion_Sloc = "); |
| Write_Location (E.Insertion_Sloc); |
| Write_Eol; |
| |
| w (" Line = ", Int (E.Line)); |
| w (" Col = ", Int (E.Col)); |
| w (" Kind = ", E.Kind'Img); |
| w (" Warn_Err = ", E.Warn_Err'Img); |
| w (" Warn_Chr = '" & E.Warn_Chr & '''); |
| w (" Uncond = ", E.Uncond); |
| w (" Msg_Cont = ", E.Msg_Cont); |
| w (" Deleted = ", E.Deleted); |
| |
| Write_Eol; |
| end dmsg; |
| |
| ------------------ |
| -- Get_Location -- |
| ------------------ |
| |
| function Get_Location (E : Error_Msg_Id) return Source_Ptr is |
| begin |
| return Errors.Table (E).Sptr.Ptr; |
| end Get_Location; |
| |
| ---------------- |
| -- Get_Msg_Id -- |
| ---------------- |
| |
| function Get_Msg_Id return Error_Msg_Id is |
| begin |
| return Cur_Msg; |
| end Get_Msg_Id; |
| |
| ------------------------ |
| -- Get_Warning_Option -- |
| ------------------------ |
| |
| function Get_Warning_Option (Id : Error_Msg_Id) return String is |
| begin |
| return Get_Warning_Option (Errors.Table (Id)); |
| end Get_Warning_Option; |
| |
| function Get_Warning_Option (E : Error_Msg_Object) return String is |
| Is_Style : constant Boolean := E.Kind in Style; |
| Warn_Chr : constant String (1 .. 2) := E.Warn_Chr; |
| |
| begin |
| if Has_Switch_Tag (E) |
| and then Warn_Chr (1) /= '?' |
| then |
| if Warn_Chr = "$ " then |
| return "-gnatel"; |
| elsif Is_Style then |
| return "-gnaty" & Warn_Chr (1); |
| elsif Warn_Chr (2) = ' ' then |
| return "-gnatw" & Warn_Chr (1); |
| else |
| return "-gnatw" & Warn_Chr; |
| end if; |
| end if; |
| |
| return ""; |
| end Get_Warning_Option; |
| |
| --------------------- |
| -- Get_Warning_Tag -- |
| --------------------- |
| |
| function Get_Warning_Tag (Id : Error_Msg_Id) return String is |
| begin |
| return Get_Warning_Tag (Errors.Table (Id)); |
| end Get_Warning_Tag; |
| |
| function Get_Warning_Tag (E : Error_Msg_Object) return String is |
| Warn_Chr : constant String (1 .. 2) := E.Warn_Chr; |
| Option : constant String := Get_Warning_Option (E); |
| |
| begin |
| if Has_Switch_Tag (E) then |
| if Warn_Chr = "? " then |
| return "[enabled by default]"; |
| elsif Warn_Chr = "* " then |
| return "[restriction warning]"; |
| elsif Option /= "" then |
| return "[" & Option & "]"; |
| end if; |
| end if; |
| |
| return ""; |
| end Get_Warning_Tag; |
| |
| ------------------------------ |
| -- Increase_Error_Msg_Count -- |
| ------------------------------ |
| |
| procedure Increase_Error_Msg_Count (E : Error_Msg_Object) is |
| |
| begin |
| case E.Kind is |
| when Info => |
| Info_Messages := Info_Messages + 1; |
| |
| when Warning | Style => |
| Warnings_Detected := Warnings_Detected + 1; |
| |
| if E.Warn_Err /= None then |
| Warnings_Treated_As_Errors := Warnings_Treated_As_Errors + 1; |
| |
| -- Propagate Warn_Err to all of the preceeding continuation |
| -- messages and the main message. |
| |
| for J in reverse 1 .. Errors.Last loop |
| if Errors.Table (J).Warn_Err = None then |
| Errors.Table (J).Warn_Err := E.Warn_Err; |
| |
| Warnings_Treated_As_Errors := |
| Warnings_Treated_As_Errors + 1; |
| end if; |
| |
| exit when not Errors.Table (J).Msg_Cont; |
| end loop; |
| end if; |
| |
| when High_Check | Medium_Check | Low_Check => |
| Check_Messages := Check_Messages + 1; |
| |
| when Error => |
| Total_Errors_Detected := Total_Errors_Detected + 1; |
| Serious_Errors_Detected := Serious_Errors_Detected + 1; |
| |
| when Non_Serious_Error => |
| Total_Errors_Detected := Total_Errors_Detected + 1; |
| end case; |
| end Increase_Error_Msg_Count; |
| |
| -------------------------------- |
| -- Is_Redundant_Error_Message -- |
| -------------------------------- |
| |
| function Is_Redundant_Error_Message |
| (Prev_Msg : Error_Msg_Id; Cur_Msg : Error_Msg_Id) return Boolean is |
| |
| begin |
| return |
| Prev_Msg /= No_Error_Msg |
| |
| -- Error messages are posted on the same line |
| |
| and then Errors.Table (Prev_Msg).Line = Errors.Table (Cur_Msg).Line |
| and then Errors.Table (Prev_Msg).Sfile = Errors.Table (Cur_Msg).Sfile |
| |
| -- Do not consider unconditional messages to be redundant right now |
| -- They may be removed later. |
| |
| and then not Errors.Table (Cur_Msg).Uncond |
| |
| -- Do not consider continuation messages as they are removed with |
| -- their parent later on. |
| |
| and then not Errors.Table (Cur_Msg).Msg_Cont |
| |
| -- Don't delete if prev msg is warning and new msg is an error. |
| -- This is because we don't want a real error masked by a |
| -- warning. In all other cases (that is parse errors for the |
| -- same line that are not unconditional) we do delete the |
| -- message. This helps to avoid junk extra messages from |
| -- cascaded parsing errors |
| |
| and then (Errors.Table (Prev_Msg).Kind not in Warning | Style |
| or else Errors.Table (Cur_Msg).Kind in Warning | Style); |
| end Is_Redundant_Error_Message; |
| |
| -------------------- |
| -- Has_Switch_Tag -- |
| -------------------- |
| |
| function Has_Switch_Tag (Id : Error_Msg_Id) return Boolean |
| is (Has_Switch_Tag (Errors.Table (Id))); |
| |
| function Has_Switch_Tag (E_Msg : Error_Msg_Object) return Boolean is |
| begin |
| return |
| E_Msg.Kind in Warning | Info | Style and then E_Msg.Warn_Chr /= " "; |
| end Has_Switch_Tag; |
| |
| -------------------- |
| -- Next_Error_Msg -- |
| -------------------- |
| |
| procedure Next_Error_Msg (E : in out Error_Msg_Id) is |
| begin |
| loop |
| E := Errors.Table (E).Next; |
| exit when E = No_Error_Msg; |
| exit when not Errors.Table (E).Deleted |
| and then not Errors.Table (E).Msg_Cont; |
| end loop; |
| end Next_Error_Msg; |
| |
| --------------------------- |
| -- Next_Continuation_Msg -- |
| --------------------------- |
| |
| procedure Next_Continuation_Msg (E : in out Error_Msg_Id) is |
| begin |
| E := Errors.Table (E).Next; |
| |
| if E = No_Error_Msg or else not Errors.Table (E).Msg_Cont then |
| E := No_Error_Msg; |
| end if; |
| end Next_Continuation_Msg; |
| |
| ---------------------- |
| -- Primary_Location -- |
| ---------------------- |
| |
| function Primary_Location (E : Error_Msg_Object) return Labeled_Span_Id is |
| L : Labeled_Span_Id; |
| begin |
| L := E.Locations; |
| while L /= No_Labeled_Span loop |
| if Locations.Table (L).Is_Primary then |
| return L; |
| end if; |
| |
| L := Locations.Table (L).Next; |
| end loop; |
| |
| return No_Labeled_Span; |
| end Primary_Location; |
| |
| ------------------ |
| -- Get_Human_Id -- |
| ------------------ |
| |
| function Get_Human_Id (E : Error_Msg_Object) return String_Ptr is |
| begin |
| if E.Switch = No_Switch_Id then |
| return Diagnostic_Entries (E.Id).Human_Id; |
| else |
| return Get_Switch (E).Human_Id; |
| end if; |
| end Get_Human_Id; |
| |
| -------------------- |
| -- Get_Doc_Switch -- |
| -------------------- |
| |
| function Get_Doc_Switch (E : Error_Msg_Object) return String is |
| begin |
| if Warning_Doc_Switch |
| and then E.Warn_Chr /= " " |
| and then E.Kind in Info |
| | Style |
| | Warning |
| then |
| if E.Switch = No_Switch_Id then |
| if E.Warn_Chr = "* " then |
| return "[restriction warning]"; |
| |
| -- Info messages can have a switch tag but they should not have |
| -- a default switch tag. |
| |
| elsif E.Kind /= Info then |
| |
| -- For Default_Warning |
| |
| return "[enabled by default]"; |
| end if; |
| else |
| declare |
| S : constant Switch_Type := Get_Switch (E); |
| begin |
| return "[-" & S.Short_Name.all & "]"; |
| end; |
| end if; |
| end if; |
| |
| return ""; |
| end Get_Doc_Switch; |
| |
| ---------------- |
| -- Get_Switch -- |
| ---------------- |
| |
| function Get_Switch (E : Error_Msg_Object) return Switch_Type is |
| begin |
| return Get_Switch (E.Switch); |
| end Get_Switch; |
| |
| ------------------- |
| -- Get_Switch_Id -- |
| ------------------- |
| |
| function Get_Switch_Id (E : Error_Msg_Object) return Switch_Id is |
| begin |
| return Get_Switch_Id (E.Kind, E.Warn_Chr); |
| end Get_Switch_Id; |
| |
| function Get_Switch_Id |
| (Kind : Error_Msg_Type; Warn_Chr : String) return Switch_Id is |
| begin |
| if Warn_Chr = "$ " then |
| return Get_Switch_Id ("gnatel"); |
| elsif Kind in Warning | Info then |
| return Get_Switch_Id ("gnatw" & Warn_Chr); |
| elsif Kind = Style then |
| return Get_Switch_Id ("gnaty" & Warn_Chr); |
| else |
| return No_Switch_Id; |
| end if; |
| end Get_Switch_Id; |
| |
| ------------- |
| -- Matches -- |
| ------------- |
| |
| function Matches (S : String; P : String) return Boolean is |
| Slast : constant Natural := S'Last; |
| PLast : constant Natural := P'Last; |
| |
| SPtr : Natural := S'First; |
| PPtr : Natural := P'First; |
| |
| begin |
| -- Loop advancing through characters of string and pattern |
| |
| SPtr := S'First; |
| PPtr := P'First; |
| loop |
| -- Return True if pattern is a single asterisk |
| |
| if PPtr = PLast and then P (PPtr) = '*' then |
| return True; |
| |
| -- Return True if both pattern and string exhausted |
| |
| elsif PPtr > PLast and then SPtr > Slast then |
| return True; |
| |
| -- Return False, if one exhausted and not the other |
| |
| elsif PPtr > PLast or else SPtr > Slast then |
| return False; |
| |
| -- Case where pattern starts with asterisk |
| |
| elsif P (PPtr) = '*' then |
| |
| -- Try all possible starting positions in S for match with the |
| -- remaining characters of the pattern. This is the recursive |
| -- call that implements the scanner backup. |
| |
| for J in SPtr .. Slast loop |
| if Matches (S (J .. Slast), P (PPtr + 1 .. PLast)) then |
| return True; |
| end if; |
| end loop; |
| |
| return False; |
| |
| -- Dealt with end of string and *, advance if we have a match |
| |
| elsif Fold_Lower (S (SPtr)) = Fold_Lower (P (PPtr)) then |
| SPtr := SPtr + 1; |
| PPtr := PPtr + 1; |
| |
| -- If first characters do not match, that's decisive |
| |
| else |
| return False; |
| end if; |
| end loop; |
| end Matches; |
| |
| ----------------------- |
| -- Output_Error_Msgs -- |
| ----------------------- |
| |
| procedure Output_Error_Msgs (E : in out Error_Msg_Id) is |
| P : Source_Ptr; |
| T : Error_Msg_Id; |
| S : Error_Msg_Id; |
| |
| Flag_Num : Pos; |
| Mult_Flags : Boolean := False; |
| |
| begin |
| S := E; |
| |
| -- Skip deleted messages at start |
| |
| if Errors.Table (S).Deleted then |
| Set_Next_Non_Deleted_Msg (S); |
| end if; |
| |
| -- Figure out if we will place more than one error flag on this line |
| |
| T := S; |
| while T /= No_Error_Msg |
| and then Errors.Table (T).Line = Errors.Table (E).Line |
| and then Errors.Table (T).Sfile = Errors.Table (E).Sfile |
| loop |
| if Errors.Table (T).Sptr.Ptr > Errors.Table (E).Sptr.Ptr then |
| Mult_Flags := True; |
| end if; |
| |
| Set_Next_Non_Deleted_Msg (T); |
| end loop; |
| |
| -- Output the error flags. The circuit here makes sure that the tab |
| -- characters in the original line are properly accounted for. The |
| -- eight blanks at the start are to match the line number. |
| |
| if not Debug_Flag_2 then |
| Write_Str (" "); |
| P := Line_Start (Errors.Table (E).Sptr.Ptr); |
| Flag_Num := 1; |
| |
| -- Loop through error messages for this line to place flags |
| |
| T := S; |
| while T /= No_Error_Msg |
| and then Errors.Table (T).Line = Errors.Table (E).Line |
| and then Errors.Table (T).Sfile = Errors.Table (E).Sfile |
| loop |
| declare |
| Src : Source_Buffer_Ptr |
| renames Source_Text (Errors.Table (T).Sfile); |
| |
| begin |
| -- Loop to output blanks till current flag position |
| |
| while P < Errors.Table (T).Sptr.Ptr loop |
| |
| -- Horizontal tab case, just echo the tab |
| |
| if Src (P) = ASCII.HT then |
| Write_Char (ASCII.HT); |
| P := P + 1; |
| |
| -- Deal with wide character case, but don't include brackets |
| -- notation in this circuit, since we know that this will |
| -- display unencoded (no one encodes brackets notation). |
| |
| elsif Src (P) /= '[' |
| and then Is_Start_Of_Wide_Char (Src, P) |
| then |
| Skip_Wide (Src, P); |
| Write_Char (' '); |
| |
| -- Normal non-wide character case (or bracket) |
| |
| else |
| P := P + 1; |
| Write_Char (' '); |
| end if; |
| end loop; |
| |
| -- Output flag (unless already output, this happens if more |
| -- than one error message occurs at the same flag position). |
| |
| if P = Errors.Table (T).Sptr.Ptr then |
| if (Flag_Num = 1 and then not Mult_Flags) |
| or else Flag_Num > 9 |
| then |
| Write_Char ('|'); |
| else |
| Write_Char |
| (Character'Val (Character'Pos ('0') + Flag_Num)); |
| end if; |
| |
| -- Skip past the corresponding source text character |
| |
| -- Horizontal tab case, we output a flag at the tab position |
| -- so now we output a tab to match up with the text. |
| |
| if Src (P) = ASCII.HT then |
| Write_Char (ASCII.HT); |
| P := P + 1; |
| |
| -- Skip wide character other than left bracket |
| |
| elsif Src (P) /= '[' |
| and then Is_Start_Of_Wide_Char (Src, P) |
| then |
| Skip_Wide (Src, P); |
| |
| -- Skip normal non-wide character case (or bracket) |
| |
| else |
| P := P + 1; |
| end if; |
| end if; |
| end; |
| |
| Set_Next_Non_Deleted_Msg (T); |
| Flag_Num := Flag_Num + 1; |
| end loop; |
| |
| Write_Eol; |
| end if; |
| |
| -- Now output the error messages |
| |
| T := S; |
| while T /= No_Error_Msg |
| and then Errors.Table (T).Line = Errors.Table (E).Line |
| and then Errors.Table (T).Sfile = Errors.Table (E).Sfile |
| loop |
| Write_Str (" >>> "); |
| Output_Msg_Text (T); |
| |
| if Debug_Flag_2 then |
| while Column < 74 loop |
| Write_Char (' '); |
| end loop; |
| |
| Write_Str (" <<<"); |
| end if; |
| |
| Write_Eol; |
| Set_Next_Non_Deleted_Msg (T); |
| end loop; |
| |
| E := T; |
| end Output_Error_Msgs; |
| |
| ------------------------ |
| -- Output_Line_Number -- |
| ------------------------ |
| |
| procedure Output_Line_Number (L : Logical_Line_Number) is |
| D : Int; -- next digit |
| C : Character; -- next character |
| Z : Boolean; -- flag for zero suppress |
| N, M : Int; -- temporaries |
| |
| begin |
| if L = No_Line_Number then |
| Write_Str (" "); |
| |
| else |
| Z := False; |
| N := Int (L); |
| |
| M := 100_000; |
| while M /= 0 loop |
| D := Int (N / M); |
| N := N rem M; |
| M := M / 10; |
| |
| if D = 0 then |
| if Z then |
| C := '0'; |
| else |
| C := ' '; |
| end if; |
| else |
| Z := True; |
| C := Character'Val (D + 48); |
| end if; |
| |
| Write_Char (C); |
| end loop; |
| |
| Write_Str (". "); |
| end if; |
| end Output_Line_Number; |
| |
| ------------------------ |
| -- Output_Text_Within -- |
| ------------------------ |
| |
| procedure Output_Text_Within (Txt : String; Line_Length : Nat) is |
| Offs : constant Nat := Column - 1; |
| -- Offset to start of message, used for continuations |
| |
| Ptr : Natural; |
| |
| Split : Natural; |
| -- Position where a new line was inserted in the original message |
| |
| Start : Natural; |
| -- Start of the current line |
| |
| Max : Integer := Integer (Line_Length - Column + 1); |
| -- Maximum characters to output on next line |
| |
| Text_Length : constant Natural := Txt'Length; |
| -- Length of the message |
| |
| begin |
| -- Here we have to split the message up into multiple lines |
| |
| Ptr := 1; |
| loop |
| -- Make sure we do not have ludicrously small line |
| |
| Max := Integer'Max (Max, 20); |
| |
| -- If remaining text fits, output it respecting LF and we are done |
| |
| if Text_Length - Ptr < Max then |
| for J in Ptr .. Text_Length loop |
| if Txt (J) = ASCII.LF then |
| Write_Eol; |
| Write_Spaces (Offs); |
| else |
| Write_Char (Txt (J)); |
| end if; |
| end loop; |
| |
| return; |
| |
| -- Line does not fit |
| |
| else |
| Start := Ptr; |
| |
| -- First scan forward looking for a hard end of line |
| |
| for Scan in Ptr .. Ptr + Max - 1 loop |
| if Txt (Scan) = ASCII.LF then |
| Split := Scan - 1; |
| Ptr := Scan + 1; |
| goto Continue; |
| end if; |
| end loop; |
| |
| -- Otherwise scan backwards looking for a space |
| |
| for Scan in reverse Ptr .. Ptr + Max - 1 loop |
| if Txt (Scan) = ' ' then |
| Split := Scan - 1; |
| Ptr := Scan + 1; |
| goto Continue; |
| end if; |
| end loop; |
| |
| -- If we fall through, no space, so split line arbitrarily |
| |
| Split := Ptr + Max - 1; |
| Ptr := Split + 1; |
| end if; |
| |
| <<Continue>> |
| if Start <= Split then |
| Write_Line (Txt (Start .. Split)); |
| Write_Spaces (Offs); |
| end if; |
| |
| Max := Integer (Line_Length - Column + 1); |
| end loop; |
| end Output_Text_Within; |
| |
| ------------------------- |
| -- Output_Msg_Location -- |
| ------------------------- |
| |
| procedure Output_Msg_Location (E : Error_Msg_Id) is |
| E_Obj : constant Error_Msg_Object := Errors.Table (E); |
| begin |
| Write_Str (SGR_Locus); |
| |
| if Full_Path_Name_For_Brief_Errors then |
| Write_Name (Full_Ref_Name (E_Obj.Sfile)); |
| else |
| Write_Name (Reference_Name (E_Obj.Sfile)); |
| end if; |
| |
| Write_Char (':'); |
| Write_Int (Int (Physical_To_Logical (E_Obj.Line, E_Obj.Sfile))); |
| Write_Char (':'); |
| |
| if E_Obj.Col < 10 then |
| Write_Char ('0'); |
| end if; |
| |
| Write_Int (Int (E_Obj.Col)); |
| Write_Str (": "); |
| |
| Write_Str (SGR_Reset); |
| end Output_Msg_Location; |
| |
| --------------------- |
| -- Output_Msg_Text -- |
| --------------------- |
| |
| procedure Output_Msg_Text (E : Error_Msg_Id) is |
| |
| E_Msg : Error_Msg_Object renames Errors.Table (E); |
| Text : constant String_Ptr := E_Msg.Text; |
| Tag : constant String := Get_Warning_Tag (E); |
| SGR_Code : constant String := Get_SGR_Code (E_Msg); |
| Kind_Prefix : constant String := |
| (if E_Msg.Kind = Style then Style_Prefix |
| else Kind_To_String (E_Msg) & ": "); |
| Buf : Bounded_String (Max_Msg_Length); |
| Line_Length : constant Nat := |
| (if Error_Msg_Line_Length = 0 then Nat'Last |
| else Error_Msg_Line_Length); |
| |
| begin |
| -- Prefix with "error:" rather than warning. |
| -- Additionally include the style suffix when needed. |
| |
| if E_Msg.Warn_Err in From_Pragma | From_Run_Time_As_Err then |
| Append |
| (Buf, |
| SGR_Error & "error: " & SGR_Reset & |
| (if E_Msg.Kind = Style then Style_Prefix else "")); |
| |
| -- Print the message kind prefix |
| -- * Info/Style/Warning messages |
| -- * Check messages that are not continuations in the pretty printer |
| -- * Error messages when error tags are allowed |
| |
| elsif E_Msg.Kind in Info | Style | Warning |
| or else |
| (E_Msg.Kind in High_Check | Medium_Check | Low_Check |
| and then not (E_Msg.Msg_Cont and then Debug_Flag_FF)) |
| or else |
| (E_Msg.Kind in Error | Non_Serious_Error |
| and then Opt.Unique_Error_Tag) |
| then |
| Append (Buf, SGR_Code & Kind_Prefix & SGR_Reset); |
| end if; |
| |
| Append (Buf, Text.all); |
| |
| -- Postfix warning tag to message if needed |
| |
| if Tag /= "" and then Warning_Doc_Switch then |
| Append (Buf, ' ' & Tag); |
| end if; |
| |
| -- Postfix [warning-as-error] at the end |
| |
| if E_Msg.Warn_Err = From_Pragma then |
| Append (Buf, " " & Warn_As_Err_Tag); |
| end if; |
| |
| Output_Text_Within (To_String (Buf), Line_Length); |
| end Output_Msg_Text; |
| |
| --------------------- |
| -- Prescan_Message -- |
| --------------------- |
| |
| procedure Prescan_Message (Msg : String) is |
| J : Natural; |
| |
| function Parse_Message_Class return String; |
| -- Convert the warning insertion sequence to a warning class represented |
| -- as a length-two string padded, if necessary, with spaces. |
| -- Return the Message class and set the iterator J to the character |
| -- following the sequence. |
| -- Raise a Program_Error if the insertion sequence is not valid. |
| |
| ------------------------- |
| -- Parse_Message_Class -- |
| ------------------------- |
| |
| function Parse_Message_Class return String is |
| C : constant Character := Msg (J - 1); |
| Message_Class : String (1 .. 2) := " "; |
| begin |
| if J <= Msg'Last and then Msg (J) = C then |
| Message_Class := "? "; |
| J := J + 1; |
| |
| elsif J < Msg'Last and then Msg (J + 1) = C |
| and then Msg (J) in 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '*' | '$' |
| then |
| Message_Class := Msg (J) & " "; |
| J := J + 2; |
| |
| elsif J + 1 < Msg'Last and then Msg (J + 2) = C |
| and then Msg (J) in '.' | '_' |
| and then Msg (J + 1) in 'a' .. 'z' |
| then |
| Message_Class := Msg (J .. J + 1); |
| J := J + 3; |
| elsif (J < Msg'Last and then Msg (J + 1) = C) or else |
| (J + 1 < Msg'Last and then Msg (J + 2) = C) |
| then |
| raise Program_Error; |
| end if; |
| |
| -- In any other cases, this is not a warning insertion sequence |
| -- and the default " " value is returned. |
| |
| return Message_Class; |
| end Parse_Message_Class; |
| |
| -- Start of processing for Prescan_Message |
| |
| begin |
| -- Continuation lines need to check only for insertion sequences. |
| -- Other attributes should be inherited from the main message. |
| |
| if Msg (Msg'First) = '\' then |
| Has_Insertion_Line := False; |
| |
| J := Msg'First; |
| |
| -- If we have a quote, don't look at following character |
| |
| while J <= Msg'Last loop |
| if Msg (J) = ''' then |
| J := J + 2; |
| |
| -- Insertion line (# insertion) |
| |
| elsif Msg (J) = '#' then |
| Has_Insertion_Line := True; |
| J := J + 1; |
| else |
| J := J + 1; |
| end if; |
| end loop; |
| |
| return; |
| |
| -- Some global variables are not set for continuation messages, as they |
| -- only make sense for the initial message. |
| |
| else |
| |
| -- Set initial values of globals (may be changed during scan) |
| |
| Error_Msg_Kind := Error; |
| Is_Unconditional_Msg := False; |
| Is_Runtime_Raise_Msg := False; |
| Warning_Msg_Char := " "; |
| |
| -- Check style message |
| |
| if Msg'Length > Style_Prefix'Length |
| and then |
| Msg (Msg'First .. Msg'First + Style_Prefix'Length - 1) = |
| Style_Prefix |
| then |
| Error_Msg_Kind := Style; |
| |
| -- Check info message |
| |
| elsif Msg'Length > Info_Prefix'Length |
| and then |
| Msg (Msg'First .. Msg'First + Info_Prefix'Length - 1) = |
| Info_Prefix |
| then |
| Error_Msg_Kind := Info; |
| |
| -- Check high check message |
| |
| elsif Msg'Length > High_Prefix'Length |
| and then |
| Msg (Msg'First .. Msg'First + High_Prefix'Length - 1) = |
| High_Prefix |
| then |
| Error_Msg_Kind := High_Check; |
| |
| -- Check medium check message |
| |
| elsif Msg'Length > Medium_Prefix'Length |
| and then |
| Msg (Msg'First .. Msg'First + Medium_Prefix'Length - 1) = |
| Medium_Prefix |
| then |
| Error_Msg_Kind := Medium_Check; |
| |
| -- Check low check message |
| |
| elsif Msg'Length > Low_Prefix'Length |
| and then |
| Msg (Msg'First .. Msg'First + Low_Prefix'Length - 1) = |
| Low_Prefix |
| then |
| Error_Msg_Kind := Low_Check; |
| end if; |
| end if; |
| |
| Has_Double_Exclam := False; |
| Has_Error_Code := False; |
| Has_Insertion_Line := False; |
| |
| -- Loop through message looking for relevant insertion sequences |
| |
| J := Msg'First; |
| while J <= Msg'Last loop |
| |
| -- If we have a quote, don't look at following character |
| |
| if Msg (J) = ''' then |
| J := J + 2; |
| |
| -- Warning message (? or < insertion sequence) |
| |
| elsif Msg (J) = '?' or else Msg (J) = '<' then |
| if Msg (J) = '?' or else Error_Msg_Warn then |
| |
| -- Consider Info and Style messages as unique message types. |
| -- Those messages can have warning insertion characters within |
| -- them. However they should only be switch specific insertion |
| -- characters and not the generic ? or ?? warning insertion |
| -- characters. |
| |
| if Error_Msg_Kind not in Style | Info then |
| Error_Msg_Kind := Warning; |
| end if; |
| |
| J := J + 1; |
| Warning_Msg_Char := Parse_Message_Class; |
| |
| -- Bomb if untagged warning message. This code can be |
| -- uncommented for debugging when looking for untagged warning |
| -- messages. |
| |
| -- pragma Assert (Warning_Msg_Char /= " "); |
| |
| else |
| J := J + 1; |
| end if; |
| |
| -- Unconditional message (! insertion) |
| |
| elsif Msg (J) = '!' then |
| Is_Unconditional_Msg := True; |
| J := J + 1; |
| |
| if J <= Msg'Last and then Msg (J) = '!' then |
| Has_Double_Exclam := True; |
| J := J + 1; |
| end if; |
| |
| -- Insertion line (# insertion) |
| |
| elsif Msg (J) = '#' then |
| Has_Insertion_Line := True; |
| J := J + 1; |
| |
| -- Non-serious error (| insertion) |
| |
| elsif Msg (J) = '|' then |
| Error_Msg_Kind := Non_Serious_Error; |
| J := J + 1; |
| |
| -- Error code ([] insertion) |
| |
| elsif Msg (J) = '[' |
| and then J < Msg'Last |
| and then Msg (J + 1) = ']' |
| then |
| Has_Error_Code := True; |
| J := J + 2; |
| |
| else |
| J := J + 1; |
| end if; |
| end loop; |
| end Prescan_Message; |
| |
| -------------------- |
| -- Purge_Messages -- |
| -------------------- |
| |
| procedure Purge_Messages (From : Source_Ptr; To : Source_Ptr) is |
| E : Error_Msg_Id; |
| |
| function To_Be_Purged (E : Error_Msg_Id) return Boolean; |
| -- Returns True for a message that is to be purged. Also adjusts |
| -- error counts appropriately. |
| |
| ------------------ |
| -- To_Be_Purged -- |
| ------------------ |
| |
| function To_Be_Purged (E : Error_Msg_Id) return Boolean is |
| begin |
| if E /= No_Error_Msg |
| and then Errors.Table (E).Sptr.Ptr > From |
| and then Errors.Table (E).Sptr.Ptr < To |
| then |
| Decrease_Error_Msg_Count (Errors.Table (E)); |
| |
| return True; |
| |
| else |
| return False; |
| end if; |
| end To_Be_Purged; |
| |
| -- Start of processing for Purge_Messages |
| |
| begin |
| while To_Be_Purged (First_Error_Msg) loop |
| First_Error_Msg := Errors.Table (First_Error_Msg).Next; |
| end loop; |
| |
| E := First_Error_Msg; |
| while E /= No_Error_Msg loop |
| while To_Be_Purged (Errors.Table (E).Next) loop |
| Errors.Table (Errors.Table (E).Next).Deleted := True; |
| |
| Errors.Table (E).Next := |
| Errors.Table (Errors.Table (E).Next).Next; |
| end loop; |
| |
| E := Errors.Table (E).Next; |
| end loop; |
| end Purge_Messages; |
| |
| ---------------- |
| -- Same_Error -- |
| ---------------- |
| |
| function Same_Error (M1, M2 : Error_Msg_Id) return Boolean is |
| Msg1 : constant String_Ptr := Errors.Table (M1).Text; |
| Msg2 : constant String_Ptr := Errors.Table (M2).Text; |
| |
| Msg2_Len : constant Integer := Msg2'Length; |
| Msg1_Len : constant Integer := Msg1'Length; |
| |
| begin |
| return |
| Msg1.all = Msg2.all |
| or else |
| (Msg1_Len - 10 > Msg2_Len |
| and then |
| Msg2.all = Msg1.all (1 .. Msg2_Len) |
| and then |
| Msg1 (Msg2_Len + 1 .. Msg2_Len + 10) = ", instance") |
| or else |
| (Msg2_Len - 10 > Msg1_Len |
| and then |
| Msg1.all = Msg2.all (1 .. Msg1_Len) |
| and then |
| Msg2 (Msg1_Len + 1 .. Msg1_Len + 10) = ", instance"); |
| end Same_Error; |
| |
| ------------------- |
| -- Set_Msg_Blank -- |
| ------------------- |
| |
| procedure Set_Msg_Blank is |
| begin |
| if Msglen > 0 |
| and then Msg_Buffer (Msglen) /= ' ' |
| and then Msg_Buffer (Msglen) /= '(' |
| and then Msg_Buffer (Msglen) /= '-' |
| and then not Manual_Quote_Mode |
| then |
| Set_Msg_Char (' '); |
| end if; |
| end Set_Msg_Blank; |
| |
| ------------------------------- |
| -- Set_Msg_Blank_Conditional -- |
| ------------------------------- |
| |
| procedure Set_Msg_Blank_Conditional is |
| begin |
| if Msglen > 0 |
| and then Msg_Buffer (Msglen) /= ' ' |
| and then Msg_Buffer (Msglen) /= '(' |
| and then Msg_Buffer (Msglen) /= '"' |
| and then not Manual_Quote_Mode |
| then |
| Set_Msg_Char (' '); |
| end if; |
| end Set_Msg_Blank_Conditional; |
| |
| ------------------ |
| -- Set_Msg_Char -- |
| ------------------ |
| |
| procedure Set_Msg_Char (C : Character) is |
| begin |
| -- The check for message buffer overflow is needed to deal with cases |
| -- where insertions get too long (in particular a child unit name can |
| -- be very long). |
| |
| if Msglen < Max_Msg_Length then |
| Msglen := Msglen + 1; |
| Msg_Buffer (Msglen) := C; |
| end if; |
| end Set_Msg_Char; |
| |
| ---------------------------- |
| -- Set_Msg_Insertion_Code -- |
| ---------------------------- |
| |
| procedure Set_Msg_Insertion_Code is |
| H : constant array (Nat range 0 .. 9) of Character := "0123456789"; |
| P10 : constant array (Natural range 0 .. 3) of Nat := |
| (10 ** 0, |
| 10 ** 1, |
| 10 ** 2, |
| 10 ** 3); |
| |
| Code_Len : constant Natural := |
| (case Error_Msg_Code is |
| when 0 => 0, |
| when 1 .. 9 => 1, |
| when 10 .. 99 => 2, |
| when 100 .. 999 => 3, |
| when 1000 .. 9999 => 4); |
| Code_Rest : Nat := Error_Msg_Code; |
| Code_Digit : Nat; |
| |
| begin |
| Set_Msg_Char ('E'); |
| |
| for J in 1 .. Error_Msg_Code_Digits - Code_Len loop |
| Set_Msg_Char ('0'); |
| end loop; |
| |
| for J in 1 .. Code_Len loop |
| Code_Digit := Code_Rest / P10 (Code_Len - J); |
| Set_Msg_Char (H (Code_Digit)); |
| Code_Rest := Code_Rest - Code_Digit * P10 (Code_Len - J); |
| end loop; |
| end Set_Msg_Insertion_Code; |
| |
| ------------------------------ |
| -- Set_Msg_Insertion_Column -- |
| ------------------------------ |
| |
| procedure Set_Msg_Insertion_Column is |
| begin |
| if RM_Column_Check then |
| Set_Msg_Str (" in column "); |
| Set_Msg_Int (Int (Error_Msg_Col) + 1); |
| end if; |
| end Set_Msg_Insertion_Column; |
| |
| --------------------------------- |
| -- Set_Msg_Insertion_File_Name -- |
| --------------------------------- |
| |
| procedure Set_Msg_Insertion_File_Name is |
| begin |
| if Error_Msg_File_1 = No_File then |
| null; |
| |
| elsif Error_Msg_File_1 = Error_File_Name then |
| Set_Msg_Blank; |
| Set_Msg_Str ("<error>"); |
| |
| else |
| Set_Msg_Blank; |
| Get_Name_String (Error_Msg_File_1); |
| Set_Msg_Quote; |
| Set_Msg_Name_Buffer; |
| Set_Msg_Quote; |
| end if; |
| |
| -- The following assignments ensure that the second and third { |
| -- insertion characters will correspond to the Error_Msg_File_2 |
| -- and Error_Msg_File_3 values. |
| |
| Error_Msg_File_1 := Error_Msg_File_2; |
| Error_Msg_File_2 := Error_Msg_File_3; |
| end Set_Msg_Insertion_File_Name; |
| |
| ----------------------------------- |
| -- Set_Msg_Insertion_Line_Number -- |
| ----------------------------------- |
| |
| procedure Set_Msg_Insertion_Line_Number (Loc, Flag : Source_Ptr) is |
| Sindex_Loc : Source_File_Index; |
| Sindex_Flag : Source_File_Index; |
| Fname : File_Name_Type; |
| Int_File : Boolean; |
| |
| procedure Set_At; |
| -- Outputs "at " unless last characters in buffer are " from ". Certain |
| -- messages read better with from than at. |
| |
| ------------ |
| -- Set_At -- |
| ------------ |
| |
| procedure Set_At is |
| begin |
| if Msglen < 6 |
| or else Msg_Buffer (Msglen - 5 .. Msglen) /= " from " |
| then |
| Set_Msg_Str ("at "); |
| end if; |
| end Set_At; |
| |
| -- Start of processing for Set_Msg_Insertion_Line_Number |
| |
| begin |
| Set_Msg_Blank; |
| |
| if Loc = No_Location then |
| Set_At; |
| Set_Msg_Str ("unknown location"); |
| |
| elsif Loc = System_Location then |
| Set_Msg_Str ("in package System"); |
| Set_Msg_Insertion_Run_Time_Name; |
| |
| elsif Loc = Standard_Location then |
| Set_Msg_Str ("in package Standard"); |
| |
| elsif Loc = Standard_ASCII_Location then |
| Set_Msg_Str ("in package Standard.ASCII"); |
| |
| else |
| -- Add "at file-name:" if reference is to other than the source |
| -- file in which the error message is placed. Note that we check |
| -- full file names, rather than just the source indexes, to |
| -- deal with generic instantiations from the current file. |
| |
| Sindex_Loc := Get_Source_File_Index (Loc); |
| Sindex_Flag := Get_Source_File_Index (Flag); |
| |
| if Full_File_Name (Sindex_Loc) /= Full_File_Name (Sindex_Flag) then |
| Set_At; |
| Fname := Reference_Name (Get_Source_File_Index (Loc)); |
| Int_File := Is_Internal_File_Name (Fname); |
| Get_Name_String (Fname); |
| Set_Msg_Name_Buffer; |
| |
| if not (Int_File and Debug_Flag_Dot_K) then |
| Set_Msg_Char (':'); |
| Set_Msg_Int (Int (Get_Logical_Line_Number (Loc))); |
| end if; |
| |
| -- If in current file, add text "at line " |
| |
| else |
| Set_At; |
| Set_Msg_Str ("line "); |
| Set_Msg_Int (Int (Get_Logical_Line_Number (Loc))); |
| end if; |
| |
| -- Deal with the instantiation case. We may have a reference to, |
| -- e.g. a type, that is declared within a generic template, and |
| -- what we are really referring to is the occurrence in an instance. |
| -- In this case, the line number of the instantiation is also of |
| -- interest, and we add a notation: |
| |
| -- , instance at xxx |
| |
| -- where xxx is a line number output using this same routine (and |
| -- the recursion can go further if the instantiation is itself in |
| -- a generic template). |
| |
| -- The flag location passed to us in this situation is indeed the |
| -- line number within the template, but as described in Sinput.L |
| -- (file sinput-l.ads, section "Handling Generic Instantiations") |
| -- we can retrieve the location of the instantiation itself from |
| -- this flag location value. |
| |
| -- Note: this processing is suppressed if Suppress_Instance_Location |
| -- is set True. This is used to prevent redundant annotations of the |
| -- location of the instantiation in the case where we are placing |
| -- the messages on the instantiation in any case. |
| |
| if Instantiation (Sindex_Loc) /= No_Location |
| and then not Suppress_Instance_Location |
| then |
| Set_Msg_Str (", instance "); |
| Set_Msg_Insertion_Line_Number (Instantiation (Sindex_Loc), Flag); |
| end if; |
| end if; |
| end Set_Msg_Insertion_Line_Number; |
| |
| ---------------------------- |
| -- Set_Msg_Insertion_Name -- |
| ---------------------------- |
| |
| procedure Set_Msg_Insertion_Name is |
| begin |
| if Error_Msg_Name_1 = No_Name then |
| null; |
| |
| elsif Error_Msg_Name_1 = Error_Name then |
| Set_Msg_Blank; |
| Set_Msg_Str ("<error>"); |
| |
| else |
| Set_Msg_Blank_Conditional; |
| Get_Unqualified_Decoded_Name_String (Error_Msg_Name_1); |
| |
| -- Remove %s or %b at end. These come from unit names. If the |
| -- caller wanted the (unit) or (body), then they would have used |
| -- the $ insertion character. Certainly no error message should |
| -- ever have %b or %s explicitly occurring. |
| |
| if Name_Len > 2 |
| and then Name_Buffer (Name_Len - 1) = '%' |
| and then (Name_Buffer (Name_Len) = 'b' |
| or else |
| Name_Buffer (Name_Len) = 's') |
| then |
| Name_Len := Name_Len - 2; |
| end if; |
| |
| -- Remove upper case letter at end, again, we should not be getting |
| -- such names, and what we hope is that the remainder makes sense. |
| |
| if Name_Len > 1 and then Name_Buffer (Name_Len) in 'A' .. 'Z' then |
| Name_Len := Name_Len - 1; |
| end if; |
| |
| -- If operator name or character literal name, just print it as is. |
| -- Also print as is if it ends in a right paren (case of x'val(nnn)). |
| |
| if Name_Buffer (1) = '"' |
| or else Name_Buffer (1) = ''' |
| or else Name_Buffer (Name_Len) = ')' |
| then |
| Set_Msg_Name_Buffer; |
| |
| -- Else output with surrounding quotes in proper casing mode |
| |
| else |
| Set_Casing (Identifier_Casing (Flag_Source)); |
| Set_Msg_Quote; |
| Set_Msg_Name_Buffer; |
| Set_Msg_Quote; |
| end if; |
| end if; |
| |
| -- The following assignments ensure that other percent insertion |
| -- characters will correspond to their appropriate Error_Msg_Name_# |
| -- values as required. |
| |
| Error_Msg_Name_1 := Error_Msg_Name_2; |
| Error_Msg_Name_2 := Error_Msg_Name_3; |
| Error_Msg_Name_3 := Error_Msg_Name_4; |
| Error_Msg_Name_4 := Error_Msg_Name_5; |
| Error_Msg_Name_5 := Error_Msg_Name_6; |
| end Set_Msg_Insertion_Name; |
| |
| ------------------------------------ |
| -- Set_Msg_Insertion_Name_Literal -- |
| ------------------------------------ |
| |
| procedure Set_Msg_Insertion_Name_Literal is |
| begin |
| if Error_Msg_Name_1 = No_Name then |
| null; |
| |
| elsif Error_Msg_Name_1 = Error_Name then |
| Set_Msg_Blank; |
| Set_Msg_Str ("<error>"); |
| |
| else |
| Set_Msg_Blank; |
| Get_Name_String (Error_Msg_Name_1); |
| Set_Msg_Quote; |
| Set_Msg_Name_Buffer; |
| Set_Msg_Quote; |
| end if; |
| |
| -- The following assignments ensure that other percent insertion |
| -- characters will correspond to their appropriate Error_Msg_Name_# |
| -- values as required. |
| |
| Error_Msg_Name_1 := Error_Msg_Name_2; |
| Error_Msg_Name_2 := Error_Msg_Name_3; |
| Error_Msg_Name_3 := Error_Msg_Name_4; |
| Error_Msg_Name_4 := Error_Msg_Name_5; |
| Error_Msg_Name_5 := Error_Msg_Name_6; |
| end Set_Msg_Insertion_Name_Literal; |
| |
| ------------------------------------- |
| -- Set_Msg_Insertion_Reserved_Name -- |
| ------------------------------------- |
| |
| procedure Set_Msg_Insertion_Reserved_Name is |
| begin |
| Set_Msg_Blank_Conditional; |
| Get_Name_String (Error_Msg_Name_1); |
| Set_Msg_Quote; |
| Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case); |
| Set_Msg_Name_Buffer; |
| Set_Msg_Quote; |
| end Set_Msg_Insertion_Reserved_Name; |
| |
| ------------------------------------- |
| -- Set_Msg_Insertion_Reserved_Word -- |
| ------------------------------------- |
| |
| procedure Set_Msg_Insertion_Reserved_Word |
| (Text : String; |
| J : in out Integer) |
| is |
| begin |
| Set_Msg_Blank_Conditional; |
| Name_Len := 0; |
| |
| while J <= Text'Last and then Text (J) in 'A' .. 'Z' loop |
| Add_Char_To_Name_Buffer (Text (J)); |
| J := J + 1; |
| end loop; |
| |
| -- Here is where we make the special exception for RM |
| |
| if Name_Len = 2 and then Name_Buffer (1 .. 2) = "RM" then |
| Set_Msg_Name_Buffer; |
| |
| -- We make a similar exception for CUDA |
| |
| elsif Name_Len = 4 and then Name_Buffer (1 .. 4) = "CUDA" then |
| Set_Msg_Name_Buffer; |
| |
| -- We make a similar exception for SPARK |
| |
| elsif Name_Len = 5 and then Name_Buffer (1 .. 5) = "SPARK" then |
| Set_Msg_Name_Buffer; |
| |
| -- Otherwise, case appropriately and add surrounding quotes |
| |
| else |
| Set_Casing (Keyword_Casing (Flag_Source), All_Lower_Case); |
| Set_Msg_Quote; |
| Set_Msg_Name_Buffer; |
| Set_Msg_Quote; |
| end if; |
| end Set_Msg_Insertion_Reserved_Word; |
| |
| ------------------------------------- |
| -- Set_Msg_Insertion_Run_Time_Name -- |
| ------------------------------------- |
| |
| procedure Set_Msg_Insertion_Run_Time_Name is |
| begin |
| if Targparm.Run_Time_Name_On_Target /= No_Name then |
| Set_Msg_Blank_Conditional; |
| Set_Msg_Char ('('); |
| Get_Name_String (Targparm.Run_Time_Name_On_Target); |
| Set_Casing (Mixed_Case); |
| Set_Msg_Str (Name_Buffer (1 .. Name_Len)); |
| Set_Msg_Char (')'); |
| end if; |
| end Set_Msg_Insertion_Run_Time_Name; |
| |
| ---------------------------- |
| -- Set_Msg_Insertion_Uint -- |
| ---------------------------- |
| |
| procedure Set_Msg_Insertion_Uint is |
| begin |
| Set_Msg_Blank; |
| UI_Image (Error_Msg_Uint_1); |
| |
| for J in 1 .. UI_Image_Length loop |
| Set_Msg_Char (UI_Image_Buffer (J)); |
| end loop; |
| |
| -- The following assignment ensures that a second caret insertion |
| -- character will correspond to the Error_Msg_Uint_2 parameter. |
| |
| Error_Msg_Uint_1 := Error_Msg_Uint_2; |
| end Set_Msg_Insertion_Uint; |
| |
| ----------------- |
| -- Set_Msg_Int -- |
| ----------------- |
| |
| procedure Set_Msg_Int (Line : Int) is |
| begin |
| if Line > 9 then |
| Set_Msg_Int (Line / 10); |
| end if; |
| |
| Set_Msg_Char (Character'Val (Character'Pos ('0') + (Line rem 10))); |
| end Set_Msg_Int; |
| |
| ------------------------- |
| -- Set_Msg_Name_Buffer -- |
| ------------------------- |
| |
| procedure Set_Msg_Name_Buffer is |
| begin |
| Set_Msg_Str (Name_Buffer (1 .. Name_Len)); |
| Destroy_Global_Name_Buffer; |
| end Set_Msg_Name_Buffer; |
| |
| ------------------- |
| -- Set_Msg_Quote -- |
| ------------------- |
| |
| procedure Set_Msg_Quote is |
| begin |
| if not Manual_Quote_Mode then |
| Set_Msg_Char ('"'); |
| end if; |
| end Set_Msg_Quote; |
| |
| ----------------- |
| -- Set_Msg_Str -- |
| ----------------- |
| |
| procedure Set_Msg_Str (Text : String) is |
| begin |
| -- Do replacement for special x'Class aspect names |
| |
| if Text = "_Pre" then |
| Set_Msg_Str ("Pre'Class"); |
| |
| elsif Text = "_Post" then |
| Set_Msg_Str ("Post'Class"); |
| |
| elsif Text = "_Type_Invariant" then |
| Set_Msg_Str ("Type_Invariant'Class"); |
| |
| elsif Text = "_pre" then |
| Set_Msg_Str ("pre'class"); |
| |
| elsif Text = "_post" then |
| Set_Msg_Str ("post'class"); |
| |
| elsif Text = "_type_invariant" then |
| Set_Msg_Str ("type_invariant'class"); |
| |
| elsif Text = "_PRE" then |
| Set_Msg_Str ("PRE'CLASS"); |
| |
| elsif Text = "_POST" then |
| Set_Msg_Str ("POST'CLASS"); |
| |
| elsif Text = "_TYPE_INVARIANT" then |
| Set_Msg_Str ("TYPE_INVARIANT'CLASS"); |
| |
| -- Preserve casing for names that include acronyms |
| |
| elsif Text = "Cpp_Class" then |
| Set_Msg_Str ("CPP_Class"); |
| |
| elsif Text = "Cpp_Constructor" then |
| Set_Msg_Str ("CPP_Constructor"); |
| |
| elsif Text = "Cpp_Virtual" then |
| Set_Msg_Str ("CPP_Virtual"); |
| |
| elsif Text = "Cpp_Vtable" then |
| Set_Msg_Str ("CPP_Vtable"); |
| |
| elsif Text = "Cuda_Device" then |
| Set_Msg_Str ("CUDA_Device"); |
| |
| elsif Text = "Cuda_Global" then |
| Set_Msg_Str ("CUDA_Global"); |
| |
| elsif Text = "Persistent_Bss" then |
| Set_Msg_Str ("Persistent_BSS"); |
| |
| elsif Text = "Spark_Mode" then |
| Set_Msg_Str ("SPARK_Mode"); |
| |
| elsif Text = "Use_Vads_Size" then |
| Set_Msg_Str ("Use_VADS_Size"); |
| |
| elsif Text = "Vads_Size" then |
| Set_Msg_Str ("VADS_size"); |
| |
| -- Normal case with no replacement |
| |
| else |
| for J in Text'Range loop |
| Set_Msg_Char (Text (J)); |
| end loop; |
| end if; |
| end Set_Msg_Str; |
| |
| ------------------------------ |
| -- Set_Next_Non_Deleted_Msg -- |
| ------------------------------ |
| |
| procedure Set_Next_Non_Deleted_Msg (E : in out Error_Msg_Id) is |
| begin |
| if E = No_Error_Msg then |
| return; |
| |
| else |
| loop |
| E := Errors.Table (E).Next; |
| exit when E = No_Error_Msg or else not Errors.Table (E).Deleted; |
| end loop; |
| end if; |
| end Set_Next_Non_Deleted_Msg; |
| |
| ------------------------------ |
| -- Set_Specific_Warning_Off -- |
| ------------------------------ |
| |
| procedure Set_Specific_Warning_Off |
| (Node : Node_Id; |
| Msg : String; |
| Reason : String_Id; |
| Config : Boolean; |
| Used : Boolean := False) |
| is |
| Loc : constant Source_Ptr := Sinfo.Nodes.Sloc (Node); |
| begin |
| Specific_Warnings.Append |
| ((Start => Loc, |
| Msg => new String'(Msg), |
| Stop => Source_Last (Get_Source_File_Index (Loc)), |
| Reason => Reason, |
| Open => True, |
| Used => Used, |
| Config => Config)); |
| end Set_Specific_Warning_Off; |
| |
| ----------------------------- |
| -- Set_Specific_Warning_On -- |
| ----------------------------- |
| |
| procedure Set_Specific_Warning_On |
| (Loc : Source_Ptr; |
| Msg : String; |
| Err : out Boolean) |
| is |
| begin |
| for J in 1 .. Specific_Warnings.Last loop |
| declare |
| SWE : Specific_Warning_Entry renames Specific_Warnings.Table (J); |
| |
| begin |
| if Msg = SWE.Msg.all |
| and then Loc > SWE.Start |
| and then SWE.Open |
| and then Get_Source_File_Index (SWE.Start) = |
| Get_Source_File_Index (Loc) |
| then |
| SWE.Stop := Loc; |
| SWE.Open := False; |
| Err := False; |
| |
| -- If a config pragma is specifically cancelled, consider |
| -- that it is no longer active as a configuration pragma. |
| |
| SWE.Config := False; |
| return; |
| end if; |
| end; |
| end loop; |
| |
| Err := True; |
| end Set_Specific_Warning_On; |
| |
| --------------------------- |
| -- Set_Warnings_Mode_Off -- |
| --------------------------- |
| |
| procedure Set_Warnings_Mode_Off (Loc : Source_Ptr; Reason : String_Id) is |
| begin |
| -- Don't bother with entries from instantiation copies, since we will |
| -- already have a copy in the template, which is what matters. |
| |
| if Instantiation (Get_Source_File_Index (Loc)) /= No_Location then |
| return; |
| end if; |
| |
| -- If all warnings are suppressed by command line switch, this can |
| -- be ignored, unless we are in GNATprove_Mode which requires pragma |
| -- Warnings to be stored for the formal verification backend. |
| |
| if Warning_Mode = Suppress |
| and then not GNATprove_Mode |
| then |
| return; |
| end if; |
| |
| -- If last entry in table already covers us, this is a redundant pragma |
| -- Warnings (Off) and can be ignored. |
| |
| if Warnings.Last >= Warnings.First |
| and then Warnings.Table (Warnings.Last).Start <= Loc |
| and then Loc <= Warnings.Table (Warnings.Last).Stop |
| then |
| return; |
| end if; |
| |
| -- If none of those special conditions holds, establish a new entry, |
| -- extending from the location of the pragma to the end of the current |
| -- source file. This ending point will be adjusted by a subsequent |
| -- corresponding pragma Warnings (On). |
| |
| Warnings.Append |
| ((Start => Loc, |
| Stop => Source_Last (Get_Source_File_Index (Loc)), |
| Reason => Reason)); |
| end Set_Warnings_Mode_Off; |
| |
| -------------------------- |
| -- Set_Warnings_Mode_On -- |
| -------------------------- |
| |
| procedure Set_Warnings_Mode_On (Loc : Source_Ptr) is |
| begin |
| -- Don't bother with entries from instantiation copies, since we will |
| -- already have a copy in the template, which is what matters. |
| |
| if Instantiation (Get_Source_File_Index (Loc)) /= No_Location then |
| return; |
| end if; |
| |
| -- If all warnings are suppressed by command line switch, this can |
| -- be ignored, unless we are in GNATprove_Mode which requires pragma |
| -- Warnings to be stored for the formal verification backend. |
| |
| if Warning_Mode = Suppress |
| and then not GNATprove_Mode |
| then |
| return; |
| end if; |
| |
| -- If the last entry in the warnings table covers this pragma, then |
| -- we adjust the end point appropriately. |
| |
| if Warnings.Last >= Warnings.First |
| and then Warnings.Table (Warnings.Last).Start <= Loc |
| and then Loc <= Warnings.Table (Warnings.Last).Stop |
| then |
| Warnings.Table (Warnings.Last).Stop := Loc; |
| end if; |
| end Set_Warnings_Mode_On; |
| |
| ------------------- |
| -- Sloc_In_Range -- |
| ------------------- |
| |
| function Sloc_In_Range (Loc, Start, Stop : Source_Ptr) return Boolean is |
| Cur_Loc : Source_Ptr := Loc; |
| |
| begin |
| while Cur_Loc /= No_Location loop |
| if Start <= Cur_Loc and then Cur_Loc <= Stop then |
| return True; |
| end if; |
| |
| Cur_Loc := Instantiation_Location (Cur_Loc); |
| end loop; |
| |
| return False; |
| end Sloc_In_Range; |
| |
| ------------------------------------- |
| -- Warning_Specifically_Suppressed -- |
| ------------------------------------- |
| |
| function Warning_Specifically_Suppressed |
| (Loc : Source_Ptr; |
| Msg : String_Ptr; |
| Tag : String := "") return String_Id |
| is |
| begin |
| -- Loop through specific warning suppression entries |
| |
| for J in Specific_Warnings.First .. Specific_Warnings.Last loop |
| declare |
| SWE : Specific_Warning_Entry renames Specific_Warnings.Table (J); |
| begin |
| -- Pragma applies if it is a configuration pragma, or if the |
| -- location is in range of a specific non-configuration pragma. |
| |
| if SWE.Config |
| or else Sloc_In_Range (Loc, SWE.Start, SWE.Stop) |
| then |
| if Matches (Msg.all, SWE.Msg.all) |
| or else Matches (Tag, SWE.Msg.all) |
| then |
| SWE.Used := True; |
| return SWE.Reason; |
| end if; |
| end if; |
| end; |
| end loop; |
| |
| return No_String; |
| end Warning_Specifically_Suppressed; |
| |
| ------------------------------ |
| -- Warning_Treated_As_Error -- |
| ------------------------------ |
| |
| function Warning_Treated_As_Error (Msg : String) return Boolean is |
| begin |
| for J in 1 .. Warnings_As_Errors_Count loop |
| if Matches (Msg, Warnings_As_Errors (J).all) then |
| return True; |
| end if; |
| end loop; |
| |
| return False; |
| end Warning_Treated_As_Error; |
| |
| function Warning_Treated_As_Error (E : Error_Msg_Object) return Boolean is |
| |
| begin |
| return |
| Warning_Treated_As_Error (E.Text.all) |
| or else Warning_Treated_As_Error (Get_Warning_Tag (E)); |
| end Warning_Treated_As_Error; |
| |
| ------------------------- |
| -- Warnings_Suppressed -- |
| ------------------------- |
| |
| function Warnings_Suppressed (Loc : Source_Ptr) return String_Id is |
| begin |
| -- Loop through table of ON/OFF warnings |
| |
| for J in Warnings.First .. Warnings.Last loop |
| if Sloc_In_Range (Loc, Warnings.Table (J).Start, |
| Warnings.Table (J).Stop) |
| then |
| return Warnings.Table (J).Reason; |
| end if; |
| end loop; |
| |
| if Warning_Mode = Suppress then |
| return Null_String_Id; |
| else |
| return No_String; |
| end if; |
| end Warnings_Suppressed; |
| |
| ------------------------- |
| -- Write_Error_Summary -- |
| ------------------------- |
| |
| procedure Write_Error_Summary is |
| begin |
| -- Extra blank line if error messages or source listing were output |
| |
| if Total_Errors_Detected + Warnings_Detected > 0 or else Full_List |
| then |
| Write_Eol; |
| end if; |
| |
| -- Message giving number of lines read and number of errors detected. |
| -- This normally goes to Standard_Output. The exception is when brief |
| -- mode is not set, verbose mode (or full list mode) is set, and |
| -- there are errors. In this case we send the message to standard |
| -- error to make sure that *something* appears on standard error |
| -- in an error situation. |
| |
| if Total_Errors_Detected + Warnings_Detected /= 0 |
| and then not Brief_Output |
| and then (Verbose_Mode or Full_List) |
| then |
| Set_Standard_Error; |
| end if; |
| |
| -- Message giving total number of lines. Don't give this message if |
| -- the Main_Source line is unknown (this happens in error situations, |
| -- e.g. when integrated preprocessing fails). |
| |
| if Main_Source_File > No_Source_File then |
| Write_Str (" "); |
| Write_Int (Num_Source_Lines (Main_Source_File)); |
| |
| if Num_Source_Lines (Main_Source_File) = 1 then |
| Write_Str (" line: "); |
| else |
| Write_Str (" lines: "); |
| end if; |
| end if; |
| |
| if Total_Errors_Detected = 0 then |
| Write_Str ("No errors"); |
| |
| elsif Total_Errors_Detected = 1 then |
| Write_Str ("1 error"); |
| |
| else |
| Write_Int (Total_Errors_Detected); |
| Write_Str (" errors"); |
| end if; |
| |
| if Warnings_Detected > 0 then |
| Write_Str (", "); |
| Write_Int (Warnings_Detected); |
| Write_Str (" warning"); |
| |
| if Warnings_Detected > 1 then |
| Write_Char ('s'); |
| end if; |
| |
| if Warnings_Treated_As_Errors > 0 then |
| Write_Str (" ("); |
| |
| if Warnings_Treated_As_Errors /= Warnings_Detected then |
| Write_Int (Warnings_Treated_As_Errors); |
| Write_Str (" "); |
| end if; |
| |
| Write_Str ("treated as error"); |
| |
| if Warnings_Treated_As_Errors > 1 then |
| Write_Str ("s"); |
| end if; |
| |
| Write_Str (")"); |
| end if; |
| end if; |
| |
| if Info_Messages /= 0 then |
| Write_Str (", "); |
| Write_Int (Info_Messages); |
| Write_Str (" info message"); |
| |
| if Info_Messages > 1 then |
| Write_Char ('s'); |
| end if; |
| end if; |
| |
| Write_Eol; |
| Set_Standard_Output; |
| end Write_Error_Summary; |
| |
| end Erroutc; |