Skip to content

Commit 95f0819

Browse files
committed
Merge branch 'mr/pmderodat/unparsing' into 'master'
Unparsing: rework error message handling See merge request eng/libadalang/langkit!1006
2 parents 341de08 + 6aa46e5 commit 95f0819

File tree

7 files changed

+164
-66
lines changed

7 files changed

+164
-66
lines changed

langkit/support/langkit_support-diagnostics.adb

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,34 @@
33
-- SPDX-License-Identifier: Apache-2.0
44
--
55

6+
with Ada.Text_IO; use Ada.Text_IO;
7+
68
package body Langkit_Support.Diagnostics is
79

10+
function Sloc_Prefix (Sloc_Range : Source_Location_Range) return String;
11+
-- If ``Sloc_Range`` is not null, return a "X:Y: " prefix with the
12+
-- corresponding start line/column numbers.
13+
14+
-----------------
15+
-- Sloc_Prefix --
16+
-----------------
17+
18+
function Sloc_Prefix (Sloc_Range : Source_Location_Range) return String is
19+
Sloc : constant Source_Location := Start_Sloc (Sloc_Range);
20+
begin
21+
return
22+
(if Sloc = No_Source_Location
23+
then ""
24+
else Image (Sloc) & ": ");
25+
end Sloc_Prefix;
26+
827
----------------------
928
-- To_Pretty_String --
1029
----------------------
1130

1231
function To_Pretty_String (D : Diagnostic) return String is
13-
Sloc : constant Source_Location := Start_Sloc (D.Sloc_Range);
14-
Sloc_Prefix : constant String :=
15-
(if Sloc = No_Source_Location
16-
then ""
17-
else Image (Sloc) & ": ");
1832
begin
19-
return Sloc_Prefix & Image (To_Text (D.Message));
33+
return Sloc_Prefix (D.Sloc_Range) & Image (To_Text (D.Message));
2034
end To_Pretty_String;
2135

2236
------------
@@ -58,4 +72,37 @@ package body Langkit_Support.Diagnostics is
5872
Append (Diagnostics, Sloc_Range, To_Text (Msg));
5973
end Append;
6074

75+
-----------
76+
-- Print --
77+
-----------
78+
79+
procedure Print
80+
(Diagnostics : Diagnostics_Vectors.Vector;
81+
Prefix : String := "error: ";
82+
Indent : Natural := 2) is
83+
begin
84+
for D of Diagnostics loop
85+
Put (Sloc_Prefix (D.Sloc_Range));
86+
Put (Prefix);
87+
declare
88+
Empty_Line : Boolean := False;
89+
Msg : constant String := To_UTF8 (To_Text (D.Message));
90+
begin
91+
for C of Msg loop
92+
if C = ASCII.LF then
93+
New_Line;
94+
Empty_Line := True;
95+
else
96+
if Empty_Line then
97+
Put ((1 .. Indent => ' '));
98+
Empty_Line := False;
99+
end if;
100+
Put (C);
101+
end if;
102+
end loop;
103+
end;
104+
New_Line;
105+
end loop;
106+
end Print;
107+
61108
end Langkit_Support.Diagnostics;

langkit/support/langkit_support-diagnostics.ads

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,12 @@ package Langkit_Support.Diagnostics is
5252
Exc : Ada.Exceptions.Exception_Occurrence);
5353
-- Shortcut to append an exception message to a vector
5454

55+
procedure Print
56+
(Diagnostics : Diagnostics_Vectors.Vector;
57+
Prefix : String := "error: ";
58+
Indent : Natural := 2);
59+
-- Print all diagnostics in ``Diagnostics`` on the standard output, with
60+
-- ``Prefix`` before each message. For multi-line messages, insert
61+
-- ``Indent`` spaces before each line except the first one.
62+
5563
end Langkit_Support.Diagnostics;

langkit/support/langkit_support-generic_api-unparsing.adb

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
with Ada.Command_Line;
77
with Ada.Containers.Hashed_Maps;
88
with Ada.Containers.Vectors;
9-
with Ada.Exceptions; use Ada.Exceptions;
109
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
1110
with Ada.Text_IO; use Ada.Text_IO;
1211
with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO;
@@ -28,6 +27,7 @@ use Langkit_Support.Internal.Descriptor;
2827
with Langkit_Support.Internal.Unparsing;
2928
use Langkit_Support.Internal.Unparsing;
3029
with Langkit_Support.Prettier_Utils; use Langkit_Support.Prettier_Utils;
30+
with Langkit_Support.Slocs; use Langkit_Support.Slocs;
3131
with Langkit_Support.Symbols; use Langkit_Support.Symbols;
3232

3333
package body Langkit_Support.Generic_API.Unparsing is
@@ -480,7 +480,10 @@ package body Langkit_Support.Generic_API.Unparsing is
480480
---------------------------
481481

482482
function Load_Unparsing_Config
483-
(Language : Language_Id; Filename : String) return Unparsing_Configuration
483+
(Language : Language_Id;
484+
Filename : String;
485+
Diagnostics : in out Diagnostics_Vectors.Vector)
486+
return Unparsing_Configuration
484487
is
485488
-- Create a map so that we can lookup nodes/fields by name
486489

@@ -618,6 +621,10 @@ package body Langkit_Support.Generic_API.Unparsing is
618621
-- Assuming that JSON is the "fields" configuration for Node, parse its
619622
-- field configurations and set Configs accordingly.
620623

624+
procedure Abort_Parsing (Message : String) with No_Return;
625+
-- Append an item to ``Diagnostics`` and raise an Invalid_Input
626+
-- exception.
627+
621628
Result : constant Unparsing_Configuration_Access :=
622629
new Unparsing_Configuration_Record;
623630
Pool : Document_Pool renames Result.Pool;
@@ -630,7 +637,7 @@ package body Langkit_Support.Generic_API.Unparsing is
630637
T : constant Type_Ref := Map.Lookup_Type (To_Symbol (Name));
631638
begin
632639
if T = No_Type_Ref or else not Is_Node_Type (T) then
633-
raise Invalid_Input with "invalid node name: " & Name;
640+
Abort_Parsing ("invalid node name: " & Name);
634641
end if;
635642
return To_Index (T);
636643
end To_Type_Index;
@@ -646,14 +653,14 @@ package body Langkit_Support.Generic_API.Unparsing is
646653
Map.Lookup_Struct_Member (Node, To_Symbol (Name));
647654
begin
648655
if M = No_Struct_Member_Ref then
649-
raise Invalid_Input with
650-
"invalid field for " & Node_Type_Image (Node) & ": " & Name;
656+
Abort_Parsing
657+
("invalid field for " & Node_Type_Image (Node) & ": " & Name);
651658
elsif not Is_Field (M) then
652-
raise Invalid_Input with
653-
Name & " is not a syntax field for " & Node_Type_Image (Node);
659+
Abort_Parsing
660+
(Name & " is not a syntax field for " & Node_Type_Image (Node));
654661
elsif Is_Null_For (M, Node) then
655-
raise Invalid_Input with
656-
Name & " is a null field for " & Node_Type_Image (Node);
662+
Abort_Parsing
663+
(Name & " is a null field for " & Node_Type_Image (Node));
657664
else
658665
return To_Index (M);
659666
end if;
@@ -1168,7 +1175,7 @@ package body Langkit_Support.Generic_API.Unparsing is
11681175
when Field_Template =>
11691176
"template for " & Field_Image (Context.Field, Context.Node));
11701177
begin
1171-
raise Invalid_Input with Prefix & ": " & Message;
1178+
Abort_Parsing (Prefix & ": " & Message);
11721179
end Abort_Parsing;
11731180

11741181
--------------------------
@@ -1220,6 +1227,16 @@ package body Langkit_Support.Generic_API.Unparsing is
12201227
JSON.Map_JSON_Object (Process'Access);
12211228
end Load_Field_Configs;
12221229

1230+
-------------------
1231+
-- Abort_Parsing --
1232+
-------------------
1233+
1234+
procedure Abort_Parsing (Message : String) is
1235+
begin
1236+
Append (Diagnostics, No_Source_Location_Range, To_Text (Message));
1237+
raise Invalid_Input;
1238+
end Abort_Parsing;
1239+
12231240
use type GNAT.Strings.String_Access;
12241241

12251242
-- First, parse the JSON document
@@ -1229,16 +1246,27 @@ package body Langkit_Support.Generic_API.Unparsing is
12291246
JSON : JSON_Value;
12301247
begin
12311248
if JSON_Text = null then
1232-
raise Invalid_Input with "cannot read " & Filename;
1249+
Abort_Parsing ("cannot read " & Filename);
12331250
end if;
12341251
JSON_Result := Read (JSON_Text.all);
12351252
GNAT.Strings.Free (JSON_Text);
12361253

12371254
if JSON_Result.Success then
12381255
JSON := JSON_Result.Value;
12391256
else
1240-
raise Invalid_Input with
1241-
Filename & ":" & Format_Parsing_Error (JSON_Result.Error);
1257+
declare
1258+
Sloc : constant Source_Location :=
1259+
(Line_Number (JSON_Result.Error.Line),
1260+
Column_Number (JSON_Result.Error.Column));
1261+
Sloc_Range : constant Source_Location_Range :=
1262+
Make_Range (Sloc, Sloc);
1263+
begin
1264+
Append
1265+
(Diagnostics,
1266+
Sloc_Range,
1267+
To_Text (To_String (JSON_Result.Error.Message)));
1268+
raise Invalid_Input;
1269+
end;
12421270
end if;
12431271

12441272
-- Then load the unparsing configuration from it. Require a
@@ -1248,7 +1276,7 @@ package body Langkit_Support.Generic_API.Unparsing is
12481276
Result.Language := Language;
12491277

12501278
if not JSON.Has_Field ("node_configs") then
1251-
raise Invalid_Input with "missing ""node_configs"" key";
1279+
Abort_Parsing ("missing ""node_configs"" key");
12521280
end if;
12531281

12541282
declare
@@ -1288,9 +1316,9 @@ package body Langkit_Support.Generic_API.Unparsing is
12881316

12891317
if Value.Has_Field ("sep") then
12901318
if not Is_List_Node (Node) then
1291-
raise Invalid_Input with
1292-
Name & " is not a list node, invalid ""sep"""
1293-
& " configuration";
1319+
Abort_Parsing
1320+
(Name & " is not a list node, invalid ""sep"""
1321+
& " configuration");
12941322
end if;
12951323
declare
12961324
Context : Template_Parsing_Context :=
@@ -1395,8 +1423,9 @@ package body Langkit_Support.Generic_API.Unparsing is
13951423

13961424
exception
13971425
when Invalid_Input =>
1426+
pragma Assert (not Diagnostics.Is_Empty);
13981427
Destroy (Symbols);
1399-
raise;
1428+
return No_Unparsing_Configuration;
14001429
end Load_Unparsing_Config;
14011430

14021431
--------------------------
@@ -1854,14 +1883,16 @@ package body Langkit_Support.Generic_API.Unparsing is
18541883
-- Parse the configuration file and the source file to pretty-print.
18551884
-- Abort if there is a parsing failure.
18561885

1886+
declare
1887+
Diagnostics : Diagnostics_Vectors.Vector;
1888+
Filename : constant String := To_String (Config_Filename.Get);
18571889
begin
1858-
Config :=
1859-
Load_Unparsing_Config (Language, To_String (Config_Filename.Get));
1860-
exception
1861-
when Exc : Invalid_Input =>
1890+
Config := Load_Unparsing_Config (Language, Filename, Diagnostics);
1891+
if Config = No_Unparsing_Configuration then
18621892
Put_Line ("Error when loading the unparsing configuration:");
1863-
Put_Line (Exception_Message (Exc));
1893+
Print (Diagnostics);
18641894
return;
1895+
end if;
18651896
end;
18661897
Context := Create_Context (Language);
18671898
Unit := Context.Get_From_File

langkit/support/langkit_support-generic_api-unparsing.ads

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
-- with Ada.Text_IO; use Ada.Text_IO;
2121
-- with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO;
2222
--
23+
-- with Langkit_Support.Diagnostics; use Langkit_Support.Diagnostics;
2324
-- with Langkit_Support.Generic_API; use Langkit_Support.Generic_API;
2425
-- with Langkit_Support.Generic_API.Analysis;
2526
-- use Langkit_Support.Generic_API.Analysis;
@@ -38,8 +39,9 @@
3839
-- -- Load_Unparsing_Config function for more information about this
3940
-- -- file.
4041
--
41-
-- Config : constant Unparsing_Configuration :=
42-
-- Load_Unparsing_Config (Self_Id, "config.json");
42+
-- Diagnostics : Diagnostics_Vectors.Vector;
43+
-- Config : constant Unparsing_Configuration :=
44+
-- Load_Unparsing_Config (Self_Id, "config.json", Diagnostics);
4345
--
4446
-- -- Parse the source file to reformat
4547
--
@@ -48,7 +50,15 @@
4850
-- U : constant Lk_Unit :=
4951
-- Ctx.Get_From_File (Ada.Command_Line.Argument (1));
5052
-- begin
51-
-- -- If it has parsing errors, bail out
53+
-- -- If we were unable to load the unparsing configuration, bail out
54+
--
55+
-- if Config = No_Unparsing_Configuration then
56+
-- Put_Line ("Error when loading the unparsing configuration:");
57+
-- Print (Diagnostics);
58+
-- raise Program_Error;
59+
-- end if;
60+
--
61+
-- -- If the source file to reformat has parsing errors, bail out
5262
--
5363
-- if U.Has_Diagnostics then
5464
-- Put_Line ("Parsing errors:");
@@ -74,6 +84,7 @@ private with Ada.Finalization;
7484

7585
with Prettier_Ada.Documents;
7686

87+
with Langkit_Support.Diagnostics; use Langkit_Support.Diagnostics;
7788
with Langkit_Support.Generic_API.Analysis;
7889
use Langkit_Support.Generic_API.Analysis;
7990

@@ -83,12 +94,18 @@ package Langkit_Support.Generic_API.Unparsing is
8394
-- Configuration that customizes how source fragments are turned into a
8495
-- prettier document.
8596

97+
No_Unparsing_Configuration : constant Unparsing_Configuration;
98+
-- Special value to mean the absence of an unparsing configuration
99+
86100
function Load_Unparsing_Config
87-
(Language : Language_Id;
88-
Filename : String) return Unparsing_Configuration;
101+
(Language : Language_Id;
102+
Filename : String;
103+
Diagnostics : in out Diagnostics_Vectors.Vector)
104+
return Unparsing_Configuration;
89105
-- Read and parse the unparsing configuration for the given Language from
90-
-- Filename. Raise a Langkit_Support.Errors.Invalid_Input exception if an
91-
-- error occurs while reading the configuration file.
106+
-- Filename. Append error messages to ``Diagnostics`` and return
107+
-- ``No_Unparsing_Configuration`` if an error occurs while reading the
108+
-- configuration file.
92109
--
93110
-- The configuration is a JSON file that provides "document templates":
94111
-- patterns to generate Prettier documents:
@@ -286,4 +303,7 @@ private
286303
overriding procedure Adjust (Self : in out Unparsing_Configuration);
287304
overriding procedure Finalize (Self : in out Unparsing_Configuration);
288305

306+
No_Unparsing_Configuration : constant Unparsing_Configuration :=
307+
(Ada.Finalization.Controlled with Value => null);
308+
289309
end Langkit_Support.Generic_API.Unparsing;

testsuite/tests/ada_api/unparsing/commands.adb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ with GNATCOLL.JSON; use GNATCOLL.JSON;
66
with Prettier_Ada.Documents;
77
with Prettier_Ada.Documents.Json;
88

9+
with Langkit_Support.Diagnostics; use Langkit_Support.Diagnostics;
910
with Langkit_Support.Generic_API.Analysis;
1011
use Langkit_Support.Generic_API.Analysis;
1112
with Langkit_Support.Generic_API.Unparsing;
@@ -42,7 +43,15 @@ procedure Commands is
4243
raise Program_Error;
4344
end if;
4445

45-
Config := Load_Unparsing_Config (Self_Id, Filename);
46+
declare
47+
Diagnostics : Diagnostics_Vectors.Vector;
48+
begin
49+
Config := Load_Unparsing_Config (Self_Id, Filename, Diagnostics);
50+
if Config = No_Unparsing_Configuration then
51+
Print (Diagnostics);
52+
raise Program_Error;
53+
end if;
54+
end;
4655
Doc := Unparse_To_Prettier (Unit.Root, Config);
4756
JSON_Text := Prettier_Ada.Documents.Json.Serialize (Doc);
4857

0 commit comments

Comments
 (0)