aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/diagnostics-utils.adb
blob: abde955f8e8e7172036bd4480159a685b0e33a50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                     D I A G N O S T I C S . U T I L S                    --
--                                                                          --
--                                 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 Diagnostics.Repository;        use Diagnostics.Repository;
with Diagnostics.Switch_Repository; use Diagnostics.Switch_Repository;
with Errout;                        use Errout;
with Erroutc;                       use Erroutc;
with Namet;                         use Namet;
with Opt;                           use Opt;
with Sinput;                        use Sinput;
with Sinfo.Nodes;                   use Sinfo.Nodes;
with Warnsw;                        use Warnsw;

package body Diagnostics.Utils is

   ------------------
   -- Get_Human_Id --
   ------------------

   function Get_Human_Id (D : Diagnostic_Type) return String_Ptr is
   begin
      if D.Switch = No_Switch_Id then
         return Diagnostic_Entries (D.Id).Human_Id;
      else
         return Get_Switch (D).Human_Id;
      end if;
   end Get_Human_Id;

   ------------------
   -- 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;

   --------------------
   -- Sloc_To_String --
   --------------------

   function Sloc_To_String
     (N : Node_Or_Entity_Id; Ref : Source_Ptr) return String
   is

   begin
      return Sloc_To_String (Sloc (N), Ref);
   end Sloc_To_String;

   --------------------
   -- Sloc_To_String --
   --------------------

   function Sloc_To_String (Sptr : Source_Ptr; Ref : Source_Ptr) return String
   is

   begin
      if Sptr = No_Location then
         return "at unknown location";

      elsif Sptr = System_Location then
         return "in package System";

      elsif Sptr = Standard_Location then
         return "in package Standard";

      elsif Sptr = Standard_ASCII_Location then
         return "in package Standard.ASCII";

      else
         if Full_File_Name (Get_Source_File_Index (Sptr))
            /= Full_File_Name (Get_Source_File_Index (Ref))
         then
            return "at " & To_String (Sptr);
         else
            return "at line " & Line_To_String (Sptr);
         end if;
      end if;
   end Sloc_To_String;

   ------------------
   -- To_Full_Span --
   ------------------

   function To_Full_Span (N : Node_Id) return Source_Span
   is
      Fst, Lst : Node_Id;
   begin
      First_And_Last_Nodes (N, Fst, Lst);
      return To_Span (Ptr   => Sloc (N),
                      First => First_Sloc (Fst),
                      Last  => Last_Sloc (Lst));
   end To_Full_Span;

   ---------------
   -- To_String --
   ---------------

   function To_String (Id : Diagnostic_Id) return String is
   begin
      if Id = No_Diagnostic_Id then
         return "GNAT0000";
      else
         return Id'Img;
      end if;
   end To_String;

   -------------
   -- To_Name --
   -------------

   function To_Name (E : Entity_Id) return String is
   begin
      --  The name of the node operator "&" has many special cases. Reuse the
      --  node to name conversion implementation from the errout package for
      --  now.

      Error_Msg_Node_1 := E;
      Set_Msg_Text ("&", Sloc (E));

      return Msg_Buffer (1 .. Msglen);
   end To_Name;

   ------------------
   -- To_Type_Name --
   ------------------

   function To_Type_Name (E : Entity_Id) return String is
   begin
      Error_Msg_Node_1 := E;
      Set_Msg_Text ("}", Sloc (E));

      return Msg_Buffer (1 .. Msglen);
   end To_Type_Name;

   --------------------
   -- Kind_To_String --
   --------------------

   function Kind_To_String
     (D : Sub_Diagnostic_Type;
      Parent : Diagnostic_Type) return String
   is
     (case D.Kind is
        when Continuation => Kind_To_String (Parent),
        when Help => "help",
        when Note => "note",
        when Suggestion => "suggestion");

   --------------------
   -- Kind_To_String --
   --------------------

   function Kind_To_String (D : Diagnostic_Type) return String is
     (if D.Warn_Err then "error"
      else
       (case D.Kind is
        when Diagnostics.Error | Non_Serious_Error => "error",
        when Warning | Restriction_Warning | Default_Warning |
             Tagless_Warning => "warning",
        when Style => "style",
        when Info => "info"));

   ------------------------------
   -- Get_Primary_Labeled_Span --
   ------------------------------

   function Get_Primary_Labeled_Span (Spans : Labeled_Span_List)
                                      return Labeled_Span_Type
   is
      use Labeled_Span_Lists;

      S  : Labeled_Span_Type;
      It : Iterator;
   begin
      if Present (Spans) then
         It := Iterate (Spans);
         while Has_Next (It) loop
            Next (It, S);
            if S.Is_Primary then
               return S;
            end if;
         end loop;
      end if;

      return No_Labeled_Span;
   end Get_Primary_Labeled_Span;

   --------------------
   -- Get_Doc_Switch --
   --------------------

   function Get_Doc_Switch (Diag : Diagnostic_Type) return String is
   begin
      if Warning_Doc_Switch
        and then Diag.Kind in Default_Warning
                            | Info
                            | Restriction_Warning
                            | Style
                            | Warning
      then
         if Diag.Switch = No_Switch_Id then
            if Diag.Kind = Restriction_Warning then
               return "[restriction warning]";

               --  Info messages can have a switch tag but they should not have
               --  a default switch tag.

            elsif Diag.Kind /= Info then

               --  For Default_Warning

               return "[enabled by default]";
            end if;
         else
            declare
               S : constant Switch_Type := Get_Switch (Diag);
            begin
               return "[-" & S.Short_Name.all & "]";
            end;
         end if;
      end if;

      return "";
   end Get_Doc_Switch;

   --------------------
   -- Appears_Before --
   --------------------

   function Appears_Before (D1, D2 : Diagnostic_Type) return Boolean is

   begin
      return Appears_Before (Primary_Location (D1).Span.Ptr,
                             Primary_Location (D2).Span.Ptr);
   end Appears_Before;

   --------------------
   -- Appears_Before --
   --------------------

   function Appears_Before (P1, P2 : Source_Ptr) return Boolean is

   begin
      if Get_Source_File_Index (P1) = Get_Source_File_Index (P2) then
         if Get_Logical_Line_Number (P1) = Get_Logical_Line_Number (P2) then
            return Get_Column_Number (P1) < Get_Column_Number (P2);
         else
            return Get_Logical_Line_Number (P1) < Get_Logical_Line_Number (P2);
         end if;
      else
         return Get_Source_File_Index (P1) < Get_Source_File_Index (P2);
      end if;
   end Appears_Before;

   ------------------------------
   -- Insert_Based_On_Location --
   ------------------------------

   procedure Insert_Based_On_Location
     (List : Diagnostic_List;
      Diagnostic : Diagnostic_Type)
   is
      use Diagnostics_Lists;

      It : Iterator := Iterate (List);
      D  : Diagnostic_Type;
   begin
      --  This is the common scenario where the error is reported at the
      --  natural order the tree is processed. This saves a lot of time when
      --  looking for the correct position in the list when there are a lot of
      --  diagnostics.

      if Present (List) and then
         not Is_Empty (List) and then
         Appears_Before (Last (List), Diagnostic)
      then
         Append (List, Diagnostic);
      else
         while Has_Next (It) loop
            Next (It, D);

            if Appears_Before (Diagnostic, D) then
               Insert_Before (List, D, Diagnostic);
               return;
            end if;
         end loop;

         Append (List, Diagnostic);
      end if;
   end Insert_Based_On_Location;

end Diagnostics.Utils;