diff options
Diffstat (limited to 'gcc/ada/erroutc-pretty_emitter.adb')
-rw-r--r-- | gcc/ada/erroutc-pretty_emitter.adb | 1410 |
1 files changed, 1410 insertions, 0 deletions
diff --git a/gcc/ada/erroutc-pretty_emitter.adb b/gcc/ada/erroutc-pretty_emitter.adb new file mode 100644 index 0000000..d9bf560 --- /dev/null +++ b/gcc/ada/erroutc-pretty_emitter.adb @@ -0,0 +1,1410 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT COMPILER COMPONENTS -- +-- -- +-- D I A G N O S T I C S . P R E T T Y _ E M I T T E R -- +-- -- +-- 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. -- +-- -- +------------------------------------------------------------------------------ + +with Namet; use Namet; +with Opt; use Opt; +with Output; use Output; +with Sinput; use Sinput; +with GNAT.Lists; use GNAT.Lists; + +package body Erroutc.Pretty_Emitter is + + REGION_OFFSET : constant := 1; + -- Number of characters between the line bar and the region span + + REGION_ARM_SIZE : constant := 2; + -- Number of characters on the region span arms + -- e.g. two for this case: + -- +-- + -- | + -- +-- + -- ^^ + + REGION_SIZE : constant := REGION_OFFSET + 1 + REGION_ARM_SIZE; + -- The total number of characters taken up by the region span characters + + MAX_BAR_POS : constant := 7; + -- The maximum position of the line bar from the start of the line + + procedure Destroy (Elem : in out Labeled_Span_Type); + pragma Inline (Destroy); + + procedure Destroy (Elem : in out Labeled_Span_Type) is + begin + -- Diagnostic elements will be freed when all the diagnostics have been + -- emitted. + null; + end Destroy; + + package Labeled_Span_Lists is new Doubly_Linked_Lists + (Element_Type => Labeled_Span_Type, + "=" => "=", + Destroy_Element => Destroy, + Check_Tampering => False); + subtype Labeled_Span_List is Labeled_Span_Lists.Doubly_Linked_List; + + type Printable_Line is record + First : Source_Ptr; + -- The first character of the line + + Last : Source_Ptr; + -- The last character of the line + + Line_Nr : Pos; + -- The line number + + Spans : Labeled_Span_List; + -- The spans applied on the line + end record; + + procedure Destroy (Elem : in out Printable_Line); + pragma Inline (Destroy); + + function Equals (L, R : Printable_Line) return Boolean is + (L.Line_Nr = R.Line_Nr); + + package Lines_Lists is new Doubly_Linked_Lists + (Element_Type => Printable_Line, + "=" => Equals, + Destroy_Element => Destroy, + Check_Tampering => False); + + subtype Lines_List is Lines_Lists.Doubly_Linked_List; + + type File_Sections is record + File : String_Ptr; + -- Name of the file + + Ptr : Source_Ptr; + -- Pointer to the Primary location in the file section that is printed + -- at the start of the file section. If there are none then the first + -- location in the section. + + Lines : Lines_List; + -- Lines to be printed for the file + end record; + + procedure Destroy (Elem : in out File_Sections); + pragma Inline (Destroy); + + function Equals (L, R : File_Sections) return Boolean is + (L.File /= null and then R.File /= null and then L.File.all = R.File.all); + + package File_Section_Lists is new Doubly_Linked_Lists + (Element_Type => File_Sections, + "=" => Equals, + Destroy_Element => Destroy, + Check_Tampering => False); + + subtype File_Section_List is File_Section_Lists.Doubly_Linked_List; + + function Create_File_Sections + (Locations : Labeled_Span_Id) return File_Section_List; + -- Create a list of file sections from the labeled spans that are to be + -- printed. + -- + -- Each file section contains a list of lines that are to be printed for + -- the file and the spans that are applied to each of those lines. + + procedure Create_File_Section + (Sections : in out File_Section_List; Loc : Labeled_Span_Type); + -- Create a new file section for the given labeled span. + + procedure Add_Printable_Line + (Lines : Lines_List; Loc : Labeled_Span_Type; S_Ptr : Source_Ptr); + + procedure Create_Printable_Line + (Lines : Lines_List; Loc : Labeled_Span_Type; S_Ptr : Source_Ptr); + -- Create a new printable line for the given labeled span and add it in the + -- correct position to the Lines list based on the line number. + + function Get_Region_Span + (Spans : Labeled_Span_List) return Labeled_Span_Type; + + function Has_Multiple_Labeled_Spans (L : Printable_Line) return Boolean; + + procedure Write_Region_Delimiter (SGR_Code : String); + -- Write the arms signifying the start and end of a region span + -- e.g. +-- + + procedure Write_Region_Bar (SGR_Code : String); + -- Write the bar signifying the continuation of a region span + -- e.g. | + + procedure Write_Region_Continuation (SGR_Code : String); + -- Write the continuation signifying the continuation of a region span + -- e.g. : + + procedure Write_Region_Offset; + -- Write a number of whitespaces equal to the size of the region span + + function Trimmed_Image (I : Natural) return String; + -- Removes the leading whitespace from the 'Image of a Natural number. + + procedure Write_Span_Labels + (Loc : Labeled_Span_Type; + L : Printable_Line; + Line_Size : Integer; + Idx : String; + Within_Region_Span : Boolean; + SGR_Code : String; + Region_Span_SGR_Code : String); + + procedure Write_File_Section + (Sec : File_Sections; + Write_File_Name : Boolean; + File_Name_Offset : Integer; + Include_Spans : Boolean; + SGR_Code : String := SGR_Note); + -- Prints the labled spans for a given File_Section. + -- + -- --> <File_Section.File_Name> + -- <Labeled_Spans inside the file> + + procedure Write_Labeled_Spans + (Locations : Labeled_Span_Id; + Write_File_Name : Boolean; + File_Name_Offset : Integer; + Include_Spans : Boolean := True; + SGR_Code : String := SGR_Note); + -- Pretty-prints all of the code regions indicated by the Locations. The + -- labeled spans in the Locations are grouped by file into File_Sections + -- and sorted by the file name of the Primary location followed by all + -- other locations sorted alphabetically. + + procedure Write_Intersecting_Labels + (Intersecting_Labels : Labeled_Span_List; SGR_Code : String); + -- Prints the indices and their associated labels of intersecting labels. + -- + -- Labeled spans that are insercting on the same line are printed without + -- labels. Instead the span pointer is replaced by an index number and in + -- the end all of the indices are printed with their associated labels. + -- + -- + -- 42 | [for I in V1.First_Index .. V1.Last_Index => V1(I), -6]; + -- | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + -- | 1- + -- | 2------------------------------------------- + -- | 1: positional element + -- | 2: named element + + function Get_Line_End + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr; + -- Get the source location for the end of the line (LF) in Buf for Loc. If + -- Loc is past the end of Buf already, return Buf'Last. + + function Get_Line_Start + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr; + -- Get the source location for the start of the line in Buf for Loc + + function Get_First_Line_Char + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr; + -- Get first non-space character in the line containing Loc + + function Get_Last_Line_Char + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr; + -- Get last non line end [LF, CR] character in the line containing Loc + + function Image (X : Positive; Width : Positive) return String; + -- Output number X over Width characters, with whitespace padding. + -- Only output the low-order Width digits of X, if X is larger than + -- Width digits. + + procedure Write_Buffer + (Buf : Source_Buffer_Ptr; First : Source_Ptr; Last : Source_Ptr); + -- Output the characters from First to Last position in Buf, using + -- Write_Buffer_Char. + + procedure Write_Buffer_Char (Buf : Source_Buffer_Ptr; Loc : Source_Ptr); + -- Output the characters at position Loc in Buf, translating ASCII.HT + -- in a suitable number of spaces so that the output is not modified + -- by starting in a different column that 1. + + procedure Write_Line_Marker (Num : Pos; Width : Positive); + -- Attempts to write the line number within Width number of whitespaces + -- followed by a bar ':' symbol. + -- + -- e.g ' 12 |' + -- + -- This is usually used on source code lines that are marked by a span. + + procedure Write_Empty_Bar_Line (Width : Integer); + -- Writes Width number of whitespaces and a bar '|' symbol. + -- + -- e.g ' |' + -- + -- This is usually used on lines where the label is going to printed. + + procedure Write_Empty_Skip_Line (Width : Integer); + -- Writes Width number of whitespaces and a bar ':' symbol. + -- + -- e.g ' :' + -- + -- This is usually used between non-continous source lines that neec to be + -- printed. + + procedure Write_Error_Msg_Line (E_Msg : Error_Msg_Object); + -- Write the error message line for the given diagnostic: + -- + -- '['<Diag.Id>']' <Diag.Kind>: <Diag.Message> ['['<Diag.Switch>']'] + + function Should_Write_File_Name + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object) return Boolean; + -- If the sub-diagnostic and the main diagnostic only point to the same + -- file then there is no reason to add the file name to the sub-diagnostic. + + function Should_Write_Spans + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object) return Boolean; + -- Old sub-diagnostics used to have the same location as the main + -- diagnostic in order to group them correctly. However in most cases + -- it was not meant to point to a location but rather add an additional + -- message to the original diagnostic. + -- + -- If the sub-diagnostic and the main diagnostic have the same location + -- then we should avoid printing the spans. + + procedure Print_Diagnostic (E : Error_Msg_Id); + -- Entry point for printing a primary diagnostic message. + + procedure Print_Edit (Edit : Edit_Type; Offset : Integer); + -- Prints an edit object as follows: + -- + -- --> <File_Name> + -- -<Line_Nr> <Old_Line> + -- +<Line_Nr> <New_Line> + + procedure Print_Fix (Fix : Fix_Type; Offset : Integer); + -- Prints a fix object as follows + -- + -- + Fix: <Fix.Description> + -- <Fix.Edits> + + procedure Print_Sub_Diagnostic + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object; Offset : Integer); + + function To_String (Sptr : Source_Ptr) return String; + -- Convert the source pointer to a string of the form: "file:line:column" + + function To_File_Name (Sptr : Source_Ptr) return String; + -- Converts the file name of the Sptr to a string. + + function Line_To_String (Sptr : Source_Ptr) return String; + -- Converts the logical line number of the Sptr to a string. + + function Column_To_String (Sptr : Source_Ptr) return String; + -- Converts the column number of the Sptr to a string. Column values less + -- than 10 are prefixed with a 0. + + ------------- + -- Destroy -- + ------------- + + procedure Destroy (Elem : in out Printable_Line) is + begin + Labeled_Span_Lists.Destroy (Elem.Spans); + end Destroy; + + ------------- + -- Destroy -- + ------------- + + procedure Destroy (Elem : in out File_Sections) is + begin + Free (Elem.File); + Lines_Lists.Destroy (Elem.Lines); + end Destroy; + + ------------------ + -- Get_Line_End -- + ------------------ + + function Get_Line_End + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr + is + Cur_Loc : Source_Ptr := Source_Ptr'Min (Loc, Buf'Last); + begin + while Cur_Loc < Buf'Last and then Buf (Cur_Loc) /= ASCII.LF loop + Cur_Loc := Cur_Loc + 1; + end loop; + + return Cur_Loc; + end Get_Line_End; + + -------------------- + -- Get_Line_Start -- + -------------------- + + function Get_Line_Start + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr + is + Cur_Loc : Source_Ptr := Loc; + begin + while Cur_Loc > Buf'First and then Buf (Cur_Loc - 1) /= ASCII.LF loop + Cur_Loc := Cur_Loc - 1; + end loop; + + return Cur_Loc; + end Get_Line_Start; + + ------------------------- + -- Get_First_Line_Char -- + ------------------------- + + function Get_First_Line_Char + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr + is + Cur_Loc : Source_Ptr := Get_Line_Start (Buf, Loc); + begin + while Cur_Loc < Buf'Last and then Buf (Cur_Loc) = ' ' loop + Cur_Loc := Cur_Loc + 1; + end loop; + + return Cur_Loc; + end Get_First_Line_Char; + + ------------------------ + -- Get_Last_Line_Char -- + ------------------------ + + function Get_Last_Line_Char + (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) return Source_Ptr + is + Cur_Loc : Source_Ptr := Get_Line_End (Buf, Loc); + begin + while Cur_Loc > Buf'First + and then Buf (Cur_Loc) in ASCII.LF | ASCII.CR + loop + Cur_Loc := Cur_Loc - 1; + end loop; + + return Cur_Loc; + end Get_Last_Line_Char; + + ----------- + -- Image -- + ----------- + + function Image (X : Positive; Width : Positive) return String is + Str : String (1 .. Width); + Curr : Natural := X; + begin + for J in reverse 1 .. Width loop + if Curr > 0 then + Str (J) := Character'Val (Character'Pos ('0') + Curr mod 10); + Curr := Curr / 10; + else + Str (J) := ' '; + end if; + end loop; + + return Str; + end Image; + + -------------------------------- + -- Has_Multiple_Labeled_Spans -- + -------------------------------- + + function Has_Multiple_Labeled_Spans (L : Printable_Line) return Boolean is + Count : Natural := 0; + + Loc : Labeled_Span_Type; + Loc_It : Labeled_Span_Lists.Iterator := + Labeled_Span_Lists.Iterate (L.Spans); + begin + while Labeled_Span_Lists.Has_Next (Loc_It) loop + Labeled_Span_Lists.Next (Loc_It, Loc); + if Loc.Label /= null then + Count := Count + 1; + end if; + end loop; + + return Count > 1; + end Has_Multiple_Labeled_Spans; + + --------------------- + -- Get_Region_Span -- + --------------------- + + function Get_Region_Span + (Spans : Labeled_Span_List) return Labeled_Span_Type + is + Loc : Labeled_Span_Type; + Loc_It : Labeled_Span_Lists.Iterator := + Labeled_Span_Lists.Iterate (Spans); + begin + while Labeled_Span_Lists.Has_Next (Loc_It) loop + Labeled_Span_Lists.Next (Loc_It, Loc); + + if Loc.Is_Region then + return Loc; + end if; + end loop; + + return No_Labeled_Span_Object; + end Get_Region_Span; + + ------------------ + -- Write_Buffer -- + ------------------ + + procedure Write_Buffer + (Buf : Source_Buffer_Ptr; First : Source_Ptr; Last : Source_Ptr) + is + begin + for Loc in First .. Last loop + Write_Buffer_Char (Buf, Loc); + end loop; + end Write_Buffer; + + ----------------------- + -- Write_Buffer_Char -- + ----------------------- + + procedure Write_Buffer_Char (Buf : Source_Buffer_Ptr; Loc : Source_Ptr) is + begin + -- If the character ASCII.HT is not the last one in the file, + -- output as many spaces as the character represents in the + -- original source file. + + if Buf (Loc) = ASCII.HT and then Loc < Buf'Last then + for X in Get_Column_Number (Loc) .. Get_Column_Number (Loc + 1) - 1 + loop + Write_Char (' '); + end loop; + + -- Otherwise output the character itself + + else + Write_Char (Buf (Loc)); + end if; + end Write_Buffer_Char; + + ----------------------- + -- Write_Line_Marker -- + ----------------------- + + procedure Write_Line_Marker (Num : Pos; Width : Positive) is + begin + Write_Str (Image (Positive (Num), Width => Width - 2)); + Write_Str (" |"); + end Write_Line_Marker; + + -------------------------- + -- Write_Empty_Bar_Line -- + -------------------------- + + procedure Write_Empty_Bar_Line (Width : Integer) is + + begin + Write_Str (String'(1 .. Width - 1 => ' ')); + Write_Str ("|"); + end Write_Empty_Bar_Line; + + --------------------------- + -- Write_Empty_Skip_Line -- + --------------------------- + + procedure Write_Empty_Skip_Line (Width : Integer) is + + begin + Write_Str (String'(1 .. Width - 1 => ' ')); + Write_Str (":"); + end Write_Empty_Skip_Line; + + ---------------------------- + -- Write_Region_Delimiter -- + ---------------------------- + + procedure Write_Region_Delimiter (SGR_Code : String) is + + begin + Write_Str (String'(1 .. REGION_OFFSET => ' ')); + Write_Str (SGR_Code); + Write_Str ("+"); + Write_Str (String'(1 .. REGION_ARM_SIZE => '-')); + Write_Str (SGR_Reset); + end Write_Region_Delimiter; + + ---------------------- + -- Write_Region_Bar -- + ---------------------- + + procedure Write_Region_Bar (SGR_Code : String) is + + begin + Write_Str (String'(1 .. REGION_OFFSET => ' ')); + Write_Str (SGR_Code); + Write_Str ("|"); + Write_Str (SGR_Reset); + Write_Str (String'(1 .. REGION_ARM_SIZE => ' ')); + end Write_Region_Bar; + + ------------------------------- + -- Write_Region_Continuation -- + ------------------------------- + + procedure Write_Region_Continuation (SGR_Code : String) is + + begin + Write_Str (String'(1 .. REGION_OFFSET => ' ')); + Write_Str (SGR_Code); + Write_Str (":"); + Write_Str (SGR_Reset); + Write_Str (String'(1 .. REGION_ARM_SIZE => ' ')); + end Write_Region_Continuation; + + ------------------------- + -- Write_Region_Offset -- + ------------------------- + + procedure Write_Region_Offset is + + begin + Write_Str (String'(1 .. REGION_SIZE => ' ')); + end Write_Region_Offset; + + ------------------------ + -- Add_Printable_Line -- + ------------------------ + + procedure Add_Printable_Line + (Lines : Lines_List; + Loc : Labeled_Span_Type; + S_Ptr : Source_Ptr) + is + L : Printable_Line; + L_It : Lines_Lists.Iterator; + + Line_Ptr : constant Pos := Pos (Get_Physical_Line_Number (S_Ptr)); + Line_Found : Boolean := False; + begin + L_It := Lines_Lists.Iterate (Lines); + while Lines_Lists.Has_Next (L_It) loop + Lines_Lists.Next (L_It, L); + + if not Line_Found and then L.Line_Nr = Line_Ptr then + if not Labeled_Span_Lists.Contains (L.Spans, Loc) then + Labeled_Span_Lists.Append (L.Spans, Loc); + end if; + Line_Found := True; + end if; + end loop; + + if not Line_Found then + Create_Printable_Line (Lines, Loc, S_Ptr); + end if; + end Add_Printable_Line; + + --------------------------- + -- Create_Printable_Line -- + --------------------------- + + procedure Create_Printable_Line + (Lines : Lines_List; Loc : Labeled_Span_Type; S_Ptr : Source_Ptr) + is + Spans : constant Labeled_Span_List := Labeled_Span_Lists.Create; + + Buf : constant Source_Buffer_Ptr := + Source_Text (Get_Source_File_Index (S_Ptr)); + + Line_Nr : constant Pos := Pos (Get_Physical_Line_Number (S_Ptr)); + + New_Line : constant Printable_Line := + (First => Get_Line_Start (Buf, S_Ptr), + Last => Get_Line_End (Buf, S_Ptr), + Line_Nr => Line_Nr, + Spans => Spans); + + L : Printable_Line; + L_It : Lines_Lists.Iterator := Lines_Lists.Iterate (Lines); + + Found_Greater_Line : Boolean := False; + Insert_Before_Line : Printable_Line; + begin + Labeled_Span_Lists.Append (Spans, Loc); + + -- Insert the new line based on the line number + + while Lines_Lists.Has_Next (L_It) loop + Lines_Lists.Next (L_It, L); + + if not Found_Greater_Line and then L.Line_Nr > New_Line.Line_Nr then + Found_Greater_Line := True; + Insert_Before_Line := L; + + Lines_Lists.Insert_Before (Lines, Insert_Before_Line, New_Line); + end if; + end loop; + + -- Insert after all the lines have been iterated over to avoid the + -- mutation lock in GNAT.Lists. + + if not Found_Greater_Line then + Lines_Lists.Append (Lines, New_Line); + end if; + end Create_Printable_Line; + + ------------------------- + -- Create_File_Section -- + ------------------------- + + procedure Create_File_Section + (Sections : in out File_Section_List; Loc : Labeled_Span_Type) + is + Lines : constant Lines_List := Lines_Lists.Create; + + -- Carret positions + Ptr : constant Source_Ptr := Loc.Span.Ptr; + Line_Ptr : constant Pos := Pos (Get_Physical_Line_Number (Ptr)); + + -- Span start positions + Fst : constant Source_Ptr := Loc.Span.First; + Line_Fst : constant Pos := Pos (Get_Physical_Line_Number (Fst)); + + -- Span end positions + Lst : constant Source_Ptr := Loc.Span.Last; + Line_Lst : constant Pos := Pos (Get_Physical_Line_Number (Lst)); + begin + Create_Printable_Line (Lines, Loc, Fst); + + if Line_Fst /= Line_Ptr then + Create_Printable_Line (Lines, Loc, Ptr); + end if; + + if Line_Ptr /= Line_Lst then + Create_Printable_Line (Lines, Loc, Lst); + end if; + + File_Section_Lists.Append + (Sections, + (File => new String'(To_File_Name (Loc.Span.Ptr)), + Ptr => Loc.Span.Ptr, + Lines => Lines)); + end Create_File_Section; + + -------------------------- + -- Create_File_Sections -- + -------------------------- + + function Create_File_Sections + (Locations : Labeled_Span_Id) return File_Section_List + is + Loc : Labeled_Span_Type; + Loc_It : Labeled_Span_Id := Locations; + + Sections : File_Section_List := File_Section_Lists.Create; + + Sec : File_Sections; + F_It : File_Section_Lists.Iterator; + + File_Found : Boolean; + begin + while Loc_It /= No_Labeled_Span loop + Loc := Erroutc.Locations.Table (Loc_It); + + File_Found := False; + F_It := File_Section_Lists.Iterate (Sections); + + while File_Section_Lists.Has_Next (F_It) loop + File_Section_Lists.Next (F_It, Sec); + + if Sec.File /= null + and then Sec.File.all = To_File_Name (Loc.Span.Ptr) + then + File_Found := True; + + Add_Printable_Line (Sec.Lines, Loc, Loc.Span.First); + Add_Printable_Line (Sec.Lines, Loc, Loc.Span.Ptr); + Add_Printable_Line (Sec.Lines, Loc, Loc.Span.Last); + + if Loc.Is_Primary then + Sec.Ptr := Loc.Span.Ptr; + end if; + end if; + end loop; + + if not File_Found then + Create_File_Section (Sections, Loc); + end if; + + Loc_It := Loc.Next; + end loop; + + return Sections; + end Create_File_Sections; + + ----------------------- + -- Write_Span_Labels -- + ----------------------- + + procedure Write_Span_Labels + (Loc : Labeled_Span_Type; + L : Printable_Line; + Line_Size : Integer; + Idx : String; + Within_Region_Span : Boolean; + SGR_Code : String; + Region_Span_SGR_Code : String) + is + Span_Char : constant Character := (if Loc.Is_Primary then '~' else '-'); + + Buf : constant Source_Buffer_Ptr := + Source_Text (Get_Source_File_Index (L.First)); + + Col_L_Fst : constant Natural := + Natural (Get_Column_Number (Get_First_Line_Char (Buf, L.First))); + Col_L_Lst : constant Natural := + Natural (Get_Column_Number (Get_Last_Line_Char (Buf, L.Last))); + + -- Carret positions + Ptr : constant Source_Ptr := Loc.Span.Ptr; + Line_Ptr : constant Pos := Pos (Get_Physical_Line_Number (Ptr)); + Col_Ptr : constant Natural := Natural (Get_Column_Number (Ptr)); + + -- Span start positions + Fst : constant Source_Ptr := Loc.Span.First; + Line_Fst : constant Pos := Pos (Get_Physical_Line_Number (Fst)); + Col_Fst : constant Natural := Natural (Get_Column_Number (Fst)); + + -- Span end positions + Lst : constant Source_Ptr := Loc.Span.Last; + Line_Lst : constant Pos := Pos (Get_Physical_Line_Number (Lst)); + Col_Lst : constant Natural := Natural (Get_Column_Number (Lst)); + + -- Attributes for the span on the current line + + Span_Sym : constant String := (if Idx = "" then "^" else Idx); + + Span_Fst : constant Natural := + (if Line_Fst = L.Line_Nr then Col_Fst else Col_L_Fst); + + Span_Lst : constant Natural := + (if Line_Lst = L.Line_Nr then Col_Lst else Col_L_Lst); + + Span_Ptr_Fst : constant Natural := + (if Line_Ptr = L.Line_Nr then Col_Ptr else Col_L_Fst); + + Span_Ptr_Lst : constant Natural := + (if Line_Ptr = L.Line_Nr then Span_Ptr_Fst + Span_Sym'Length + else Span_Fst); + + begin + if not Loc.Is_Region then + Write_Empty_Bar_Line (Line_Size); + + if Within_Region_Span then + Write_Region_Bar (Region_Span_SGR_Code); + else + Write_Region_Offset; + end if; + + Write_Str (String'(1 .. Span_Fst - 1 => ' ')); + + Write_Str (SGR_Code); + + if Line_Ptr = L.Line_Nr then + Write_Str (String'(Span_Fst .. Col_Ptr - 1 => Span_Char)); + Write_Str (Span_Sym); + end if; + + Write_Str (String'(Span_Ptr_Lst .. Span_Lst => Span_Char)); + + Write_Str (SGR_Reset); + + Write_Eol; + + -- Write the label under the line unless it is an intersecting span. + -- In this case omit the label which will be printed later along with + -- the index. + + if Loc.Label /= null and then Idx = "" then + Write_Empty_Bar_Line (Line_Size); + + if Within_Region_Span then + Write_Region_Bar (Region_Span_SGR_Code); + else + Write_Region_Offset; + end if; + + Write_Str (String'(1 .. Span_Fst - 1 => ' ')); + Write_Str (SGR_Code); + Write_Str (Loc.Label.all); + Write_Str (SGR_Reset); + Write_Eol; + end if; + else + if Line_Lst = L.Line_Nr then + Write_Empty_Bar_Line (Line_Size); + Write_Str (String'(1 .. REGION_OFFSET => ' ')); + Write_Str (SGR_Code); + Write_Str (Loc.Label.all); + Write_Str (SGR_Reset); + Write_Eol; + end if; + end if; + end Write_Span_Labels; + + ------------------- + -- Trimmed_Image -- + ------------------- + + function Trimmed_Image (I : Natural) return String is + Img_Raw : constant String := Natural'Image (I); + begin + return Img_Raw (Img_Raw'First + 1 .. Img_Raw'Last); + end Trimmed_Image; + + ------------------------------- + -- Write_Intersecting_Labels -- + ------------------------------- + + procedure Write_Intersecting_Labels + (Intersecting_Labels : Labeled_Span_List; SGR_Code : String) + is + L : Labeled_Span_Type; + L_It : Labeled_Span_Lists.Iterator := + Labeled_Span_Lists.Iterate (Intersecting_Labels); + Idx : Integer := 0; + begin + while Labeled_Span_Lists.Has_Next (L_It) loop + Labeled_Span_Lists.Next (L_It, L); + Idx := Idx + 1; + + Write_Empty_Bar_Line (MAX_BAR_POS); + Write_Str (" "); + Write_Str ((if L.Is_Primary then SGR_Code else SGR_Note)); + Write_Int (Int (Idx)); + Write_Str (": "); + Write_Str (L.Label.all); + Write_Str (SGR_Reset); + Write_Eol; + end loop; + end Write_Intersecting_Labels; + + ------------------------ + -- Write_File_Section -- + ------------------------ + + procedure Write_File_Section + (Sec : File_Sections; Write_File_Name : Boolean; + File_Name_Offset : Integer; Include_Spans : Boolean; + SGR_Code : String := SGR_Note) + is + use Lines_Lists; + + function Get_SGR_Code (L : Labeled_Span_Type) return String is + (if L.Is_Primary then SGR_Code else SGR_Note); + + L : Printable_Line; + L_It : Iterator := Iterate (Sec.Lines); + + Multiple_Labeled_Spans : Boolean := False; + + Idx : Integer := 0; + + Intersecting_Labels : constant Labeled_Span_List := + Labeled_Span_Lists.Create; + + Prev_Line_Nr : Natural := 0; + + Within_Region_Span : Boolean := False; + begin + if Write_File_Name then + + -- offset the file start location for sub-diagnostics + + Write_Str (String'(1 .. File_Name_Offset => ' ')); + Write_Str ("--> " & To_String (Sec.Ptr)); + Write_Eol; + end if; + + -- Historically SPARK does not include spans in their info messages. + + if not Include_Spans then + return; + end if; + + while Has_Next (L_It) loop + Next (L_It, L); + declare + Line_Nr : constant Pos := L.Line_Nr; + Line_Str : constant String := Trimmed_Image (Natural (Line_Nr)); + + Line_Size : constant Integer := + Integer'Max (Line_Str'Length, MAX_BAR_POS); + + Loc : Labeled_Span_Type; + Loc_It : Labeled_Span_Lists.Iterator := + Labeled_Span_Lists.Iterate (L.Spans); + + Buf : constant Source_Buffer_Ptr := + Source_Text (Get_Source_File_Index (L.First)); + + Region_Span : constant Labeled_Span_Type := + Get_Region_Span (L.Spans); + + Contains_Region_Span_Start : constant Boolean := + Region_Span /= No_Labeled_Span_Object + and then Line_Nr = + Pos (Get_Physical_Line_Number (Region_Span.Span.First)); + Contains_Region_Span_End : constant Boolean := + Region_Span /= No_Labeled_Span_Object + and then Line_Nr = + Pos (Get_Physical_Line_Number (Region_Span.Span.Last)); + + Region_Span_Color : constant String := + (if Region_Span /= No_Labeled_Span_Object then + Get_SGR_Code (Region_Span) + else SGR_Note); + begin + if not Multiple_Labeled_Spans then + Multiple_Labeled_Spans := Has_Multiple_Labeled_Spans (L); + end if; + + -- Write an empty line with the continuation symbol if the line + -- numbers are not contiguous + + if Prev_Line_Nr /= 0 and then Pos (Prev_Line_Nr + 1) /= Line_Nr + then + Write_Empty_Skip_Line (Line_Size); + + if Within_Region_Span then + Write_Region_Continuation (Region_Span_Color); + end if; + + Write_Eol; + end if; + + if Contains_Region_Span_Start then + Within_Region_Span := True; + end if; + + Write_Line_Marker (Line_Nr, Line_Size); + + -- Write either the region span symbol or the same number of + -- whitespaces. + + if Contains_Region_Span_Start or Contains_Region_Span_End then + Write_Region_Delimiter (Region_Span_Color); + elsif Within_Region_Span then + Write_Region_Bar (Region_Span_Color); + else + Write_Region_Offset; + end if; + + -- Write the line itself + + Write_Buffer (Buf => Buf, First => L.First, Last => L.Last); + + -- Write all the spans for the line + + while Labeled_Span_Lists.Has_Next (Loc_It) loop + Labeled_Span_Lists.Next (Loc_It, Loc); + + if Multiple_Labeled_Spans and then Loc.Label /= null then + + -- Collect all the spans with labels to print them at the + -- end. + + Labeled_Span_Lists.Append (Intersecting_Labels, Loc); + + Idx := Idx + 1; + + Write_Span_Labels + (Loc => Loc, + L => L, + Line_Size => Line_Size, + Idx => Trimmed_Image (Idx), + Within_Region_Span => Within_Region_Span, + SGR_Code => Get_SGR_Code (Loc), + Region_Span_SGR_Code => Region_Span_Color); + else + Write_Span_Labels + (Loc => Loc, + L => L, + Line_Size => Line_Size, + Idx => "", + Within_Region_Span => Within_Region_Span, + SGR_Code => Get_SGR_Code (Loc), + Region_Span_SGR_Code => Region_Span_Color); + end if; + + end loop; + + if Contains_Region_Span_End then + Within_Region_Span := False; + end if; + + Prev_Line_Nr := Natural (Line_Nr); + end; + end loop; + + Write_Intersecting_Labels (Intersecting_Labels, SGR_Code); + end Write_File_Section; + + ------------------------- + -- Write_Labeled_Spans -- + ------------------------- + + procedure Write_Labeled_Spans + (Locations : Labeled_Span_Id; + Write_File_Name : Boolean; + File_Name_Offset : Integer; + Include_Spans : Boolean := True; + SGR_Code : String := SGR_Note) + is + Sections : File_Section_List := Create_File_Sections (Locations); + + Sec : File_Sections; + F_It : File_Section_Lists.Iterator := + File_Section_Lists.Iterate (Sections); + begin + while File_Section_Lists.Has_Next (F_It) loop + File_Section_Lists.Next (F_It, Sec); + + Write_File_Section + (Sec => Sec, + Write_File_Name => Write_File_Name, + File_Name_Offset => File_Name_Offset, + Include_Spans => Include_Spans, + SGR_Code => SGR_Code); + end loop; + + File_Section_Lists.Destroy (Sections); + end Write_Labeled_Spans; + + -------------------------- + -- Write_Error_Msg_Line -- + -------------------------- + + procedure Write_Error_Msg_Line (E_Msg : Error_Msg_Object) is + Switch_Str : constant String := Get_Doc_Switch (E_Msg); + + SGR_Code : constant String := Get_SGR_Code (E_Msg); + begin + Write_Str (SGR_Code); + + if not GNATprove_Mode or else E_Msg.Id /= No_Diagnostic_Id then + Write_Str ("[" & To_String (E_Msg.Id) & "]"); + end if; + + Write_Str (" " & Kind_To_String (E_Msg) & ": "); + + Write_Str (SGR_Reset); + + Write_Str (E_Msg.Text.all); + + if Switch_Str /= "" then + Write_Str (" " & Switch_Str); + end if; + + if E_Msg.Warn_Err = From_Pragma then + Write_Str (" " & Warn_As_Err_Tag); + end if; + + Write_Eol; + end Write_Error_Msg_Line; + + ---------------------------- + -- Should_Write_File_Name -- + ---------------------------- + + function Should_Write_File_Name + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object) return Boolean + is + Sub_Loc : constant Labeled_Span_Type := + Locations.Table (Primary_Location (Sub_Diag)); + + Diag_Loc : constant Labeled_Span_Type := + Locations.Table (Primary_Location (Diag)); + + function Has_Multiple_Files (Diag : Error_Msg_Object) return Boolean; + + ------------------------ + -- Has_Multiple_Files -- + ------------------------ + + function Has_Multiple_Files (Diag : Error_Msg_Object) return Boolean is + First : constant Labeled_Span_Type := + Locations.Table (Diag.Locations); + + File : constant String := To_File_Name (First.Span.Ptr); + + Loc_Id : Labeled_Span_Id := Diag.Locations; + Loc : Labeled_Span_Type; + begin + Loc_Id := Diag.Locations; + while Loc_Id /= No_Labeled_Span loop + Loc := Locations.Table (Loc_Id); + + if To_File_Name (Loc.Span.Ptr) /= File then + return True; + end if; + + Loc_Id := Loc.Next; + end loop; + + return False; + end Has_Multiple_Files; + + -- Start of processing for Should_Write_File_Name + + begin + return + Has_Multiple_Files (Diag) + or else To_File_Name (Sub_Loc.Span.Ptr) /= + To_File_Name (Diag_Loc.Span.Ptr); + end Should_Write_File_Name; + + ------------------------ + -- Should_Write_Spans -- + ------------------------ + + function Should_Write_Spans + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object) return Boolean + is + Sub_Loc : constant Labeled_Span_Id := Primary_Location (Sub_Diag); + Diag_Loc : constant Labeled_Span_Id := Primary_Location (Diag); + begin + return + Sub_Loc /= No_Labeled_Span and then Diag_Loc /= No_Labeled_Span + and then Locations.Table (Sub_Loc).Span.Ptr /= + Locations.Table (Diag_Loc).Span.Ptr; + end Should_Write_Spans; + + ---------------- + -- Print_Edit -- + ---------------- + + procedure Print_Edit (Edit : Edit_Type; Offset : Integer) is + Buf : constant Source_Buffer_Ptr := + Source_Text (Get_Source_File_Index (Edit.Span.Ptr)); + + Line_Nr : constant Pos := Pos (Get_Physical_Line_Number (Edit.Span.Ptr)); + + Line_Fst : constant Source_Ptr := Get_Line_Start (Buf, Edit.Span.First); + Line_Lst : constant Source_Ptr := Get_Line_End (Buf, Edit.Span.First); + begin + Write_Str (String'(1 .. Offset => ' ')); + Write_Str ("--> " & To_File_Name (Edit.Span.Ptr)); + Write_Eol; + + -- write the original line + + Write_Char ('-'); + Write_Line_Marker (Line_Nr, MAX_BAR_POS - 1); + + Write_Buffer (Buf => Buf, First => Line_Fst, Last => Line_Lst); + + -- write the edited line + + Write_Char ('+'); + Write_Line_Marker (Line_Nr, MAX_BAR_POS - 1); + + Write_Buffer + (Buf => Buf, First => Line_Fst, Last => Edit.Span.First - 1); + + if Edit.Text /= null then + Write_Str (Edit.Text.all); + end if; + + Write_Buffer (Buf => Buf, First => Edit.Span.Last + 1, Last => Line_Lst); + end Print_Edit; + + --------------- + -- Print_Fix -- + --------------- + + procedure Print_Fix (Fix : Fix_Type; Offset : Integer) is + E : Edit_Id; + begin + Write_Str (String'(1 .. Offset => ' ')); + Write_Str ("+ Fix: "); + + if Fix.Description /= null then + Write_Str (Fix.Description.all); + end if; + Write_Eol; + + E := Fix.Edits; + while E /= No_Edit loop + Print_Edit (Edits.Table (E), MAX_BAR_POS - 1); + + E := Edits.Table (E).Next; + end loop; + end Print_Fix; + + -------------------------- + -- Print_Sub_Diagnostic -- + -------------------------- + + procedure Print_Sub_Diagnostic + (Sub_Diag : Error_Msg_Object; Diag : Error_Msg_Object; Offset : Integer) + is + begin + Write_Str (String'(1 .. Offset => ' ')); + + Write_Str ("+ "); + + Write_Str (Sub_Diag.Text.all); + Write_Eol; + + if Should_Write_Spans (Sub_Diag, Diag) then + Write_Labeled_Spans + (Locations => Sub_Diag.Locations, + Write_File_Name => Should_Write_File_Name (Sub_Diag, Diag), + File_Name_Offset => Offset, + Include_Spans => not GNATprove_Mode or else Sub_Diag.Kind /= Info, + SGR_Code => SGR_Note); + end if; + end Print_Sub_Diagnostic; + + ---------------------- + -- Print_Diagnostic -- + ---------------------- + + procedure Print_Diagnostic (E : Error_Msg_Id) is + E_Msg : constant Error_Msg_Object := Errors.Table (E); + + E_Next_Id : Error_Msg_Id; + + F : Fix_Id; + begin + -- Print the main diagnostic + + Write_Error_Msg_Line (E_Msg); + + -- Print diagnostic locations along with spans + + Write_Labeled_Spans + (Locations => E_Msg.Locations, + Write_File_Name => True, + File_Name_Offset => 0, + Include_Spans => not GNATprove_Mode or else E_Msg.Kind /= Info, + SGR_Code => Get_SGR_Code (E_Msg)); + + -- Print subdiagnostics + + E_Next_Id := E_Msg.Next; + while E_Next_Id /= No_Error_Msg + and then Errors.Table (E_Next_Id).Msg_Cont + loop + -- Print the subdiagnostic and offset the location of the file + -- name + Print_Sub_Diagnostic + (Errors.Table (E_Next_Id), E_Msg, MAX_BAR_POS - 1); + + E_Next_Id := Errors.Table (E_Next_Id).Next; + end loop; + + -- Print fixes + + F := E_Msg.Fixes; + while F /= No_Fix loop + Print_Fix (Fixes.Table (F), MAX_BAR_POS - 1); + + F := Fixes.Table (F).Next; + end loop; + + -- Separate main diagnostics with a blank line + + Write_Eol; + end Print_Diagnostic; + + -------------------------- + -- Print_Error_Messages -- + -------------------------- + + procedure Print_Error_Messages is + E : Error_Msg_Id; + begin + Set_Standard_Error; + + E := First_Error_Msg; + while E /= No_Error_Msg loop + + if not Errors.Table (E).Deleted and then not Errors.Table (E).Msg_Cont + then + Print_Diagnostic (E); + end if; + + E := Errors.Table (E).Next; + end loop; + + Set_Standard_Output; + end Print_Error_Messages; + + ------------------ + -- To_File_Name -- + ------------------ + + function To_File_Name (Sptr : Source_Ptr) return String is + Sfile : constant Source_File_Index := Get_Source_File_Index (Sptr); + Ref_Name : constant File_Name_Type := + (if Full_Path_Name_For_Brief_Errors then Full_Ref_Name (Sfile) + else Reference_Name (Sfile)); + + begin + return Get_Name_String (Ref_Name); + end To_File_Name; + + -------------------- + -- Line_To_String -- + -------------------- + + function Line_To_String (Sptr : Source_Ptr) return String is + Line : constant Logical_Line_Number := Get_Logical_Line_Number (Sptr); + Img_Raw : constant String := Int'Image (Int (Line)); + + begin + return Img_Raw (Img_Raw'First + 1 .. Img_Raw'Last); + end Line_To_String; + + ---------------------- + -- Column_To_String -- + ---------------------- + + function Column_To_String (Sptr : Source_Ptr) return String is + Col : constant Column_Number := Get_Column_Number (Sptr); + Img_Raw : constant String := Int'Image (Int (Col)); + + begin + return + (if Col < 10 then "0" else "") & + Img_Raw (Img_Raw'First + 1 .. Img_Raw'Last); + end Column_To_String; + + --------------- + -- To_String -- + --------------- + + function To_String (Sptr : Source_Ptr) return String is + begin + return + To_File_Name (Sptr) & ":" & Line_To_String (Sptr) & ":" & + Column_To_String (Sptr); + end To_String; + +end Erroutc.Pretty_Emitter; |