Skip to content

Commit 095db02

Browse files
allow interfaces to be also annotated with external refinements
1 parent 3ac0f36 commit 095db02

5 files changed

Lines changed: 89 additions & 2 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package testSuite.classes.iterator_interface_correct;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.StateRefinement;
5+
import liquidjava.specification.StateSet;
6+
7+
/**
8+
* External refinements for the JDK interface {@code java.util.Iterator}. Exercises {@code @ExternalRefinementsFor} on an
9+
* interface target (not a concrete class) and a generic method ({@code N next()} vs the JDK's {@code E next()}).
10+
*/
11+
@StateSet({ "hasMore", "inNext", "notInNext" })
12+
@ExternalRefinementsFor("java.util.Iterator")
13+
public interface IteratorRefinements<N> {
14+
15+
@StateRefinement(to = "_ --> hasMore()")
16+
boolean hasNext();
17+
18+
@StateRefinement(from = "hasMore()", to = "inNext()")
19+
N next();
20+
21+
@StateRefinement(from = "inNext()", to = "notInNext()")
22+
void remove();
23+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package testSuite.classes.iterator_interface_correct;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
6+
public class Test {
7+
8+
public static void main(String[] args) {
9+
ArrayList<Object> list = new ArrayList<>();
10+
list.add(new Object());
11+
Iterator<Object> it = list.iterator();
12+
if (it.hasNext()) {
13+
it.next();
14+
it.remove(); // valid: remove() after next()
15+
}
16+
}
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package testSuite.classes.iterator_interface_error;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.StateRefinement;
5+
import liquidjava.specification.StateSet;
6+
7+
/**
8+
* External refinements for the JDK interface {@code java.util.Iterator}. Exercises {@code @ExternalRefinementsFor} on an
9+
* interface target (not a concrete class) and a generic method ({@code N next()} vs the JDK's {@code E next()}).
10+
*/
11+
@StateSet({ "hasMore", "inNext", "notInNext" })
12+
@ExternalRefinementsFor("java.util.Iterator")
13+
public interface IteratorRefinements<N> {
14+
15+
@StateRefinement(to = "_ --> hasMore()")
16+
boolean hasNext();
17+
18+
@StateRefinement(from = "hasMore()", to = "inNext()")
19+
N next();
20+
21+
@StateRefinement(from = "inNext()", to = "notInNext()")
22+
void remove();
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package testSuite.classes.iterator_interface_error;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
6+
public class Test {
7+
8+
public static void main(String[] args) {
9+
ArrayList<Object> list = new ArrayList<>();
10+
list.add(new Object());
11+
Iterator<Object> it = list.iterator();
12+
it.remove(); // State Refinement Error
13+
}
14+
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import spoon.reflect.declaration.CtParameter;
2525
import spoon.reflect.declaration.CtType;
2626
import spoon.reflect.factory.Factory;
27+
import spoon.reflect.reference.CtTypeParameterReference;
2728
import spoon.reflect.reference.CtTypeReference;
2829

2930
public class ExternalRefinementTypeChecker extends TypeChecker {
@@ -66,10 +67,13 @@ public <T> void visitCtField(CtField<T> f) {
6667

6768
public <R> void visitCtMethod(CtMethod<R> method) {
6869
CtType<?> targetType = factory.Type().createReference(prefix).getTypeDeclaration();
69-
if (!(targetType instanceof CtClass))
70+
if (!(targetType instanceof CtClass) && !(targetType instanceof CtInterface))
7071
return;
7172

72-
boolean isConstructor = method.getSimpleName().equals(targetType.getSimpleName());
73+
// Interfaces have no constructors; only a class can declare one, so the constructor-name shortcut
74+
// (a refinement method named like the target type) only applies to class targets.
75+
boolean isConstructor = targetType instanceof CtClass
76+
&& method.getSimpleName().equals(targetType.getSimpleName());
7377
if (isConstructor) {
7478
if (!constructorExists(targetType, method)) {
7579
String signature = method.getSignature();
@@ -149,6 +153,12 @@ private boolean typesMatch(CtTypeReference<?> type1, CtTypeReference<?> type2) {
149153
if (type1 == null || type2 == null)
150154
return false;
151155

156+
// Type variables (generics such as E, N, T) carry different names in the JDK type and in the refinement
157+
// interface (e.g. Iterator's `E next()` vs a spec's `N next()`), so they never match by qualified name.
158+
// Treat any two type-parameter references as compatible.
159+
if (type1 instanceof CtTypeParameterReference && type2 instanceof CtTypeParameterReference)
160+
return true;
161+
152162
return type1.getQualifiedName().equals(type2.getQualifiedName());
153163
}
154164

0 commit comments

Comments
 (0)