Skip to content

Commit 2a4c8dd

Browse files
committed
Merge branch 'topic/enhance_lkql_error' into 'master'
Enhance error messages when parsing an LKQL rule file Closes #337 See merge request eng/libadalang/langkit-query-language!309
2 parents e3ab8b5 + 60fb5b4 commit 2a4c8dd

File tree

8 files changed

+139
-63
lines changed

8 files changed

+139
-63
lines changed

lkql_checker/src/gnatcheck-compiler.adb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ package body Gnatcheck.Compiler is
442442
else Get_Rule_Id (Message_Kind)));
443443
end Analyze_Line;
444444

445-
-- Start of processing for Analyze_Builder_Output
445+
-- Start of processing for Analyze_Output
446446

447447
begin
448448
Errors := False;
@@ -512,12 +512,15 @@ package body Gnatcheck.Compiler is
512512
end if;
513513
end;
514514
end if;
515-
elsif Line_Len >= 20
516-
and then Line (1 .. 20) = "WORKER_FATAL_ERROR: "
515+
elsif Line_Len >= 16
516+
and then Line (1 .. 16) = "WORKER_WARNING: "
517517
then
518-
Error ("error raised by the worker: " & Line (21 .. Line_Len));
518+
Warning (Line (17 .. Line_Len));
519+
elsif Line_Len >= 14
520+
and then Line (1 .. 14) = "WORKER_ERROR: "
521+
then
522+
Error (Line (15 .. Line_Len));
519523
Errors := True;
520-
return;
521524
else
522525
Analyze_Line (Line (1 .. Line_Len));
523526
end if;
@@ -1692,8 +1695,7 @@ package body Gnatcheck.Compiler is
16921695

16931696
function Spawn_LKQL_Rule_File_Parser
16941697
(LKQL_RF_Name : String;
1695-
Result_File : String;
1696-
Error_File : String) return Process_Id
1698+
Result_File : String) return Process_Id
16971699
is
16981700
use GNAT.String_Split;
16991701

@@ -1737,7 +1739,7 @@ package body Gnatcheck.Compiler is
17371739
-- Spawn the process and return the associated process ID
17381740
Pid :=
17391741
Non_Blocking_Spawn
1740-
(Worker.all, Args (1 .. Num_Args), Result_File, Error_File);
1742+
(Worker.all, Args (1 .. Num_Args), Result_File);
17411743

17421744
for J in Args'Range loop
17431745
Free (Args (J));

lkql_checker/src/gnatcheck-compiler.ads

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@ package Gnatcheck.Compiler is
170170

171171
function Spawn_LKQL_Rule_File_Parser
172172
(LKQL_RF_Name : String;
173-
Result_File : String;
174-
Error_File : String) return Process_Id;
173+
Result_File : String) return Process_Id;
175174
-- Spawn the executable which handles the LKQL rule config file parsing
176175
-- with the provided `LKQL_RF_Name` then return the process identifier
177176
-- associated to it. Redirects all output made by the process in the

lkql_checker/src/gnatcheck-rules-rule_table.adb

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -805,8 +805,6 @@ package body Gnatcheck.Rules.Rule_Table is
805805
Get_Rule_File_Name (LKQL_RF_Name);
806806
JSON_Config_File_Name : constant String :=
807807
Global_Report_Dir.all & "gnatcheck-rules.json.out";
808-
Error_File_Name : constant String :=
809-
Global_Report_Dir.all & "gnatcheck-rules.json.err";
810808
Parser_Pid : Process_Id;
811809
Waited_Pid : Process_Id;
812810
Success : Boolean;
@@ -836,30 +834,30 @@ package body Gnatcheck.Rules.Rule_Table is
836834
-- Call the LKQL rule config file parser and parse its result
837835
Parser_Pid :=
838836
Spawn_LKQL_Rule_File_Parser
839-
(Rule_File_Absolute_Path, JSON_Config_File_Name, Error_File_Name);
837+
(Rule_File_Absolute_Path, JSON_Config_File_Name);
840838
Wait_Process (Waited_Pid, Success);
841839

842840
if Parser_Pid /= Waited_Pid or else not Success then
843841
Error ("can not call the LKQL rule file parser");
844842
Rule_Option_Problem_Detected := True;
845-
return;
846-
end if;
847-
Analyze_Output (Error_File_Name, Success);
843+
else
844+
Config_JSON := Read (Read_File (JSON_Config_File_Name).all);
848845

849-
Config_JSON := Read (Read_File (JSON_Config_File_Name).all);
850-
if not Config_JSON.Success then
851-
Error ("can not parse the rule config JSON file");
852-
Rule_Option_Problem_Detected := True;
853-
return;
854-
end if;
846+
-- If the JSON parsing failed, it means that LKQL rule file
847+
-- processing failed and diagnostics are in the output file.
848+
if not Config_JSON.Success then
849+
Analyze_Output (JSON_Config_File_Name, Success);
850+
Rule_Option_Problem_Detected := True;
855851

856-
-- Populate the global rule table with the rule config
857-
Map_JSON_Object (Config_JSON.Value, Rule_Object_Mapper'Access);
852+
-- Else, populate the global rule table with the rule config
853+
else
854+
Map_JSON_Object (Config_JSON.Value, Rule_Object_Mapper'Access);
855+
end if;
858856

859-
-- Delete the temporary JSON files if not it debug mode
860-
if not Arg.Debug_Mode.Get then
861-
Delete_File (JSON_Config_File_Name, Success);
862-
Delete_File (Error_File_Name, Success);
857+
-- Delete the temporary JSON files if not it debug mode
858+
if not Arg.Debug_Mode.Get then
859+
Delete_File (JSON_Config_File_Name, Success);
860+
end if;
863861
end if;
864862
end Process_LKQL_Rule_File;
865863

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/GNATCheckWorker.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,19 @@ protected int executeScript(Context.Builder contextBuilder) {
185185

186186
// If a LKQL rule config file has been provided, parse it and display the result
187187
if (this.args.lkqlConfigFile != null) {
188-
System.out.println(
189-
new JSONObject(
190-
parseLKQLRuleFile(this.args.lkqlConfigFile).entrySet().stream()
191-
.map(e -> Map.entry(e.getKey(), e.getValue().toJson()))
192-
.collect(
193-
Collectors.toMap(
194-
Map.Entry::getKey, Map.Entry::getValue))));
188+
try {
189+
final var instances = parseLKQLRuleFile(this.args.lkqlConfigFile);
190+
final var jsonInstances =
191+
new JSONObject(
192+
instances.entrySet().stream()
193+
.map(e -> Map.entry(e.getKey(), e.getValue().toJson()))
194+
.collect(
195+
Collectors.toMap(
196+
Map.Entry::getKey, Map.Entry::getValue)));
197+
System.out.println(jsonInstances);
198+
} catch (LKQLRuleFileError e) {
199+
System.out.println(e.getMessage());
200+
}
195201
return 0;
196202
}
197203

@@ -222,7 +228,12 @@ protected int executeScript(Context.Builder contextBuilder) {
222228
final Map<String, RuleInstance> instances = new HashMap<>();
223229
for (var rulesFrom : this.args.rulesFroms) {
224230
if (!rulesFrom.isEmpty()) {
225-
instances.putAll(parseLKQLRuleFile(rulesFrom));
231+
try {
232+
instances.putAll(parseLKQLRuleFile(rulesFrom));
233+
} catch (LKQLRuleFileError e) {
234+
System.out.println(e.getMessage());
235+
return 0;
236+
}
226237
}
227238
}
228239
optionsBuilder.ruleInstances(instances);
@@ -237,7 +248,7 @@ protected int executeScript(Context.Builder contextBuilder) {
237248
executable.executeVoid(true);
238249
return 0;
239250
} catch (Exception e) {
240-
System.out.println("WORKER_FATAL_ERROR: " + e.getMessage());
251+
System.out.println(e.getMessage());
241252
return 0;
242253
}
243254
}
@@ -253,16 +264,28 @@ protected List<String> preprocessArguments(
253264

254265
// ----- Option parsing helpers -----
255266

256-
/** Emit a formatted error when there is an invalid LKQL rule file. */
257-
private static void errorInLKQLRuleFile(final String lkqlRuleFile, final String message) {
258-
System.err.println("WORKER_FATAL_ERROR: " + message + " (" + lkqlRuleFile + ")");
267+
/** Throws an exception with the given message, related ot the provided LKQL file name. */
268+
private static void errorInLKQLRuleFile(final String lkqlRuleFile, final String message)
269+
throws LKQLRuleFileError {
270+
errorInLKQLRuleFile(lkqlRuleFile, message, true);
271+
}
272+
273+
private static void errorInLKQLRuleFile(
274+
final String lkqlRuleFile, final String message, final boolean addTag)
275+
throws LKQLRuleFileError {
276+
throw new LKQLRuleFileError(
277+
(addTag ? "WORKER_ERROR: " : "") + message + " (" + lkqlRuleFile + ")");
259278
}
260279

261280
/**
262-
* Read the given LKQL file and parse it as a rule configuration file to return the extracted
263-
* instances.
281+
* Read the given LKQL file and parse it as a rule configuration file to return the list of
282+
* instances defined in it.
283+
*
284+
* @throws LKQLRuleFileError If there is any error in the provided LKQL rule file, preventing
285+
* the analysis to go further.
264286
*/
265-
private static Map<String, RuleInstance> parseLKQLRuleFile(final String lkqlRuleFileName) {
287+
private static Map<String, RuleInstance> parseLKQLRuleFile(final String lkqlRuleFileName)
288+
throws LKQLRuleFileError {
266289
final File lkqlFile = new File(lkqlRuleFileName);
267290
final String lkqlFileBasename = lkqlFile.getName();
268291
final Map<String, RuleInstance> res = new HashMap<>();
@@ -313,9 +336,10 @@ private static Map<String, RuleInstance> parseLKQLRuleFile(final String lkqlRule
313336
}
314337
} catch (IOException e) {
315338
errorInLKQLRuleFile(lkqlFileBasename, "Could not read file");
339+
} catch (LKQLRuleFileError e) {
340+
throw e;
316341
} catch (Exception e) {
317-
errorInLKQLRuleFile(
318-
lkqlFileBasename, "Error during file processing: " + e.getMessage());
342+
errorInLKQLRuleFile(lkqlFileBasename, e.getMessage(), false);
319343
}
320344
return res;
321345
}
@@ -328,7 +352,8 @@ private static void processInstancesObject(
328352
final String lkqlRuleFile,
329353
final Value instancesObject,
330354
final RuleInstance.SourceMode sourceMode,
331-
final Map<String, RuleInstance> toPopulate) {
355+
final Map<String, RuleInstance> toPopulate)
356+
throws LKQLRuleFileError {
332357
// Iterate on all instance object keys
333358
for (String ruleName : instancesObject.getMemberKeys()) {
334359
final String lowerRuleName = ruleName.toLowerCase();
@@ -385,7 +410,8 @@ private static void processArgsObject(
385410
final Value argsObject,
386411
final RuleInstance.SourceMode sourceMode,
387412
final String ruleName,
388-
final Map<String, RuleInstance> toPopulate) {
413+
final Map<String, RuleInstance> toPopulate)
414+
throws LKQLRuleFileError {
389415
// Ensure that the given value has members (is an object)
390416
if (!argsObject.hasMembers()) {
391417
errorInLKQLRuleFile(lkqlRuleFile, "Arguments should be in an object value");
@@ -439,6 +465,15 @@ private static boolean acceptSoleArgs(final String ruleName) {
439465
return List.of("style_checks", "warnings").contains(ruleName);
440466
}
441467

468+
// ----- Inner classes -----
469+
470+
/** An exception to throw while analysing an LKQL rule file. */
471+
static final class LKQLRuleFileError extends Exception {
472+
public LKQLRuleFileError(String message) {
473+
super(message);
474+
}
475+
}
476+
442477
// ----- The LKQL checker -----
443478

444479
public static final String checkerSource =

lkql_jit/language/src/main/java/com/adacore/lkql_jit/checker/utils/CheckerUtils.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -280,19 +280,34 @@ public String diagnostic(
280280
SourceLocation adaErrorLocation,
281281
SourceLocation lkqlErrorLocation,
282282
String ruleName) {
283+
// If there is an Ada location for this diagnostic, emit it as a GNU formatted one
284+
if (adaErrorLocation != null) {
285+
var adaLoc = adaErrorLocation.display(true) + ": ";
286+
var lkqlLoc =
287+
lkqlErrorLocation != null
288+
? "internal issue at " + lkqlErrorLocation.display(true) + ": "
289+
: "";
290+
var rulePart =
291+
ruleName == null || ruleName.isBlank()
292+
? ""
293+
: " [" + ruleName.toLowerCase() + "]";
294+
295+
return adaLoc + kindtoString(messageKind) + ": " + lkqlLoc + message + rulePart;
296+
}
283297

284-
var adaLoc =
285-
adaErrorLocation != null ? adaErrorLocation.display(true) + ": " : "null:0:0: ";
286-
var lkqlLoc =
287-
lkqlErrorLocation != null
288-
? "internal issue at " + lkqlErrorLocation.display(true) + ": "
289-
: "";
290-
var rulePart =
291-
ruleName == null || ruleName.isBlank()
292-
? ""
293-
: " [" + ruleName.toLowerCase() + "]";
298+
// Else, emit a "worker error / warning" one
299+
else {
300+
var prefix = kindToWorkerPrefix(messageKind);
301+
var lkqlLoc =
302+
lkqlErrorLocation != null ? lkqlErrorLocation.display(true) + ": " : "";
294303

295-
return adaLoc + kindtoString(messageKind) + ": " + lkqlLoc + message + rulePart;
304+
if (messageKind == MessageKind.RULE_VIOLATION) {
305+
prefix = kindToWorkerPrefix(MessageKind.ERROR);
306+
message = "rule violation without Ada location: " + message;
307+
}
308+
309+
return prefix + ": " + lkqlLoc + message;
310+
}
296311
}
297312

298313
@Override
@@ -304,6 +319,14 @@ public String kindtoString(MessageKind messageKind) {
304319
};
305320
}
306321

322+
public static String kindToWorkerPrefix(MessageKind messageKind) {
323+
return switch (messageKind) {
324+
case WARNING -> "WORKER_WARNING";
325+
case ERROR -> "WORKER_ERROR";
326+
case RULE_VIOLATION -> "";
327+
};
328+
}
329+
307330
@Override
308331
public void emitFileNotFound(SourceLocation from, String fileName, boolean isError) {
309332
this.emitDiagnostic(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val rules = @{
2+
Goto_Statements,
3+
}

testsuite/tests/gnatcheck_errors/invalid_lkql_rules_config/test.out

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1+
Invalid syntax
2+
==============
3+
4+
gnatcheck: invalid_syntax.lkql:1:01: Cannot parse <val_decl>
5+
gnatcheck: invalid_syntax.lkql:3:01: Expected Upper_Identifier, got '}'
6+
gnatcheck: invalid_syntax.lkql:3:01: End of input expected, got "R_Curl"
7+
gnatcheck: Syntax errors in invalid_syntax.lkql: stopping interpreter (invalid_syntax.lkql)
8+
gnatcheck: No rule to check specified
9+
try "gnatcheck --help" for more information.
10+
>>>program returned status code 2
11+
112
Invalid LKQL semantic
213
=====================
314

4-
gnatcheck: error raised by the worker: Error during file processing: null:0:0: error: internal issue at invalid_semantic.lkql:1:25: Type error: expected Int but got Bool (invalid_semantic.lkql)
15+
gnatcheck: invalid_semantic.lkql:1:25: Type error: expected Int but got Bool (invalid_semantic.lkql)
516
gnatcheck: No rule to check specified
617
try "gnatcheck --help" for more information.
718
>>>program returned status code 2
819

920
No 'rules' object
1021
=================
1122

12-
gnatcheck: error raised by the worker: LKQL config file must define a 'rules' top level object value (no_rules_object.lkql)
23+
gnatcheck: LKQL config file must define a 'rules' top level object value (no_rules_object.lkql)
1324
gnatcheck: No rule to check specified
1425
try "gnatcheck --help" for more information.
1526
>>>program returned status code 2
@@ -32,15 +43,15 @@ try "gnatcheck --help" for more information.
3243
Not valid arguments
3344
===================
3445

35-
gnatcheck: error raised by the worker: Rule arguments must be an object or indexable value (not_valid_args.lkql)
46+
gnatcheck: Rule arguments must be an object or indexable value (not_valid_args.lkql)
3647
gnatcheck: No rule to check specified
3748
try "gnatcheck --help" for more information.
3849
>>>program returned status code 2
3950

4051
Not valid arguments element
4152
===========================
4253

43-
gnatcheck: error raised by the worker: Arguments should be in an object value (not_valid_args_elem.lkql)
54+
gnatcheck: Arguments should be in an object value (not_valid_args_elem.lkql)
4455
gnatcheck: No rule to check specified
4556
try "gnatcheck --help" for more information.
4657
>>>program returned status code 2
@@ -70,4 +81,7 @@ gnatcheck: extra argument for instance goto_statements: 'extra' (extra_param.lkq
7081
Multiple instance with the same name
7182
====================================
7283

73-
gnatcheck: error raised by the worker: Multiple instances with the same name: an_instance (instance_names.lkql)
84+
gnatcheck: Multiple instances with the same name: an_instance (instance_names.lkql)
85+
gnatcheck: No rule to check specified
86+
try "gnatcheck --help" for more information.
87+
>>>program returned status code 2

testsuite/tests/gnatcheck_errors/invalid_lkql_rules_config/test.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ input_sources:
44
format: brief
55

66
tests:
7+
- label: Invalid syntax
8+
lkql_rule_file: invalid_syntax.lkql
79
- label: Invalid LKQL semantic
810
lkql_rule_file: invalid_semantic.lkql
911
- label: No 'rules' object

0 commit comments

Comments
 (0)