Skip to content

Commit 2916690

Browse files
authored
Check Method Signatures in External Refinements (#76)
1 parent 4724084 commit 2916690

File tree

8 files changed

+124
-21
lines changed

8 files changed

+124
-21
lines changed

liquidjava-example/src/main/java/testSuite/ErrorExtRefNonExistentMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ public interface ErrorExtRefNonExistentMethod<E> {
1212
public void ArrayList();
1313

1414
@StateRefinement(to = "size(this) == (size(old(this)) + 1)")
15-
public void adddd(E e);
15+
public boolean adddd(E e);
1616
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.RefinementPredicate;
5+
import liquidjava.specification.StateRefinement;
6+
7+
@ExternalRefinementsFor("java.util.ArrayList")
8+
public interface ErrorExtRefWrongConstructor<E> {
9+
10+
@RefinementPredicate("int size(ArrayList l)")
11+
@StateRefinement(to = "size(this) == 0")
12+
public void ArrayList(String wrongParameter);
13+
14+
@StateRefinement(to = "size(this) == (size(old(this)) + 1)")
15+
public boolean add(E e);
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.RefinementPredicate;
5+
import liquidjava.specification.StateRefinement;
6+
7+
@ExternalRefinementsFor("java.util.ArrayList")
8+
public interface ErrorExtRefWrongParameterType<E> {
9+
10+
@RefinementPredicate("int size(ArrayList l)")
11+
@StateRefinement(to = "size(this) == 0")
12+
public void ArrayList();
13+
14+
@StateRefinement(to = "size(this) == (size(old(this)) + 1)")
15+
public boolean add(int wrongParameter);
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.ExternalRefinementsFor;
4+
import liquidjava.specification.RefinementPredicate;
5+
import liquidjava.specification.StateRefinement;
6+
7+
@ExternalRefinementsFor("java.util.ArrayList")
8+
public interface ErrorExtRefWrongRetType<E> {
9+
10+
@RefinementPredicate("int size(ArrayList l)")
11+
@StateRefinement(to = "size(this) == 0")
12+
public void ArrayList();
13+
14+
@StateRefinement(to = "size(this) == (size(old(this)) + 1)")
15+
public int add(E e); // wrong return type
16+
}

liquidjava-example/src/main/java/testSuite/classes/arraylist_correct/ArrayListRefinements.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface ArrayListRefinements<E> {
1212
public void ArrayList();
1313

1414
@StateRefinement(to = "size(this) == (size(old(this)) + 1)")
15-
public void add(E e);
15+
public boolean add(E e);
1616

1717
// @Refinement("size(_) == size(this)")
1818
// public Object clone();

liquidjava-example/src/main/java/testSuite/classes/conflicting_ghost_names_correct/StackRefinements.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public interface StackRefinements<E> {
1111
public void Stack();
1212

1313
@StateRefinement(to="size(this) == size(old(this)) + 1")
14-
public boolean push(E elem);
14+
public E push(E elem);
1515

1616
@StateRefinement(from="size(this) > 0", to="size(this) == size(old(this)) - 1")
1717
public E pop();

liquidjava-example/src/main/java/testSuite/math/correctInvocation/MathRefinements.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ public interface MathRefinements {
1616
public int abs(int arg0);
1717

1818
@Refinement("(arg0 > 0)?( _ == arg0):(_ == -arg0)")
19-
public int abs(long arg0);
19+
public long abs(long arg0);
2020

2121
@Refinement("(arg0 > 0)?( _ == arg0):(_ == -arg0)")
22-
public int abs(float arg0);
22+
public float abs(float arg0);
2323

2424
@Refinement("(arg0 > 0)?( _ == arg0):(_ == -arg0)")
25-
public int abs(double arg0);
25+
public double abs(double arg0);
2626

2727
@Refinement(" _ == a+b")
2828
public int addExact(int a, int b);
@@ -43,13 +43,13 @@ public interface MathRefinements {
4343
public int decrementExact(int a);
4444

4545
@Refinement("_ == (a-1)")
46-
public int decrementExact(long a);
46+
public long decrementExact(long a);
4747

4848
@Refinement("_ == (a+1)")
4949
public int incrementExact(int a);
5050

5151
@Refinement("_ == (a+1)")
52-
public int incrementExact(long a);
52+
public long incrementExact(long a);
5353

5454
@Refinement("(a > b)? (_ == a):(_ == b)")
5555
public int max(int a, int b);

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

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import java.util.Arrays;
44
import java.util.List;
55
import java.util.Optional;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.Stream;
8+
69
import liquidjava.errors.ErrorEmitter;
710
import liquidjava.errors.ErrorHandler;
811
import liquidjava.processor.context.Context;
@@ -18,8 +21,8 @@
1821
import spoon.reflect.declaration.CtField;
1922
import spoon.reflect.declaration.CtInterface;
2023
import spoon.reflect.declaration.CtMethod;
24+
import spoon.reflect.declaration.CtParameter;
2125
import spoon.reflect.declaration.CtType;
22-
import spoon.reflect.declaration.CtTypeParameter;
2326
import spoon.reflect.factory.Factory;
2427
import spoon.reflect.reference.CtTypeReference;
2528

@@ -78,11 +81,38 @@ public <R> void visitCtMethod(CtMethod<R> method) {
7881
if (errorEmitter.foundError())
7982
return;
8083

81-
if (!methodExists(method)) {
82-
ErrorHandler.printCustomError(method,
83-
"Could not find method '" + method.getSignature() + "' in class '" + prefix + "'", errorEmitter);
84+
CtType<?> targetType = factory.Type().createReference(prefix).getTypeDeclaration();
85+
if (targetType == null || !(targetType instanceof CtClass))
8486
return;
87+
88+
boolean isConstructor = method.getSimpleName().equals(targetType.getSimpleName());
89+
if (isConstructor) {
90+
if (!constructorExists(targetType, method)) {
91+
ErrorHandler.printCustomError(method,
92+
String.format("Could not find constructor '%s' for '%s'", method.getSignature(), prefix),
93+
errorEmitter);
94+
return;
95+
}
96+
} else {
97+
if (!methodExists(targetType, method)) {
98+
String matchingNames = targetType.getMethods().stream()
99+
.filter(m -> m.getSimpleName().equals(method.getSimpleName()))
100+
.map(m -> String.format("%s %s", m.getType().getSimpleName(), m.getSignature()))
101+
.collect(Collectors.joining("\n "));
102+
103+
StringBuilder sb = new StringBuilder();
104+
sb.append(String.format("Could not find method '%s %s' for '%s'", method.getType().getSimpleName(),
105+
method.getSignature(), prefix));
106+
107+
if (!matchingNames.isEmpty()) {
108+
sb.append("\nAvailable overloads:\n ");
109+
sb.append(matchingNames);
110+
}
111+
ErrorHandler.printCustomError(method, sb.toString(), errorEmitter);
112+
return;
113+
}
85114
}
115+
86116
MethodsFunctionsChecker mfc = new MethodsFunctionsChecker(this);
87117
try {
88118
mfc.getMethodRefinements(method, prefix);
@@ -138,17 +168,42 @@ private boolean classExists(String className) {
138168
return factory.Type().createReference(className).getTypeDeclaration() != null;
139169
}
140170

141-
private boolean methodExists(CtMethod<?> method) {
142-
CtType<?> targetType = factory.Type().createReference(prefix).getTypeDeclaration();
171+
private boolean methodExists(CtType<?> targetType, CtMethod<?> method) {
172+
// find method with matching signature
173+
return targetType.getMethods().stream().filter(m -> m.getSimpleName().equals(method.getSimpleName()))
174+
.anyMatch(m -> parametersMatch(m.getParameters(), method.getParameters())
175+
&& typesMatch(m.getType(), method.getType()));
176+
}
177+
178+
private boolean constructorExists(CtType<?> targetType, CtMethod<?> method) {
179+
// find constructor with matching signature
180+
CtClass<?> targetClass = (CtClass<?>) targetType;
181+
return targetClass.getConstructors().stream()
182+
.anyMatch(c -> parametersMatch(c.getParameters(), method.getParameters()));
183+
}
184+
185+
private boolean typesMatch(CtTypeReference<?> type1, CtTypeReference<?> type2) {
186+
if (type1 == null && type2 == null)
187+
return true;
188+
189+
if (type1 == null || type2 == null)
190+
return false;
191+
192+
return type1.getQualifiedName().equals(type2.getQualifiedName());
193+
}
194+
195+
private boolean parametersMatch(List<?> targetParams, List<?> refinementParams) {
196+
if (targetParams.size() != refinementParams.size())
197+
return false;
143198

144-
// find a method with matching name and parameter count
145-
boolean methodFound = targetType.getMethods().stream()
146-
.anyMatch(m -> m.getSimpleName().equals(method.getSimpleName())
147-
&& m.getParameters().size() == method.getParameters().size());
199+
for (int i = 0; i < targetParams.size(); i++) {
200+
CtParameter<?> targetParam = (CtParameter<?>) targetParams.get(i);
201+
CtParameter<?> refinementParam = (CtParameter<?>) refinementParams.get(i);
202+
if (targetParam == null || refinementParam == null)
203+
return false;
148204

149-
if (!methodFound) {
150-
// check if constructor method
151-
return method.getSimpleName().equals(targetType.getSimpleName());
205+
if (!typesMatch(targetParam.getType(), refinementParam.getType()))
206+
return false;
152207
}
153208
return true;
154209
}

0 commit comments

Comments
 (0)