Skip to content

Commit a08dbad

Browse files
committed
Added enum type support with tests
1 parent 2a8f9b7 commit a08dbad

File tree

8 files changed

+119
-4
lines changed

8 files changed

+119
-4
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.StateRefinement;
4+
import liquidjava.specification.StateSet;
5+
6+
@SuppressWarnings("unused")
7+
@StateSet({"photoMode", "videoMode", "noMode"})
8+
class CorrectEnumUsage {
9+
enum Mode {
10+
Photo, Video, Unknown
11+
}
12+
13+
Mode mode;
14+
@StateRefinement(to="noMode(this)")
15+
public CorrectEnumUsage() {}
16+
17+
@StateRefinement(from="noMode(this) && mode == Mode.Photo", to="photoMode(this)")
18+
@StateRefinement(from="noMode(this) && mode == Mode.Video", to="videoMode(this)")
19+
public void setMode(Mode mode) {
20+
this.mode = mode;
21+
}
22+
23+
@StateRefinement(from="photoMode(this)")
24+
public void takePhoto() {}
25+
26+
27+
public static void main(String[] args) {
28+
// Correct
29+
CorrectEnumUsage st = new CorrectEnumUsage();
30+
st.setMode(Mode.Photo);
31+
st.takePhoto();
32+
}
33+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// State Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.StateRefinement;
5+
import liquidjava.specification.StateSet;
6+
7+
8+
@SuppressWarnings("unused")
9+
@StateSet({"photoMode", "videoMode", "noMode"})
10+
class ErrorEnumUsage {
11+
enum Mode {
12+
Photo, Video, Unknown
13+
}
14+
15+
Mode mode;
16+
@StateRefinement(to="noMode(this)")
17+
public ErrorEnumUsage() {}
18+
19+
@StateRefinement(from="noMode(this) && mode == Mode.Photo", to="photoMode(this)")
20+
@StateRefinement(from="noMode(this) && mode == Mode.Video", to="videoMode(this)")
21+
public void setMode(Mode mode) {
22+
this.mode = mode;
23+
}
24+
25+
@StateRefinement(from="photoMode(this)")
26+
public void takePhoto() {}
27+
28+
29+
public static void main(String[] args) {
30+
// Correct
31+
ErrorEnumUsage st = new ErrorEnumUsage();
32+
st.setMode(Mode.Video);
33+
st.takePhoto(); //error
34+
}
35+
}

liquidjava-verifier/src/main/antlr4/rj/grammar/RJ.g4

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ literalExpression:
3838
| literal #lit
3939
| ID #var
4040
| functionCall #invocation
41+
| enumCall #enum
4142
;
4243

4344
functionCall:
@@ -55,6 +56,9 @@ ghostCall:
5556
aliasCall:
5657
ID_UPPER '(' args? ')';
5758

59+
enumCall:
60+
OBJECT_TYPE;
61+
5862
args: pred (',' pred)* ;
5963

6064

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@
77
import liquidjava.diagnostics.errors.LJError;
88
import liquidjava.processor.context.Context;
99
import liquidjava.processor.refinement_checker.general_checkers.MethodsFunctionsChecker;
10+
import liquidjava.rj_language.Predicate;
11+
import liquidjava.utils.constants.Formats;
12+
import liquidjava.utils.constants.Types;
1013
import spoon.reflect.declaration.CtClass;
1114
import spoon.reflect.declaration.CtConstructor;
15+
import spoon.reflect.declaration.CtEnum;
16+
import spoon.reflect.declaration.CtEnumValue;
1217
import spoon.reflect.declaration.CtInterface;
1318
import spoon.reflect.declaration.CtMethod;
1419
import spoon.reflect.declaration.CtType;
@@ -116,4 +121,19 @@ public <R> void visitCtMethod(CtMethod<R> method) {
116121
}
117122
context.exitContext();
118123
}
124+
125+
@Override
126+
public <T extends Enum<?>> void visitCtEnum(CtEnum<T> enumRead) {
127+
String enumName = enumRead.getSimpleName();
128+
String qualifiedEnumName = enumRead.getQualifiedName();
129+
int ordinal = 0;
130+
for (CtEnumValue ev : enumRead.getEnumValues()) {
131+
String varName = String.format(Formats.ENUM_VALUE, enumName, ev.getSimpleName());
132+
Predicate refinement = Predicate.createEquals(Predicate.createVar(varName),
133+
Predicate.createLit(String.valueOf(ordinal), Types.INT));
134+
context.addGlobalVariableToContext(varName, qualifiedEnumName, enumRead.getReference(), refinement);
135+
ordinal++;
136+
}
137+
super.visitCtEnum(enumRead);
138+
}
119139
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,11 @@ public <T> void visitCtFieldRead(CtFieldRead<T> fieldRead) {
269269
String targetName = fieldRead.getTarget().toString();
270270
fieldRead.putMetadata(Keys.REFINEMENT, Predicate.createEquals(Predicate.createVar(Keys.WILDCARD),
271271
BuiltinFunctionPredicate.length(targetName, fieldRead)));
272-
272+
} else if (fieldRead.getVariable().getDeclaringType().isEnum()) {
273+
String target = fieldRead.getVariable().getDeclaringType().getSimpleName();
274+
String enumLiteral = String.format(Formats.ENUM_VALUE, target, fieldName);
275+
fieldRead.putMetadata(Keys.REFINEMENT,
276+
Predicate.createEquals(Predicate.createVar(Keys.WILDCARD), Predicate.createVar(enumLiteral)));
273277
} else {
274278
fieldRead.putMetadata(Keys.REFINEMENT, new Predicate());
275279
// TODO DO WE WANT THIS OR TO SHOW ERROR MESSAGE?

liquidjava-verifier/src/main/java/liquidjava/rj_language/visitors/CreateASTVisitor.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import liquidjava.rj_language.ast.UnaryExpression;
2020
import liquidjava.rj_language.ast.Var;
2121
import liquidjava.utils.Utils;
22+
import liquidjava.utils.constants.Formats;
2223
import liquidjava.utils.constants.Keys;
2324

2425
import org.antlr.v4.runtime.tree.ParseTree;
2526
import org.apache.commons.lang3.NotImplementedException;
2627
import rj.grammar.RJParser.AliasCallContext;
2728
import rj.grammar.RJParser.ArgsContext;
2829
import rj.grammar.RJParser.DotCallContext;
30+
import rj.grammar.RJParser.EnumContext;
2931
import rj.grammar.RJParser.ExpBoolContext;
3032
import rj.grammar.RJParser.ExpContext;
3133
import rj.grammar.RJParser.ExpGroupContext;
@@ -156,9 +158,10 @@ private Expression literalExpressionCreate(ParseTree rc) throws LJError {
156158
return new GroupExpression(create(((LitGroupContext) rc).literalExpression()));
157159
else if (rc instanceof LitContext)
158160
return create(((LitContext) rc).literal());
159-
else if (rc instanceof VarContext) {
161+
else if (rc instanceof VarContext)
160162
return new Var(((VarContext) rc).ID().getText());
161-
163+
else if (rc instanceof EnumContext) {
164+
return new Var(enumCreate((EnumContext) rc));
162165
} else {
163166
return create(((InvocationContext) rc).functionCall());
164167
}
@@ -234,6 +237,15 @@ private List<Expression> getArgs(ArgsContext args) throws LJError {
234237
return le;
235238
}
236239

240+
private String enumCreate(EnumContext rc) {
241+
String enumText = rc.enumCall().getText();
242+
int lastDot = enumText.lastIndexOf('.');
243+
String enumClass = enumText.substring(0, lastDot);
244+
String enumConst = enumText.substring(lastDot + 1);
245+
String varName = String.format(Formats.ENUM_VALUE, enumClass, enumConst);
246+
return varName;
247+
}
248+
237249
private Expression literalCreate(LiteralContext literalContext) throws LJError {
238250
if (literalContext.BOOL() != null)
239251
return new LiteralBoolean(literalContext.BOOL().getText());

liquidjava-verifier/src/main/java/liquidjava/smt/TranslatorContextToZ3.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ private static Expr<?> getExpr(Context z3, String name, CtTypeReference<?> type)
4848
case "long", "java.lang.Long" -> z3.mkRealConst(name);
4949
case "float", "double", "java.lang.Float", "java.lang.Double" -> (FPExpr) z3.mkConst(name, z3.mkFPSort64());
5050
case "int[]" -> z3.mkArrayConst(name, z3.mkIntSort(), z3.mkIntSort());
51-
default -> z3.mkConst(name, z3.mkUninterpretedSort(typeName));
51+
case "java.lang.Enum" -> z3.mkIntConst(name);
52+
default -> {
53+
if (type.isEnum())
54+
yield z3.mkIntConst(name);
55+
yield z3.mkConst(name, z3.mkUninterpretedSort(typeName));
56+
}
5257
};
5358
}
5459

@@ -88,6 +93,7 @@ static Sort getSort(Context z3, String sort) {
8893
case "float", "java.lang.Float" -> z3.mkFPSort32();
8994
case "double", "java.lang.Double" -> z3.mkFPSortDouble();
9095
case "int[]" -> z3.mkArraySort(z3.mkIntSort(), z3.mkIntSort());
96+
case "java.lang.Enum" -> z3.getIntSort();
9197
case "String" -> z3.getStringSort();
9298
case "void" -> z3.mkUninterpretedSort("void");
9399
default -> z3.mkUninterpretedSort(sort);

liquidjava-verifier/src/main/java/liquidjava/utils/constants/Formats.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ public final class Formats {
55
public static final String INSTANCE = "#%s_%d";
66
public static final String THIS = "this#%s";
77
public static final String RET = "#ret_%d";
8+
public static final String ENUM_VALUE = "enum#%s#%s";
89
}

0 commit comments

Comments
 (0)