Skip to content

Commit d079005

Browse files
Treat operations and fields with unresolved types as unrefined (#388, #389, #390)
When a class is not on the classpath (the usual case when one file of a project is verified), Spoon leaves the type of operations on its values, and the declaring type of its fields, unset, and the verifier crashed on them. - A binary operation whose type, or the type of one of its operands, is unknown is now a fresh unknown boolean for comparisons and logical operators, and is left unrefined otherwise; an operator assignment with such a right-hand side gives the variable an unknown value. - A negation of an operand with an unknown type is boolean; other unary operators on such operands are left unrefined. - A field whose declaring type is unknown is qualified with its name only, so it matches no known field, and is not treated as an enum constant. Unknown values carry no information, so a refinement that depends on them is reported as not provable rather than accepted; violations next to them are still reported (Error* fixtures). The example module excludes the *Unresolved* test cases from javac, as they import missing classes on purpose. Fixes #388 Fixes #389 Fixes #390 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent ad055b4 commit d079005

11 files changed

Lines changed: 279 additions & 57 deletions

File tree

‎liquidjava-example/pom.xml‎

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
1-
<project xmlns="http://maven.apache.org/POM/4.0.0"
2-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4-
5-
<modelVersion>4.0.0</modelVersion>
6-
<groupId>liquidjava-example</groupId>
7-
<artifactId>liquidjava-example</artifactId>
8-
<version>0.0.1-SNAPSHOT</version>
9-
<name>liquidjava-example</name>
10-
<packaging>jar</packaging>
11-
12-
<build>
13-
<plugins>
14-
<plugin>
15-
<groupId>org.apache.maven.plugins</groupId>
16-
<artifactId>maven-compiler-plugin</artifactId>
17-
<version>${pVersion.compiler}</version>
18-
<configuration>
19-
<source>20</source>
20-
<target>20</target>
21-
</configuration>
22-
</plugin>
23-
</plugins>
24-
</build>
25-
26-
<properties>
27-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
28-
<maven.compiler.source>20</maven.compiler.source>
29-
<maven.compiler.target>20</maven.compiler.target>
30-
<!-- versions -->
31-
<version.junit>4.13.1</version.junit>
32-
<version.memcompiler>1.3.0</version.memcompiler>
33-
<!-- plugin versions -->
34-
<pVersion.compiler>3.8.0</pVersion.compiler>
35-
<pVersion.surefire>2.22.1</pVersion.surefire>
36-
</properties>
37-
38-
<dependencies>
39-
<dependency>
40-
<groupId>org.mdkt.compiler</groupId>
41-
<artifactId>InMemoryJavaCompiler</artifactId>
42-
<version>${version.memcompiler}</version>
43-
</dependency>
44-
<dependency>
45-
<groupId>io.github.liquid-java</groupId>
46-
<artifactId>liquidjava-api</artifactId>
47-
<version>0.0.7</version>
48-
</dependency>
49-
</dependencies>
50-
</project>
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>liquidjava-example</groupId>
7+
<artifactId>liquidjava-example</artifactId>
8+
<version>0.0.1-SNAPSHOT</version>
9+
<name>liquidjava-example</name>
10+
<packaging>jar</packaging>
11+
12+
<build>
13+
<plugins>
14+
<plugin>
15+
<groupId>org.apache.maven.plugins</groupId>
16+
<artifactId>maven-compiler-plugin</artifactId>
17+
<version>${pVersion.compiler}</version>
18+
<configuration>
19+
<source>20</source>
20+
<target>20</target>
21+
<excludes>
22+
<!-- test cases that use classes missing from the classpath on purpose -->
23+
<exclude>testSuite/**/*Unresolved*.java</exclude>
24+
</excludes>
25+
</configuration>
26+
</plugin>
27+
</plugins>
28+
</build>
29+
30+
<properties>
31+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
32+
<maven.compiler.source>20</maven.compiler.source>
33+
<maven.compiler.target>20</maven.compiler.target>
34+
<!-- versions -->
35+
<version.junit>4.13.1</version.junit>
36+
<version.memcompiler>1.3.0</version.memcompiler>
37+
<!-- plugin versions -->
38+
<pVersion.compiler>3.8.0</pVersion.compiler>
39+
<pVersion.surefire>2.22.1</pVersion.surefire>
40+
</properties>
41+
42+
<dependencies>
43+
<dependency>
44+
<groupId>org.mdkt.compiler</groupId>
45+
<artifactId>InMemoryJavaCompiler</artifactId>
46+
<version>${version.memcompiler}</version>
47+
</dependency>
48+
<dependency>
49+
<groupId>io.github.liquid-java</groupId>
50+
<artifactId>liquidjava-api</artifactId>
51+
<version>0.0.7</version>
52+
</dependency>
53+
</dependencies>
54+
</project>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package testSuite;
2+
3+
import org.example.missing.Item;
4+
import org.example.missing.Interrupts;
5+
6+
import liquidjava.specification.Refinement;
7+
8+
// Operations whose operand types cannot be resolved (classes not on the classpath) are unrefined (#388, #344)
9+
@SuppressWarnings("unused")
10+
public class CorrectUnresolvedBinaryOperator {
11+
static boolean same(Item a, Item b) {
12+
if (a.id() == b.id()) {
13+
return true;
14+
}
15+
return false;
16+
}
17+
18+
static boolean comparedToConstant(Item p, int id) {
19+
return p.getValue() == id;
20+
}
21+
22+
static void inLoop(Item embedding) {
23+
for (int i = 0; i < embedding.length(); i++) {
24+
}
25+
}
26+
27+
static void withKnownOperand(boolean someHandlerDefined) {
28+
if (!someHandlerDefined || Interrupts.isInterruptsDisabled()) {
29+
return;
30+
}
31+
}
32+
33+
static boolean nested(Item x) {
34+
return x.a() + 1 > 0;
35+
}
36+
37+
static String concatenation(Item i) {
38+
return "Configuration is not an SQL config:" + i.name();
39+
}
40+
41+
static void unknownVarNullCheck(Item x) {
42+
var v = x.a();
43+
if (v == null) {
44+
return;
45+
}
46+
}
47+
48+
static void operatorAssignment(Item x) {
49+
int c = 0;
50+
c += x.a();
51+
}
52+
53+
static void knownValuesStillUsed(Item x, int k) {
54+
if (k > 0 && x.ok()) {
55+
@Refinement("_ > 0")
56+
int y = 1;
57+
}
58+
}
59+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package testSuite;
2+
3+
import org.example.missing.Config;
4+
5+
// Fields whose declaring class cannot be resolved (#390)
6+
@SuppressWarnings("unused")
7+
public class CorrectUnresolvedFieldDeclaringType {
8+
private static final int MIN_ADMIN_PASSWORD_LENGTH = 8;
9+
10+
static boolean shortPassword(Config config) {
11+
return config.httpAdminPassword.length() < MIN_ADMIN_PASSWORD_LENGTH;
12+
}
13+
14+
void undeclaredField(Exception e) {
15+
log.error("get host name error:{}", e.getMessage()); // e.g., a field generated by Lombok
16+
}
17+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package testSuite;
2+
3+
import org.example.missing.Item;
4+
import org.example.missing.Settings;
5+
6+
// A negation of a call whose type cannot be resolved is an unknown boolean (#389)
7+
@SuppressWarnings("unused")
8+
public class CorrectUnresolvedUnaryOperator {
9+
static void start(Settings settings) {
10+
if (!settings.enabled()) {
11+
return;
12+
}
13+
}
14+
15+
static boolean negatedComparison(Item x) {
16+
return !(x.a() == x.b());
17+
}
18+
19+
static int negated(Item x) {
20+
return -x.a();
21+
}
22+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package testSuite;
2+
3+
import org.example.missing.Item;
4+
5+
import liquidjava.specification.Refinement;
6+
7+
// A violation under a comparison of values whose types cannot be resolved is still reported (#388)
8+
@SuppressWarnings("unused")
9+
public class ErrorUnresolvedBinaryOperator {
10+
static void positive(@Refinement("_ > 0") int x) {
11+
}
12+
13+
static void comparison(Item a, Item b) {
14+
if (a.id() == b.id()) {
15+
positive(0); // Expect: Refinement Error
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package testSuite;
2+
3+
import org.example.missing.Config;
4+
5+
import liquidjava.specification.Refinement;
6+
7+
// A violation next to a field whose declaring class cannot be resolved is still reported (#390)
8+
@SuppressWarnings("unused")
9+
public class ErrorUnresolvedFieldDeclaringType {
10+
static void positive(@Refinement("_ > 0") int x) {
11+
}
12+
13+
static void field(Config config) {
14+
if (config.httpAdminPassword.length() < 8) {
15+
positive(-2); // Expect: Refinement Error
16+
}
17+
}
18+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package testSuite;
2+
3+
import org.example.missing.Item;
4+
5+
import liquidjava.specification.Refinement;
6+
7+
// An operation whose operand types cannot be resolved has an unknown value (#388)
8+
@SuppressWarnings("unused")
9+
public class ErrorUnresolvedOperationValue {
10+
static void unknownValue(Item x) {
11+
@Refinement("_ > 0")
12+
int y = x.a() + 1; // Expect: Refinement Error
13+
}
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package testSuite;
2+
3+
import org.example.missing.Settings;
4+
5+
import liquidjava.specification.Refinement;
6+
7+
// A violation under a negation of a call whose type cannot be resolved is still reported (#389)
8+
@SuppressWarnings("unused")
9+
public class ErrorUnresolvedUnaryOperator {
10+
static void positive(@Refinement("_ > 0") int x) {
11+
}
12+
13+
static void negation(Settings settings) {
14+
if (!settings.enabled()) {
15+
positive(-1); // Expect: Refinement Error
16+
}
17+
}
18+
}

‎liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/RefinementTypeChecker.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,8 @@ public <T> void visitCtFieldRead(CtFieldRead<T> fieldRead) {
335335
String targetName = fieldRead.getTarget().toString();
336336
fieldRead.putMetadata(Keys.REFINEMENT, Predicate.createEquals(Predicate.createVar(Keys.WILDCARD),
337337
BuiltinFunctionPredicate.length(targetName, fieldRead)));
338-
} else if (fieldRead.getVariable().getDeclaringType().getDeclaration() instanceof CtEnum) {
338+
} else if (fieldRead.getVariable().getDeclaringType() != null
339+
&& fieldRead.getVariable().getDeclaringType().getDeclaration() instanceof CtEnum) {
339340
// only enums declared in the analyzed sources can be translated to SMT
340341
String target = fieldRead.getVariable().getDeclaringType().getSimpleName();
341342
String enumLiteral = String.format(Formats.ENUM, target, fieldName);

0 commit comments

Comments
 (0)