Skip to content

Commit f243bb1

Browse files
committed
Merge branch 'main' into context-history
2 parents 8dddf47 + 1bd08b4 commit f243bb1

28 files changed

+405
-90
lines changed

.github/workflows/maven.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
4949
# Build the project (compiles and packages the code)
5050
- name: Build the project
51-
run: mvn -B clean install --file pom.xml --batch-mode --fail-fast -Dgpg.skip=true -Dmaven.deploy.skip=true
51+
run: mvn -B clean install -DskipTests --file pom.xml --batch-mode --fail-fast -Dgpg.skip=true -Dmaven.deploy.skip=true
5252

5353
# Run tests
5454
- name: Run tests
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.Refinement;
4+
5+
@SuppressWarnings("unused")
6+
public class CorrectChars {
7+
8+
@Refinement("_ == 65")
9+
int getA() {
10+
return 'A';
11+
}
12+
13+
void test() {
14+
printLetter('A');
15+
printLetter('Z');
16+
printLetter('a');
17+
printLetter('z');
18+
}
19+
20+
void printLetter(@Refinement("_ >= 65 && _ <= 90 || _ >= 97 && _ <= 122") char c) {
21+
System.out.println(c);
22+
}
23+
24+
void test2() {
25+
@Refinement("_ == 97")
26+
char c = 'a';
27+
28+
@Refinement("_ == 98")
29+
int res = inc(c);
30+
}
31+
32+
@Refinement("_ == x + 1")
33+
int inc(int x) {
34+
return x + 1;
35+
}
36+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
public class ErrorChars {
7+
8+
void test() {
9+
printLetter('$'); // error
10+
}
11+
12+
void printLetter(@Refinement("_ >= 65 && _ <= 90 || _ >= 97 && _ <= 122") char c) {
13+
System.out.println(c);
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Invalid Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
@SuppressWarnings("unused")
7+
public class ErrorInvalidRefinement {
8+
9+
void test() {
10+
@Refinement("x")
11+
int x = 0;
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Invalid Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
public class ErrorInvalidRefinementParameter {
7+
8+
void testInvalidRefinement(@Refinement("y + 1") int y) {}
9+
10+
int otherMethod() {
11+
return -1;
12+
}
13+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Invalid Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
public class ErrorInvalidRefinementReturn {
7+
8+
@Refinement("_ * 2")
9+
void test() {
10+
}
11+
}

liquidjava-verifier/src/main/java/liquidjava/diagnostics/LJDiagnostic.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,26 +108,34 @@ public String getSnippet() {
108108

109109
for (int i = startLine; i <= endLine; i++) {
110110
String lineNumStr = String.format("%" + padding + "d", i);
111-
String line = lines.get(i - 1);
111+
String rawLine = lines.get(i - 1);
112+
String line = rawLine.replace("\t", " ");
112113

113114
// add line
114115
sb.append(Colors.GREY).append(lineNumStr).append(PIPE).append(line).append(Colors.RESET).append("\n");
115116

116117
// add error markers on the line(s) with the error
117118
if (i >= position.lineStart() && i <= position.lineEnd()) {
118119
int colStart = (i == position.lineStart()) ? position.colStart() : 1;
119-
int colEnd = (i == position.lineEnd()) ? position.colEnd() : line.length();
120+
int colEnd = (i == position.lineEnd()) ? position.colEnd() : rawLine.length();
120121

121122
if (colStart > 0 && colEnd > 0) {
123+
int tabsBeforeStart = (int) rawLine.substring(0, Math.max(0, colStart - 1)).chars()
124+
.filter(ch -> ch == '\t').count();
125+
int tabsBeforeEnd = (int) rawLine.substring(0, Math.max(0, colEnd)).chars()
126+
.filter(ch -> ch == '\t').count();
127+
int visualColStart = colStart + tabsBeforeStart * 3;
128+
int visualColEnd = colEnd + tabsBeforeEnd * 3;
129+
122130
// line number padding + pipe + column offset
123131
String indent = " ".repeat(padding) + Colors.GREY + PIPE + Colors.RESET
124-
+ " ".repeat(colStart - 1);
125-
String markers = accentColor + "^".repeat(Math.max(1, colEnd - colStart + 1));
132+
+ " ".repeat(visualColStart - 1);
133+
String markers = accentColor + "^".repeat(Math.max(1, visualColEnd - visualColStart + 1));
126134
sb.append(indent).append(markers);
127135

128136
// custom message
129137
if (customMessage != null && !customMessage.isBlank()) {
130-
String offset = " ".repeat(padding + colEnd + PIPE.length() + 1);
138+
String offset = " ".repeat(padding + visualColEnd + PIPE.length() + 1);
131139
sb.append(" " + customMessage.replace("\n", "\n" + offset));
132140
}
133141
sb.append(Colors.RESET).append("\n");

liquidjava-verifier/src/main/java/liquidjava/diagnostics/errors/InvalidRefinementError.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class InvalidRefinementError extends LJError {
1212
private final String refinement;
1313

1414
public InvalidRefinementError(SourcePosition position, String message, String refinement) {
15-
super("Invalid Refinement", message, position, null);
15+
super("Invalid Refinement Error", message, position, null);
1616
this.refinement = refinement;
1717
}
1818

liquidjava-verifier/src/main/java/liquidjava/diagnostics/errors/RefinementError.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package liquidjava.diagnostics.errors;
22

33
import liquidjava.diagnostics.TranslationTable;
4-
import liquidjava.rj_language.ast.Expression;
54
import liquidjava.rj_language.opt.derivation_node.ValDerivationNode;
5+
import liquidjava.smt.Counterexample;
66
import spoon.reflect.cu.SourcePosition;
77

88
/**
@@ -14,13 +14,39 @@ public class RefinementError extends LJError {
1414

1515
private final ValDerivationNode expected;
1616
private final ValDerivationNode found;
17+
private final Counterexample counterexample;
1718

1819
public RefinementError(SourcePosition position, ValDerivationNode expected, ValDerivationNode found,
19-
TranslationTable translationTable, String customMessage) {
20+
TranslationTable translationTable, Counterexample counterexample, String customMessage) {
2021
super("Refinement Error", String.format("%s is not a subtype of %s", found.getValue(), expected.getValue()),
2122
position, translationTable, customMessage);
2223
this.expected = expected;
2324
this.found = found;
25+
this.counterexample = counterexample;
26+
}
27+
28+
@Override
29+
public String getDetails() {
30+
return getCounterexampleString();
31+
}
32+
33+
private String getCounterexampleString() {
34+
if (counterexample == null || counterexample.assignments().isEmpty())
35+
return "";
36+
37+
// filter fresh variables and join assignements with &&
38+
String counterexampleExp = counterexample.assignments().stream().filter(a -> !a.startsWith("#fresh_"))
39+
.reduce((a, b) -> a + " && " + b).orElse("");
40+
41+
// check if counterexample is trivial (same as the found value)
42+
if (counterexampleExp.equals(found.getValue().toString()))
43+
return "";
44+
45+
return "Counterexample: " + counterexampleExp;
46+
}
47+
48+
public Counterexample getCounterexample() {
49+
return counterexample;
2450
}
2551

2652
public ValDerivationNode getExpected() {

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/MethodsFirstChecker.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,24 @@ public <T> void visitCtInterface(CtInterface<T> intrface) {
9696
@Override
9797
public <T> void visitCtConstructor(CtConstructor<T> c) {
9898
context.enterContext();
99-
getRefinementFromAnnotation(c);
100-
mfc.getConstructorRefinements(c);
101-
super.visitCtConstructor(c);
99+
try {
100+
getRefinementFromAnnotation(c);
101+
mfc.getConstructorRefinements(c);
102+
super.visitCtConstructor(c);
103+
} catch (LJError e) {
104+
diagnostics.add(e);
105+
}
102106
context.exitContext();
103107
}
104108

105109
public <R> void visitCtMethod(CtMethod<R> method) {
106110
context.enterContext();
107-
mfc.getMethodRefinements(method);
108-
super.visitCtMethod(method);
111+
try {
112+
mfc.getMethodRefinements(method);
113+
super.visitCtMethod(method);
114+
} catch (LJError e) {
115+
diagnostics.add(e);
116+
}
109117
context.exitContext();
110118
}
111119
}

0 commit comments

Comments
 (0)