Skip to content
Open
2 changes: 1 addition & 1 deletion liquidjava-verifier/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<groupId>io.github.liquid-java</groupId>
<artifactId>liquidjava-verifier</artifactId>
<version>0.0.14</version>
<version>0.0.15</version>
<name>liquidjava-verifier</name>
<description>LiquidJava Verifier</description>
<url>https://github.com/liquid-java/liquidjava</url>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ public class LJDiagnostic extends RuntimeException {
private final String accentColor;
private final String customMessage;
private String file;
private ErrorPosition position;
private SourcePosition position;
private static final String PIPE = " | ";

public LJDiagnostic(String title, String message, SourcePosition pos, String accentColor, String customMessage) {
this.title = title;
this.message = message;
this.file = (pos != null && pos.getFile() != null) ? pos.getFile().getPath() : null;
this.position = ErrorPosition.fromSpoonPosition(pos);
this.position = pos;
this.accentColor = accentColor;
this.customMessage = customMessage;
}
Expand All @@ -41,14 +41,14 @@ public String getDetails() {
return ""; // to be overridden by subclasses
}

public ErrorPosition getPosition() {
public SourcePosition getPosition() {
return position;
}

public void setPosition(SourcePosition pos) {
if (pos == null)
return;
this.position = ErrorPosition.fromSpoonPosition(pos);
this.position = pos;
this.file = pos.getFile().getPath();
}

Expand Down Expand Up @@ -82,7 +82,7 @@ public String toString() {

// location
if (file != null && position != null) {
sb.append("\n").append(file).append(":").append(position.lineStart()).append(Colors.RESET).append("\n");
sb.append("\n").append(file).append(":").append(position.getLine()).append(Colors.RESET).append("\n");
}

return sb.toString();
Expand All @@ -100,8 +100,8 @@ public String getSnippet() {
// before and after lines for context
int contextBefore = 2;
int contextAfter = 2;
int startLine = Math.max(1, position.lineStart() - contextBefore);
int endLine = Math.min(lines.size(), position.lineEnd() + contextAfter);
int startLine = Math.max(1, position.getLine() - contextBefore);
int endLine = Math.min(lines.size(), position.getEndLine() + contextAfter);

// calculate padding for line numbers
int padding = String.valueOf(endLine).length();
Expand All @@ -115,9 +115,9 @@ public String getSnippet() {
sb.append(Colors.GREY).append(lineNumStr).append(PIPE).append(line).append(Colors.RESET).append("\n");

// add error markers on the line(s) with the error
if (i >= position.lineStart() && i <= position.lineEnd()) {
int colStart = (i == position.lineStart()) ? position.colStart() : 1;
int colEnd = (i == position.lineEnd()) ? position.colEnd() : rawLine.length();
if (i >= position.getLine() && i <= position.getEndLine()) {
int colStart = (i == position.getLine()) ? position.getColumn() : 1;
int colEnd = (i == position.getEndLine()) ? position.getEndColumn() : rawLine.length();

if (colStart > 0 && colEnd > 0) {
int tabsBeforeStart = (int) rawLine.substring(0, Math.max(0, colStart - 1)).chars()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,32 +104,31 @@ public void addVarToContext(RefinedVariable var) {
var.addSuperTypes(type.getSuperclass(), type.getSuperInterfaces());
}

public RefinedVariable addVarToContext(String simpleName, CtTypeReference<?> type, Predicate c,
CtElement placementInCode) {
public RefinedVariable addVarToContext(String simpleName, CtTypeReference<?> type, Predicate c, CtElement element) {
RefinedVariable vi = new Variable(simpleName, type, c);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
vi.addSuperTypes(type.getSuperclass(), type.getSuperInterfaces());
addVarToContext(vi);
return vi;
}

public RefinedVariable addInstanceToContext(String simpleName, CtTypeReference<?> type, Predicate c,
CtElement placementInCode) {
CtElement element) {
RefinedVariable vi = new VariableInstance(simpleName, type, c);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
if (!ctxInstanceVars.contains(vi))
addInstanceVariable(vi);
return vi;
}

public void addRefinementToVariableInContext(String name, CtTypeReference<?> type, Predicate et,
CtElement placementInCode) {
CtElement element) {
if (hasVariable(name)) {
RefinedVariable vi = getVariableByName(name);
vi.setRefinement(et);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
} else {
addVarToContext(name, type, et, placementInCode);
addVarToContext(name, type, et, element);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@
import java.util.Map;
import java.util.Set;

import liquidjava.utils.Utils;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtParameter;

public class ContextHistory {
private static ContextHistory instance;

private Map<String, Map<String, Set<RefinedVariable>>> fileScopeVars; // file -> (scope -> variables in scope)
private Map<String, Set<GhostState>> ghosts; // file -> ghosts

// globals
private Set<AliasWrapper> aliases;
private Set<RefinedVariable> instanceVars;
private Set<RefinedVariable> globalVars;
private Set<GhostState> ghosts;
private Set<AliasWrapper> aliases;

private ContextHistory() {
fileScopeVars = new HashMap<>();
instanceVars = new HashSet<>();
globalVars = new HashSet<>();
ghosts = new HashSet<>();
ghosts = new HashMap<>();
aliases = new HashSet<>();
}

Expand All @@ -41,32 +44,43 @@ public void clearHistory() {
}

public void saveContext(CtElement element, Context context) {
SourcePosition pos = element.getPosition();
if (pos == null || pos.getFile() == null)
String file = getFile(element);
if (file == null)
return;

// add variables in scope for this position
String file = pos.getFile().getAbsolutePath();
// add variables in scope
String scope = getScopePosition(element);
fileScopeVars.putIfAbsent(file, new HashMap<>());
fileScopeVars.get(file).put(scope, new HashSet<>(context.getCtxVars()));

// add other elements in context
// add other elements in context (except ghosts)
instanceVars.addAll(context.getCtxInstanceVars());
globalVars.addAll(context.getCtxGlobalVars());
ghosts.addAll(context.getGhostStates());
aliases.addAll(context.getAliases());
}

public void saveGhost(CtElement element, GhostState ghost) {
String file = getFile(element);
if (file == null)
return;
ghosts.putIfAbsent(file, new HashSet<>());
ghosts.get(file).add(ghost);
}

private String getFile(CtElement element) {
SourcePosition pos = element.getPosition();
if (pos == null || pos.getFile() == null)
return null;

return pos.getFile().getAbsolutePath();
}

public String getScopePosition(CtElement element) {
CtElement startElement = element instanceof CtParameter<?> ? element.getParent() : element;
SourcePosition annPosition = Utils.getFirstLJAnnotationPosition(startElement);
SourcePosition pos = element.getPosition();
SourcePosition innerPosition = pos;
if (element instanceof CtExecutable<?> executable) {
if (executable.getBody() != null)
innerPosition = executable.getBody().getPosition();
}
return String.format("%d:%d-%d:%d", innerPosition.getLine(), innerPosition.getColumn() + 1, pos.getEndLine(),
pos.getEndColumn());
return String.format("%d:%d-%d:%d", annPosition.getLine() - 1, annPosition.getColumn() - 1,
pos.getEndLine() - 1, pos.getEndColumn() - 1);
}

public Map<String, Map<String, Set<RefinedVariable>>> getFileScopeVars() {
Expand All @@ -81,7 +95,7 @@ public Set<RefinedVariable> getGlobalVars() {
return globalVars;
}

public Set<GhostState> getGhosts() {
public Map<String, Set<GhostState>> getGhosts() {
return ghosts;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
import java.util.List;
import java.util.Set;
import liquidjava.rj_language.Predicate;
import liquidjava.utils.Utils;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.reference.CtTypeReference;

public abstract class RefinedVariable extends Refined {
private final List<CtTypeReference<?>> supertypes;
private PlacementInCode placementInCode;
private boolean isParameter;
private SourcePosition annPosition;

public RefinedVariable(String name, CtTypeReference<?> type, Predicate c) {
super(name, type, c);
supertypes = new ArrayList<>();
isParameter = false;
}

public abstract Predicate getMainRefinement();
Expand All @@ -34,14 +40,23 @@ public void addSuperTypes(CtTypeReference<?> ts, Set<CtTypeReference<?>> sts) {
supertypes.add(ct);
}

public void addPlacementInCode(PlacementInCode s) {
placementInCode = s;
public void addPlacementInCode(CtElement element) {
placementInCode = PlacementInCode.createPlacement(element);
annPosition = Utils.getFirstLJAnnotationPosition(element);
}

public void addPlacementInCode(PlacementInCode placement) {
placementInCode = placement;
}

public PlacementInCode getPlacementInCode() {
return placementInCode;
}

public SourcePosition getAnnPosition() {
return annPosition;
}

@Override
public int hashCode() {
final int prime = 31;
Expand All @@ -65,4 +80,12 @@ public boolean equals(Object obj) {
return supertypes.equals(other.supertypes);
}
}

public void setIsParameter(boolean b) {
isParameter = b;
}

public boolean isParameter() {
return isParameter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import liquidjava.diagnostics.warnings.ExternalClassNotFoundWarning;
import liquidjava.diagnostics.warnings.ExternalMethodNotFoundWarning;
import liquidjava.processor.context.Context;
import liquidjava.processor.context.ContextHistory;
import liquidjava.processor.context.GhostFunction;
import liquidjava.processor.facade.GhostDTO;
import liquidjava.processor.refinement_checker.general_checkers.MethodsFunctionsChecker;
Expand All @@ -28,8 +29,9 @@
import spoon.reflect.reference.CtTypeReference;

public class ExternalRefinementTypeChecker extends TypeChecker {
String prefix;
Diagnostics diagnostics = Diagnostics.getInstance();
private String prefix;
private final Diagnostics diagnostics = Diagnostics.getInstance();
private final ContextHistory contextHistory = ContextHistory.getInstance();

public ExternalRefinementTypeChecker(Context context, Factory factory) {
super(context, factory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import liquidjava.diagnostics.errors.*;
import liquidjava.processor.context.AliasWrapper;
import liquidjava.processor.context.Context;
import liquidjava.processor.context.ContextHistory;
import liquidjava.processor.context.GhostFunction;
import liquidjava.processor.context.GhostState;
import liquidjava.processor.context.RefinedVariable;
Expand Down Expand Up @@ -40,6 +41,7 @@ public abstract class TypeChecker extends CtScanner {
protected final Context context;
protected final Factory factory;
protected final VCChecker vcChecker;
private final ContextHistory contextHistory = ContextHistory.getInstance();

public TypeChecker(Context context, Factory factory) {
this.context = context;
Expand Down Expand Up @@ -81,7 +83,7 @@ public Optional<Predicate> getRefinementFromAnnotation(CtElement element) throws
if (ref.isPresent()) {
Predicate p = new Predicate(ref.get(), element);
if (!p.getExpression().isBooleanExpression()) {
SourcePosition position = Utils.getAnnotationPosition(element, ref.get());
SourcePosition position = Utils.getLJAnnotationPosition(element, ref.get());
throw new InvalidRefinementError(position, "Refinement predicate must be a boolean expression",
ref.get());
}
Expand Down Expand Up @@ -157,6 +159,7 @@ private void createStateSet(CtNewArray<String> e, int set, CtElement element) th
gs.setRefinement(Predicate.createEquals(ip, Predicate.createLit(Integer.toString(order), Types.INT)));
// open(THIS) -> state1(THIS) == 1
context.addToGhostClass(g.getParentClassName(), gs);
contextHistory.saveGhost(element, gs);
}
order++;
}
Expand Down Expand Up @@ -191,6 +194,7 @@ private void createStateGhost(String string, CtElement element, SourcePosition p
CtTypeReference<?> r = factory.Type().createReference(gd.returnType());
GhostState gs = new GhostState(gd.name(), param, r, qn);
context.addToGhostClass(sn, gs);
contextHistory.saveGhost(element, gs);
}

protected String getQualifiedClassName(CtElement element) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private Predicate handleFunctionRefinements(RefinedFunction f, CtElement method,
c = oc.get().substituteVariable(Keys.WILDCARD, paramName);
param.putMetadata(Keys.REFINEMENT, c);
RefinedVariable v = rtc.getContext().addVarToContext(param.getSimpleName(), param.getType(), c, param);
v.setIsParameter(true);
rtc.getMessageFromAnnotation(param).ifPresent(v::setMessage);
if (v instanceof Variable)
f.addArgRefinements((Variable) v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private static Predicate createStatePredicate(String value, String targetClass,
boolean isTo, String prefix) throws LJError {
Predicate p = new Predicate(value, e, prefix);
if (!p.getExpression().isBooleanExpression()) {
SourcePosition position = Utils.getAnnotationPosition(e, value);
SourcePosition position = Utils.getLJAnnotationPosition(e, value);
throw new InvalidRefinementError(position, "State refinement transition must be a boolean expression",
value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected Expression parse(String ref, CtElement element) throws LJError {
return RefinementsParser.createAST(ref, prefix);
} catch (LJError e) {
// add location info to error
SourcePosition pos = Utils.getAnnotationPosition(element, ref);
SourcePosition pos = Utils.getLJAnnotationPosition(element, ref);
e.setPosition(pos);
throw e;
}
Expand Down
11 changes: 10 additions & 1 deletion liquidjava-verifier/src/main/java/liquidjava/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,21 @@ public static String qualifyName(String prefix, String name) {
return String.format("%s.%s", prefix, name);
}

public static SourcePosition getAnnotationPosition(CtElement element, String refinement) {
public static SourcePosition getLJAnnotationPosition(CtElement element, String refinement) {
return element.getAnnotations().stream()
.filter(a -> isLiquidJavaAnnotation(a) && hasRefinementValue(a, "\"" + refinement + "\"")).findFirst()
.map(CtElement::getPosition).orElse(element.getPosition());
}

public static SourcePosition getFirstLJAnnotationPosition(CtElement element) {
return element.getAnnotations().stream().filter(Utils::isLiquidJavaAnnotation).map(CtElement::getPosition)
.min((p1, p2) -> {
if (p1.getLine() != p2.getLine())
return Integer.compare(p1.getLine(), p2.getLine());
return Integer.compare(p1.getColumn(), p2.getColumn());
}).orElse(element.getPosition());
}

private static boolean isLiquidJavaAnnotation(CtAnnotation<?> annotation) {
return annotation.getAnnotationType().getQualifiedName().startsWith("liquidjava.specification");
}
Expand Down