view gcc/ada/sa_messages.adb @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line source

------------------------------------------------------------------------------
--                       C O D E P E E R / S P A R K                        --
--                                                                          --
--                     Copyright (C) 2015-2019, AdaCore                     --
--                                                                          --
-- This 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.  This software is distributed in the hope  that it will be useful, --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY 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  this  software;   see  file --
-- COPYING3.  If not, go to http://www.gnu.org/licenses for a complete copy --
-- of the license.                                                          --
--                                                                          --
------------------------------------------------------------------------------

pragma Ada_2012;

with Ada.Directories; use Ada.Directories;
with Ada.Strings.Unbounded.Hash;

with Ada.Text_IO;     use Ada.Text_IO;
with GNATCOLL.JSON;   use GNATCOLL.JSON;

package body SA_Messages is

   -----------------------
   -- Local subprograms --
   -----------------------

   function "<" (Left, Right : SA_Message) return Boolean is
     (if Left.Kind /= Right.Kind then
         Left.Kind < Right.Kind
      else
         Left.Kind in Check_Kind
           and then Left.Check_Result < Right.Check_Result);

   function "<" (Left, Right : Simple_Source_Location) return Boolean is
      (if Left.File_Name /= Right.File_Name then
          Left.File_Name < Right.File_Name
       elsif Left.Line /= Right.Line then
          Left.Line < Right.Line
       else
          Left.Column < Right.Column);

   function "<" (Left, Right : Source_Locations) return Boolean is
     (if Left'Length /= Right'Length then
         Left'Length < Right'Length
      elsif Left'Length = 0 then
         False
      elsif Left (Left'Last) /= Right (Right'Last) then
         Left (Left'Last) < Right (Right'Last)
      else
         Left (Left'First .. Left'Last - 1) <
           Right (Right'First .. Right'Last - 1));

   function "<" (Left, Right : Source_Location) return Boolean is
     (Left.Locations < Right.Locations);

   function Base_Location
     (Location : Source_Location) return Simple_Source_Location is
     (Location.Locations (1));

   function Hash (Key : SA_Message) return Hash_Type;
   function Hash (Key : Source_Location) return Hash_Type;

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

   function "<" (Left, Right : Message_And_Location) return Boolean is
     (if Left.Message = Right.Message
      then Left.Location < Right.Location
      else Left.Message < Right.Message);

   ------------
   -- Column --
   ------------

   function Column (Location : Source_Location) return Column_Number is
     (Base_Location (Location).Column);

   ---------------
   -- File_Name --
   ---------------

   function File_Name (Location : Source_Location) return String is
     (To_String (Base_Location (Location).File_Name));

   function File_Name (Location : Source_Location) return Unbounded_String is
     (Base_Location (Location).File_Name);

   ------------------------
   -- Enclosing_Instance --
   ------------------------

   function Enclosing_Instance
     (Location : Source_Location) return Source_Location_Or_Null is
     (Count     => Location.Count - 1,
      Locations => Location.Locations (2 .. Location.Count));

   ----------
   -- Hash --
   ----------

   function Hash (Key : Message_And_Location) return Hash_Type is
     (Hash (Key.Message) + Hash (Key.Location));

   function Hash (Key : SA_Message) return Hash_Type is
   begin
      return Result : Hash_Type :=
                        Hash_Type'Mod (Message_Kind'Pos (Key.Kind))
      do
         if Key.Kind in Check_Kind then
            Result := Result +
              Hash_Type'Mod (SA_Check_Result'Pos (Key.Check_Result));
         end if;
      end return;
   end Hash;

   function Hash (Key : Source_Location) return Hash_Type is
   begin
      return Result : Hash_Type := Hash_Type'Mod (Key.Count) do
         for Loc of Key.Locations loop
            Result := Result + Hash (Loc.File_Name);
            Result := Result + Hash_Type'Mod (Loc.Line);
            Result := Result + Hash_Type'Mod (Loc.Column);
         end loop;
      end return;
   end Hash;

   ---------------
   -- Iteration --
   ---------------

   function Iteration (Location : Source_Location) return Iteration_Id is
     (Base_Location (Location).Iteration);

   ----------
   -- Line --
   ----------

   function Line (Location : Source_Location) return Line_Number is
     (Base_Location (Location).Line);

   --------------
   -- Location --
   --------------

   function Location
     (Item : Message_And_Location) return Source_Location is
     (Item.Location);

   ----------
   -- Make --
   ----------

   function Make
     (File_Name          : String;
      Line               : Line_Number;
      Column             : Column_Number;
      Iteration          : Iteration_Id;
      Enclosing_Instance : Source_Location_Or_Null) return Source_Location
   is
   begin
      return Result : Source_Location
                        (Count => Enclosing_Instance.Count + 1)
      do
         Result.Locations (1) :=
           (File_Name => To_Unbounded_String (File_Name),
            Line      => Line,
            Column    => Column,
            Iteration => Iteration);

         Result.Locations (2 .. Result.Count) := Enclosing_Instance.Locations;
      end return;
   end Make;

   ------------------
   -- Make_Msg_Loc --
   ------------------

   function Make_Msg_Loc
     (Msg : SA_Message;
      Loc : Source_Location) return Message_And_Location
   is
   begin
      return Message_And_Location'(Count    => Loc.Count,
                                   Message  => Msg,
                                   Location => Loc);
   end Make_Msg_Loc;

   -------------
   -- Message --
   -------------

   function Message (Item : Message_And_Location) return SA_Message is
     (Item.Message);

   package Field_Names is

      --  A Source_Location value is represented in JSON as a two or three
      --  field value having fields Message_Kind (a string) and Locations (an
      --  array); if the Message_Kind indicates a check kind, then a third
      --  field is present: Check_Result (a string). The element type of the
      --  Locations array is a value having at least 4 fields:
      --  File_Name (a string), Line (an integer), Column (an integer),
      --  and Iteration_Kind (an integer); if the Iteration_Kind field
      --  has the value corresponding to the enumeration literal Numbered,
      --  then two additional integer fields are present, Iteration_Number
      --  and Iteration_Of_Total.

      Check_Result       : constant String := "Check_Result";
      Column             : constant String := "Column";
      File_Name          : constant String := "File_Name";
      Iteration_Kind     : constant String := "Iteration_Kind";
      Iteration_Number   : constant String := "Iteration_Number";
      Iteration_Of_Total : constant String := "Iteration_Total";
      Line               : constant String := "Line";
      Locations          : constant String := "Locations";
      Message_Kind       : constant String := "Message_Kind";
      Messages           : constant String := "Messages";
   end Field_Names;

   package body Writing is
      File : File_Type;
      --  The file to which output will be written (in Close, not in Write)

      Messages : JSON_Array;
      --  Successive calls to Write append messages to this list

      -----------------------
      -- Local subprograms --
      -----------------------

      function To_JSON_Array
        (Locations : Source_Locations) return JSON_Array;
      --  Represent a Source_Locations array as a JSON_Array

      function To_JSON_Value
        (Location : Simple_Source_Location) return JSON_Value;
      --  Represent a Simple_Source_Location as a JSON_Value

      -----------
      -- Close --
      -----------

      procedure Close is
         Value : constant JSON_Value := Create_Object;

      begin
         --  only one field for now
         Set_Field (Value, Field_Names.Messages, Messages);
         Put_Line (File, Write (Item => Value, Compact => False));
         Clear (Messages);
         Close (File => File);
      end Close;

      -------------
      -- Is_Open --
      -------------

      function Is_Open return Boolean is (Is_Open (File));

      ----------
      -- Open --
      ----------

      procedure Open (File_Name : String) is
      begin
         Create (File => File, Mode => Out_File, Name => File_Name);
         Clear (Messages);
      end Open;

      -------------------
      -- To_JSON_Array --
      -------------------

      function To_JSON_Array
        (Locations : Source_Locations) return JSON_Array
      is
      begin
         return Result : JSON_Array := Empty_Array do
            for Location of Locations loop
               Append (Result, To_JSON_Value (Location));
            end loop;
         end return;
      end To_JSON_Array;

      -------------------
      -- To_JSON_Value --
      -------------------

      function To_JSON_Value
        (Location : Simple_Source_Location) return JSON_Value
      is
      begin
         return Result : constant JSON_Value := Create_Object do
            Set_Field (Result, Field_Names.File_Name, Location.File_Name);
            Set_Field (Result, Field_Names.Line, Integer (Location.Line));
            Set_Field (Result, Field_Names.Column, Integer (Location.Column));
            Set_Field (Result, Field_Names.Iteration_Kind, Integer'(
                       Iteration_Kind'Pos (Location.Iteration.Kind)));

            if Location.Iteration.Kind = Numbered then
               Set_Field (Result, Field_Names.Iteration_Number,
                          Location.Iteration.Number);
               Set_Field (Result, Field_Names.Iteration_Of_Total,
                          Location.Iteration.Of_Total);
            end if;
         end return;
      end To_JSON_Value;

      -----------
      -- Write --
      -----------

      procedure Write (Message : SA_Message; Location : Source_Location) is
         Value : constant JSON_Value := Create_Object;

      begin
         Set_Field (Value, Field_Names.Message_Kind, Message.Kind'Img);

         if Message.Kind in Check_Kind then
            Set_Field
              (Value, Field_Names.Check_Result, Message.Check_Result'Img);
         end if;

         Set_Field
           (Value, Field_Names.Locations, To_JSON_Array (Location.Locations));
         Append (Messages, Value);
      end Write;
   end Writing;

   package body Reading is
      File       : File_Type;
      --  The file from which messages are read (in Open, not in Read)

      Messages   : JSON_Array;
      --  The list of messages that were read in from File

      Next_Index : Positive;
      --  The index of the message in Messages which will be returned by the
      --  next call to Get.

      Parse_Full_Path : Boolean := True;
      --  if the full path or only the base name of the file should be parsed

      -----------
      -- Close --
      -----------

      procedure Close is
      begin
         Clear (Messages);
         Close (File);
      end Close;

      ----------
      -- Done --
      ----------

      function Done return Boolean is (Next_Index > Length (Messages));

      ---------
      -- Get --
      ---------

      function Get return Message_And_Location is
         Value : constant JSON_Value := Get (Messages, Next_Index);

         function Get_Message (Kind :  Message_Kind) return SA_Message;
         --  Return SA_Message of given kind, filling in any non-discriminant
         --  by reading from Value.

         function Make
           (Location : Source_Location;
            Message  : SA_Message) return Message_And_Location;
         --  Constructor

         function To_Location
           (Encoded   : JSON_Array;
            Full_Path : Boolean) return Source_Location;
         --  Decode a Source_Location from JSON_Array representation

         function To_Simple_Location
           (Encoded   : JSON_Value;
            Full_Path : Boolean) return Simple_Source_Location;
         --  Decode a Simple_Source_Location from JSON_Value representation

         -----------------
         -- Get_Message --
         -----------------

         function Get_Message (Kind :  Message_Kind) return SA_Message is
         begin
            --  If we had AI12-0086, then we could use aggregates here (which
            --  would be better than field-by-field assignment for the usual
            --  maintainability reasons). But we don't, so we won't.

            return Result : SA_Message (Kind => Kind) do
               if Kind in Check_Kind then
                  Result.Check_Result :=
                    SA_Check_Result'Value
                      (Get (Value, Field_Names.Check_Result));
               end if;
            end return;
         end Get_Message;

         ----------
         -- Make --
         ----------

         function Make
           (Location : Source_Location;
            Message  : SA_Message) return Message_And_Location
         is
           (Count => Location.Count, Message => Message, Location => Location);

         -----------------
         -- To_Location --
         -----------------

         function To_Location
           (Encoded   : JSON_Array;
            Full_Path : Boolean) return Source_Location is
         begin
            return Result : Source_Location (Count => Length (Encoded)) do
               for I in Result.Locations'Range loop
                  Result.Locations (I) :=
                    To_Simple_Location (Get (Encoded, I), Full_Path);
               end loop;
            end return;
         end To_Location;

         ------------------------
         -- To_Simple_Location --
         ------------------------

         function To_Simple_Location
           (Encoded   : JSON_Value;
            Full_Path : Boolean) return Simple_Source_Location
         is
            function Get_Iteration_Id
              (Kind : Iteration_Kind) return Iteration_Id;
            --  Given the discriminant for an Iteration_Id value, return the
            --  entire value.

            ----------------------
            -- Get_Iteration_Id --
            ----------------------

            function Get_Iteration_Id (Kind : Iteration_Kind)
              return Iteration_Id
            is
            begin
               --  Initialize non-discriminant fields, if any

               return Result : Iteration_Id (Kind => Kind) do
                  if Kind = Numbered then
                     Result :=
                       (Kind     => Numbered,
                        Number   =>
                          Get (Encoded, Field_Names.Iteration_Number),
                        Of_Total =>
                          Get (Encoded, Field_Names.Iteration_Of_Total));
                  end if;
               end return;
            end Get_Iteration_Id;

            --  Local variables

            FN : constant Unbounded_String :=
                   Get (Encoded, Field_Names.File_Name);

         --  Start of processing for To_Simple_Location

         begin
            return
              (File_Name =>
                 (if Full_Path then
                     FN
                  else
                     To_Unbounded_String (Simple_Name (To_String (FN)))),
               Line      =>
                 Line_Number (Integer'(Get (Encoded, Field_Names.Line))),
               Column    =>
                 Column_Number (Integer'(Get (Encoded, Field_Names.Column))),
               Iteration =>
                 Get_Iteration_Id
                   (Kind => Iteration_Kind'Val (Integer'(Get
                              (Encoded, Field_Names.Iteration_Kind)))));
         end To_Simple_Location;

      --  Start of processing for Get

      begin
         Next_Index := Next_Index + 1;

         return Make
           (Message  =>
              Get_Message
                (Message_Kind'Value (Get (Value, Field_Names.Message_Kind))),
            Location =>
              To_Location
                (Get (Value, Field_Names.Locations), Parse_Full_Path));
      end Get;

      -------------
      -- Is_Open --
      -------------

      function Is_Open return Boolean is (Is_Open (File));

      ----------
      -- Open --
      ----------

      procedure Open (File_Name : String; Full_Path : Boolean := True) is
         File_Text : Unbounded_String := Null_Unbounded_String;

      begin
         Parse_Full_Path := Full_Path;
         Open (File => File, Mode => In_File, Name => File_Name);

         --  File read here, not in Get, but that's an implementation detail

         while not End_Of_File (File) loop
            Append (File_Text, Get_Line (File));
         end loop;

         Messages   := Get (Read (File_Text), Field_Names.Messages);
         Next_Index := 1;
      end Open;
   end Reading;

end SA_Messages;