Skip to content

Commit 26bc704

Browse files
committed
Preserve enclosing class refinements across nested classes
1 parent 918f777 commit 26bc704

5 files changed

Lines changed: 83 additions & 33 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.Refinement;
4+
5+
class CorrectOuterFieldAfterNestedClass {
6+
@Refinement("_ > 0") int x = 1;
7+
8+
static class Inner { int y; }
9+
10+
@Refinement("_ > 0")
11+
public int get() { return x; }
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.Refinement;
4+
5+
class ErrorNestedFieldLeak {
6+
int x = 1;
7+
8+
static class Inner {
9+
@Refinement("_ < 0") int x = -1;
10+
}
11+
12+
@Refinement("_ < 0")
13+
public int get() { return x; } // Expect: Refinement Error
14+
}

‎liquidjava-verifier/src/main/java/liquidjava/processor/context/Context.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ public void reinitializeContext() {
4747
clearInstanceVariables();
4848
}
4949

50+
public ClassScope enterClassScope() {
51+
ClassScope scope = new ClassScope(ctxVars, ctxInstanceVars);
52+
reinitializeContext();
53+
return scope;
54+
}
55+
56+
public void exitClassScope(ClassScope scope) {
57+
ctxVars = scope.variables;
58+
ctxInstanceVars = scope.instances;
59+
}
60+
61+
public static class ClassScope {
62+
private final Stack<List<RefinedVariable>> variables;
63+
private final List<RefinedVariable> instances;
64+
65+
private ClassScope(Stack<List<RefinedVariable>> variables, List<RefinedVariable> instances) {
66+
this.variables = variables;
67+
this.instances = instances;
68+
}
69+
}
70+
5071
public void clearInstanceVariables() {
5172
ctxInstanceVars = new ArrayList<>();
5273
}

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

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,41 +32,45 @@ public MethodsFirstChecker(Context context, Factory factory) {
3232

3333
@Override
3434
public <T> void visitCtClass(CtClass<T> ctClass) {
35-
context.reinitializeContext();
3635
if (visitedClasses.contains(ctClass.getQualifiedName()))
3736
return;
3837
else
3938
visitedClasses.add(ctClass.getQualifiedName());
40-
// visitInterfaces
41-
if (!ctClass.getSuperInterfaces().isEmpty())
42-
for (CtTypeReference<?> t : ctClass.getSuperInterfaces()) {
43-
if (t.isInterface()) {
44-
CtType<?> ct = t.getDeclaration();
45-
if (ct instanceof CtInterface)
46-
visitCtInterface((CtInterface<?>) ct);
39+
Context.ClassScope scope = context.enterClassScope();
40+
try {
41+
// visitInterfaces
42+
if (!ctClass.getSuperInterfaces().isEmpty())
43+
for (CtTypeReference<?> t : ctClass.getSuperInterfaces()) {
44+
if (t.isInterface()) {
45+
CtType<?> ct = t.getDeclaration();
46+
if (ct instanceof CtInterface)
47+
visitCtInterface((CtInterface<?>) ct);
48+
}
4749
}
50+
// visitSubclasses
51+
CtTypeReference<?> sup = ctClass.getSuperclass();
52+
if (sup != null && sup.isClass()) {
53+
CtType<?> ct = sup.getDeclaration();
54+
if (ct instanceof CtClass)
55+
visitCtClass((CtClass<?>) ct);
4856
}
49-
// visitSubclasses
50-
CtTypeReference<?> sup = ctClass.getSuperclass();
51-
if (sup != null && sup.isClass()) {
52-
CtType<?> ct = sup.getDeclaration();
53-
if (ct instanceof CtClass)
54-
visitCtClass((CtClass<?>) ct);
55-
}
56-
// first try-catch: process class-level annotations)
57-
// errors here should not prevent visiting methods, constructors or fields of the class
58-
try {
59-
getRefinementFromAnnotation(ctClass);
60-
handleStateSetsFromAnnotation(ctClass);
61-
} catch (LJError e) {
62-
diagnostics.add(e);
63-
}
64-
// second try-catch: visit class children (methods, constructors, fields)
65-
// errors from one child should not prevent visiting sibling elements
66-
try {
67-
super.visitCtClass(ctClass);
68-
} catch (LJError e) {
69-
diagnostics.add(e);
57+
// first try-catch: process class-level annotations)
58+
// errors here should not prevent visiting methods, constructors or fields of the class
59+
try {
60+
getRefinementFromAnnotation(ctClass);
61+
handleStateSetsFromAnnotation(ctClass);
62+
} catch (LJError e) {
63+
diagnostics.add(e);
64+
}
65+
// second try-catch: visit class children (methods, constructors, fields)
66+
// errors from one child should not prevent visiting sibling elements
67+
try {
68+
super.visitCtClass(ctClass);
69+
} catch (LJError e) {
70+
diagnostics.add(e);
71+
}
72+
} finally {
73+
context.exitClassScope(scope);
7074
}
7175
}
7276

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,14 @@ public RefinementTypeChecker(Context context, Factory factory) {
7474

7575
@Override
7676
public <T> void visitCtClass(CtClass<T> ctClass) {
77-
// System.out.println("CTCLASS:"+ctClass.getSimpleName());
78-
context.reinitializeContext();
79-
77+
Context.ClassScope scope = context.enterClassScope();
8078
try {
8179
super.visitCtClass(ctClass);
8280
} catch (LJError e) {
8381
diagnostics.add(e);
82+
} finally {
83+
context.exitClassScope(scope);
8484
}
85-
8685
}
8786

8887
@Override

0 commit comments

Comments
 (0)