|
46 | 46 | import spoon.reflect.declaration.CtClass; |
47 | 47 | import spoon.reflect.declaration.CtElement; |
48 | 48 | import spoon.reflect.declaration.CtExecutable; |
| 49 | +import spoon.reflect.declaration.CtMethod; |
49 | 50 | import spoon.reflect.declaration.CtVariable; |
50 | 51 | import spoon.reflect.visitor.filter.TypeFilter; |
51 | 52 |
|
@@ -161,7 +162,18 @@ private boolean mayBeAliased() { |
161 | 162 | return true; |
162 | 163 | return scope.getElements(new TypeFilter<>(CtVariableRead.class)).stream() |
163 | 164 | .filter(use -> use.getVariable().getDeclaration() == variable && mayRunBefore(use, scope)) |
164 | | - .anyMatch(use -> escapes(use, scope)); |
| 165 | + .anyMatch(use -> escapes(use, scope) || callsUnspecifiedBody(use)); |
| 166 | + } |
| 167 | + |
| 168 | + /** |
| 169 | + * Whether {@code use} is the receiver of a call to a method of the analyzed code with no state refinement: the |
| 170 | + * verifier takes it to keep the state, which its body may still change |
| 171 | + */ |
| 172 | + private boolean callsUnspecifiedBody(CtVariableRead<?> use) { |
| 173 | + return use.getParent()instanceof CtInvocation<?> call && call != invocation && call.getTarget() == use |
| 174 | + && call.getExecutable().getDeclaration()instanceof CtMethod<?> method && method.getBody() != null |
| 175 | + && method.getAnnotations().stream().noneMatch(a -> a.getAnnotationType().getQualifiedName() |
| 176 | + .equals("liquidjava.specification.StateRefinement")); |
165 | 177 | } |
166 | 178 |
|
167 | 179 | /** |
@@ -234,7 +246,8 @@ && implies(facts.get().substituteVariable(Keys.WILDCARD, assigned.getName()), fo |
234 | 246 | : Optional.empty(); |
235 | 247 | Predicate returned = call.returned(); |
236 | 248 | // a set declared without a name has a generated one, which the message does not otherwise show |
237 | | - boolean saysNothing = found.isEmpty() |
| 249 | + // a state refinement of the method may give the value its state, as `to = "running(_)"` does |
| 250 | + boolean saysNothing = found.isEmpty() && call.function().getAllStates().stream().noneMatch(ObjectState::hasTo) |
238 | 251 | && shown.stream().noneMatch(set -> Utils.getSimpleName(set.getName()).matches(Formats.STATE_SET_NAMES)) |
239 | 252 | && setsOf(List.of(returned), ghostStates).stream().noneMatch(shown::contains) |
240 | 253 | && returned.getVariableNames().stream().allMatch(Keys.WILDCARD::equals) |
|
0 commit comments