Skip to content

Commit 31b657d

Browse files
authored
Add Diagnostics Classes (#81)
1 parent ecea182 commit 31b657d

32 files changed

+623
-53
lines changed

liquidjava-verifier/src/main/java/liquidjava/api/CommandLineLauncher.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import java.io.File;
44
import java.util.Arrays;
55
import java.util.List;
6-
import liquidjava.errors.ErrorEmitter;
6+
7+
import liquidjava.diagnostics.ErrorEmitter;
78
import liquidjava.processor.RefinementProcessor;
89
import spoon.Launcher;
910
import spoon.processing.ProcessingManager;

liquidjava-verifier/src/main/java/liquidjava/errors/ErrorEmitter.java renamed to liquidjava-verifier/src/main/java/liquidjava/diagnostics/ErrorEmitter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package liquidjava.errors;
1+
package liquidjava.diagnostics;
22

33
import java.net.URI;
44
import java.util.HashMap;

liquidjava-verifier/src/main/java/liquidjava/errors/ErrorHandler.java renamed to liquidjava-verifier/src/main/java/liquidjava/diagnostics/ErrorHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package liquidjava.errors;
1+
package liquidjava.diagnostics;
22

33
import java.util.Formatter;
44
import java.util.HashMap;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package liquidjava.diagnostics;
2+
3+
import spoon.reflect.cu.SourcePosition;
4+
5+
public class ErrorPosition {
6+
7+
private int lineStart;
8+
private int colStart;
9+
private int lineEnd;
10+
private int colEnd;
11+
12+
public ErrorPosition(int lineStart, int colStart, int lineEnd, int colEnd) {
13+
this.lineStart = lineStart;
14+
this.colStart = colStart;
15+
this.lineEnd = lineEnd;
16+
this.colEnd = colEnd;
17+
}
18+
19+
public int getLineStart() {
20+
return lineStart;
21+
}
22+
23+
public int getColStart() {
24+
return colStart;
25+
}
26+
27+
public int getLineEnd() {
28+
return lineEnd;
29+
}
30+
31+
public int getColEnd() {
32+
return colEnd;
33+
}
34+
35+
public static ErrorPosition fromSpoonPosition(SourcePosition pos) {
36+
if (pos == null || !pos.isValidPosition())
37+
return null;
38+
return new ErrorPosition(pos.getLine(), pos.getColumn(), pos.getEndLine(), pos.getEndColumn());
39+
}
40+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package liquidjava.diagnostics;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
6+
import liquidjava.diagnostics.errors.LJError;
7+
import liquidjava.diagnostics.warnings.LJWarning;
8+
import liquidjava.processor.context.PlacementInCode;
9+
10+
public class LJDiagnostics {
11+
private static LJDiagnostics instance;
12+
13+
private ArrayList<LJError> errors;
14+
private ArrayList<LJWarning> warnings;
15+
private HashMap<String, PlacementInCode> translationMap;
16+
17+
private LJDiagnostics() {
18+
this.errors = new ArrayList<>();
19+
this.warnings = new ArrayList<>();
20+
this.translationMap = new HashMap<>();
21+
}
22+
23+
public static LJDiagnostics getInstance() {
24+
if (instance == null)
25+
instance = new LJDiagnostics();
26+
return instance;
27+
}
28+
29+
public void addError(LJError error) {
30+
this.errors.add(error);
31+
}
32+
33+
public void addWarning(LJWarning warning) {
34+
this.warnings.add(warning);
35+
}
36+
37+
public void setTranslationMap(HashMap<String, PlacementInCode> map) {
38+
this.translationMap = map;
39+
}
40+
41+
public boolean foundError() {
42+
return !this.errors.isEmpty();
43+
}
44+
45+
public boolean foundWarning() {
46+
return !this.warnings.isEmpty();
47+
}
48+
49+
public ArrayList<LJError> getErrors() {
50+
return this.errors;
51+
}
52+
53+
public ArrayList<LJWarning> getWarnings() {
54+
return this.warnings;
55+
}
56+
57+
public HashMap<String, PlacementInCode> getTranslationMap() {
58+
return this.translationMap;
59+
}
60+
61+
public LJError getError() {
62+
return foundError() ? this.errors.get(0) : null;
63+
}
64+
65+
public LJWarning getWarning() {
66+
return foundWarning() ? this.warnings.get(0) : null;
67+
}
68+
69+
public void clear() {
70+
this.errors.clear();
71+
this.warnings.clear();
72+
this.translationMap.clear();
73+
}
74+
75+
@Override
76+
public String toString() {
77+
StringBuilder sb = new StringBuilder();
78+
if (foundError()) {
79+
for (LJError error : errors) {
80+
sb.append(error.toString()).append("\n");
81+
}
82+
} else {
83+
if (foundWarning()) {
84+
sb.append("Warnings:\n");
85+
for (LJWarning warning : warnings) {
86+
sb.append(warning.getMessage()).append("\n");
87+
}
88+
sb.append("Passed Verification!\n");
89+
}
90+
}
91+
return sb.toString();
92+
}
93+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package liquidjava.diagnostics.errors;
2+
3+
/**
4+
* Custom error with an arbitrary message
5+
*
6+
* @see LJError
7+
*/
8+
public class CustomError extends LJError {
9+
10+
public CustomError(String message) {
11+
super("Found Error", message, null);
12+
}
13+
14+
@Override
15+
public String toString() {
16+
return super.toString(null);
17+
}
18+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package liquidjava.diagnostics.errors;
2+
3+
import liquidjava.rj_language.Predicate;
4+
import spoon.reflect.declaration.CtElement;
5+
6+
/**
7+
* Error indicating that a ghost method invocation is invalid (e.g., has wrong arguments)
8+
*
9+
* @see LJError
10+
*/
11+
public class GhostInvocationError extends LJError {
12+
13+
private Predicate expected;
14+
15+
public GhostInvocationError(CtElement element, Predicate expected) {
16+
super("Ghost Invocation Error", "Invalid types or number of arguments in ghost invocation", element);
17+
this.expected = expected;
18+
}
19+
20+
public Predicate getExpected() {
21+
return expected;
22+
}
23+
24+
@Override
25+
public String toString() {
26+
StringBuilder sb = new StringBuilder();
27+
sb.append("Expected: ").append(expected.toString()).append("\n");
28+
return super.toString(sb.toString());
29+
}
30+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package liquidjava.diagnostics.errors;
2+
3+
import spoon.reflect.declaration.CtElement;
4+
5+
/**
6+
* Error indicating that a constructor contains a state refinement with a 'from' state, which is not allowed
7+
*
8+
* @see LJError
9+
*/
10+
public class IllegalConstructorTransitionError extends LJError {
11+
12+
public IllegalConstructorTransitionError(CtElement element) {
13+
super("Illegal Constructor Transition Error",
14+
"Found constructor with 'from' state (should only have a 'to' state)", element);
15+
}
16+
17+
@Override
18+
public String toString() {
19+
return super.toString(null);
20+
}
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package liquidjava.diagnostics.errors;
2+
3+
import spoon.reflect.declaration.CtElement;
4+
5+
/**
6+
* Error indicating that a refinement is invalid (e.g., not a boolean expression)
7+
*
8+
* @see LJError
9+
*/
10+
public class InvalidRefinementError extends LJError {
11+
12+
private String refinement;
13+
14+
public InvalidRefinementError(String message, CtElement element, String refinement) {
15+
super("Invalid Refinement", message, element);
16+
this.refinement = refinement;
17+
}
18+
19+
public String getRefinement() {
20+
return refinement;
21+
}
22+
23+
@Override
24+
public String toString() {
25+
StringBuilder sb = new StringBuilder();
26+
sb.append("Refinement: ").append(refinement).append("\n");
27+
return super.toString(sb.toString());
28+
}
29+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package liquidjava.diagnostics.errors;
2+
3+
import liquidjava.diagnostics.ErrorPosition;
4+
import liquidjava.utils.Utils;
5+
import spoon.reflect.cu.SourcePosition;
6+
import spoon.reflect.declaration.CtElement;
7+
8+
/**
9+
* Base class for all LiquidJava errors
10+
*/
11+
public abstract class LJError extends Exception {
12+
13+
private String title;
14+
private String message;
15+
private CtElement element;
16+
private ErrorPosition position;
17+
private SourcePosition location;
18+
19+
public LJError(String title, String message, CtElement element) {
20+
super(message);
21+
this.title = title;
22+
this.message = message;
23+
this.element = element;
24+
try {
25+
this.location = element.getPosition();
26+
this.position = ErrorPosition.fromSpoonPosition(element.getPosition());
27+
} catch (Exception e) {
28+
this.location = null;
29+
this.position = null;
30+
}
31+
}
32+
33+
public String getTitle() {
34+
return title;
35+
}
36+
37+
public String getMessage() {
38+
return message;
39+
}
40+
41+
public CtElement getElement() {
42+
return element;
43+
}
44+
45+
public ErrorPosition getPosition() {
46+
return position;
47+
}
48+
49+
public SourcePosition getLocation() {
50+
return location;
51+
}
52+
53+
@Override
54+
public abstract String toString();
55+
56+
public String toString(String extra) {
57+
StringBuilder sb = new StringBuilder();
58+
sb.append(title).append(" at: \n").append(element.toString().replace("@liquidjava.specification.", "@"))
59+
.append("\n\n");
60+
sb.append(message).append("\n");
61+
if (extra != null)
62+
sb.append(extra).append("\n");
63+
sb.append("Location: ").append(location != null ? Utils.stripParens(location.toString()) : "<unknown>")
64+
.append("\n");
65+
return sb.toString();
66+
}
67+
}

0 commit comments

Comments
 (0)