Skip to content

Commit 2c2ec82

Browse files
further implement subtyping checks
1 parent b486849 commit 2c2ec82

File tree

2 files changed

+68
-19
lines changed

2 files changed

+68
-19
lines changed

src/main/java/io/github/bldl/annotationProcessing/annotations/MyVariance.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,8 @@
5757
*/
5858
public boolean strict() default false;
5959

60+
public Class<?>[] requiredSubtypes() default {};
61+
62+
public Class<?>[] requiredSupertypes() default {};
63+
6064
}

src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.HashMap;
44
import java.util.Map;
55
import java.util.Optional;
6+
67
import javax.annotation.processing.Messager;
78
import javax.tools.Diagnostic.Kind;
89

@@ -17,6 +18,7 @@
1718
import com.github.javaparser.resolution.types.ResolvedType;
1819

1920
import io.github.bldl.annotationProcessing.annotations.MyVariance;
21+
import io.github.bldl.astParsing.AstManipulator;
2022
import io.github.bldl.astParsing.util.ClassData;
2123
import io.github.bldl.graph.ClassHierarchyGraph;
2224

@@ -25,6 +27,7 @@ public class SubtypingCheckVisitor extends VoidVisitorAdapter<Void> {
2527
private final Messager messager;
2628
private final ClassData classData;
2729
private final ClassHierarchyGraph<String> classHierarchy;
30+
private final String qualifiedClassName;
2831

2932
public SubtypingCheckVisitor(Map<String, Map<Integer, Type>> methodParams,
3033
Messager messager,
@@ -34,6 +37,7 @@ public SubtypingCheckVisitor(Map<String, Map<Integer, Type>> methodParams,
3437
this.messager = messager;
3538
this.classData = classData;
3639
this.classHierarchy = classHierarchy;
40+
qualifiedClassName = AstManipulator.appendPackageDeclaration(classData.packageName(), classData.className());
3741
}
3842

3943
public void visit(MethodCallExpr methodCall, Void arg) {
@@ -45,9 +49,11 @@ public void visit(MethodCallExpr methodCall, Void arg) {
4549
ResolvedType argumentType = methodCall.getArgument(i).calculateResolvedType(),
4650
parameterType = methodParams.get(methodCall.getNameAsString()).get(i).resolve();
4751
boolean valid = isValidSubtype(parameterType, argumentType);
48-
if (!valid)
52+
if (!valid) {
4953
messager.printMessage(Kind.ERROR,
5054
String.format("Invalid subtype for method call: %s", methodCall.toString()));
55+
56+
}
5157
}
5258
}
5359

@@ -56,11 +62,12 @@ public void visit(AssignExpr assignExpr, Void arg) {
5662
ResolvedType assignedType = assignExpr.getValue().calculateResolvedType(),
5763
assigneeType = assignExpr.getTarget().calculateResolvedType();
5864
boolean valid = isValidSubtype(assigneeType, assignedType);
59-
if (!valid)
65+
if (!valid) {
6066
messager.printMessage(Kind.ERROR,
6167
String.format("Invalid subtype for assignment expression call: %s\n%s is not a subtype of %s",
6268
assignExpr.toString(),
6369
assignedType.toString(), assigneeType.toString()));
70+
}
6471
}
6572

6673
public void visit(ForEachStmt n, Void arg) {
@@ -76,18 +83,26 @@ public void visit(VariableDeclarator declaration, Void arg) {
7683
ResolvedType assignedType = initializer.get().calculateResolvedType();
7784
boolean valid = isValidSubtype(assigneeType,
7885
assignedType);
79-
if (!valid)
86+
if (!valid) {
8087
messager.printMessage(Kind.ERROR,
8188
String.format("Invalid subtype for variable declaration: %s\n %s is not a subtype of %s",
8289
declaration.toString(),
8390
assignedType.toString(), assigneeType.toString()));
91+
92+
}
8493
}
8594

8695
private boolean isValidSubtype(ResolvedType aType, ResolvedType assignType) {
87-
if (!aType.isReferenceType() || !assignType.isReferenceType())
96+
var a = (Boolean.class);
97+
a.getName();
98+
if (!aType.isReferenceType() || !assignType.isReferenceType()) {
99+
messager.printMessage(Kind.NOTE,
100+
aType.toString() + " or " + assignType.toString() + " is not a reference type.");
88101
return true;
102+
}
89103

90104
ResolvedReferenceType assigneeType = aType.asReferenceType(), assignedType = assignType.asReferenceType();
105+
91106
if (!classHierarchy.containsVertex(assigneeType.getQualifiedName())) {
92107
messager.printMessage(Kind.WARNING,
93108
String.format("%s is not a user defined type, so no subtyping checks can be made", assigneeType));
@@ -98,38 +113,49 @@ private boolean isValidSubtype(ResolvedType aType, ResolvedType assignType) {
98113
String.format("%s is not a user defined type, so no subtyping checks can be made", assignedType));
99114
return true;
100115
}
101-
if (!assigneeType.typeParametersValues().isEmpty() || !assignedType.typeParametersValues().isEmpty())
116+
if (!assigneeType.typeParametersValues().isEmpty() || !assignedType.typeParametersValues().isEmpty()) {
117+
messager.printMessage(Kind.NOTE,
118+
"NO params for: " + assigneeType.toString() + " or " + assignedType.toString());
119+
messager.printMessage(Kind.NOTE,
120+
assigneeType.typeParametersValues().toString());
121+
messager.printMessage(Kind.NOTE,
122+
assignedType.typeParametersValues().toString());
102123
return true;
124+
}
103125

104126
var assigneeArgs = assigneeType.typeParametersValues();
105127
var assignedArgs = assignedType.typeParametersValues();
106-
boolean isSubtype = true;
107-
// TODO update classData to use qualified name
108-
if (assigneeType.getQualifiedName().equals(classData.className())) {
128+
boolean isSubtype = classHierarchy.isDescendant(assigneeType.getQualifiedName(),
129+
assignedType.getQualifiedName(), -1);
130+
if (assigneeType.getQualifiedName()
131+
.equals(qualifiedClassName)) {
132+
messager.printMessage(Kind.NOTE, "Found matching type for: " + assigneeType.getQualifiedName());
109133
Map<Integer, MyVariance> paramVariance = new HashMap<>();
110134
for (var param : classData.params().values())
111135
paramVariance.put(param.index(), param.variance());
112136
for (int i = 0; i < assigneeArgs.size(); ++i) {
113-
isSubtype = isSubtype && isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i));
137+
isSubtype &= isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i));
114138
ResolvedType assigneeArgType = assigneeArgs.get(i);
115139
ResolvedType assignedArgType = assignedArgs.get(i);
116140
if (!paramVariance.containsKey(i) || !assigneeArgType.isReferenceType()
117-
|| !assignedArgType.isReferenceType())
141+
|| !assignedArgType.isReferenceType()) {
142+
isSubtype &= isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i));
118143
continue;
119-
144+
}
145+
isSubtype &= checkRequiredTypes(assignedArgType, paramVariance.get(i));
120146
switch (paramVariance.get(i).variance()) {
121147
case COVARIANT:
122-
isSubtype = isSubtype && classHierarchy.isDescendant(
148+
isSubtype &= classHierarchy.isDescendant(
123149
assigneeArgType.asReferenceType().getQualifiedName(),
124150
assignedArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth());
125151
break;
126152
case CONTRAVARIANT:
127-
isSubtype = isSubtype && classHierarchy.isDescendant(
153+
isSubtype &= classHierarchy.isDescendant(
128154
assignedArgType.asReferenceType().getQualifiedName(),
129155
assigneeArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth());
130156
break;
131157
case BIVARIANT:
132-
isSubtype = isSubtype && classHierarchy.isDescendant(
158+
isSubtype &= classHierarchy.isDescendant(
133159
assigneeArgType.asReferenceType().getQualifiedName(),
134160
assignedArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth())
135161
|| classHierarchy.isDescendant(
@@ -138,20 +164,39 @@ private boolean isValidSubtype(ResolvedType aType, ResolvedType assignType) {
138164
paramVariance.get(i).depth());
139165
break;
140166
case SIDEVARIANT:
141-
isSubtype = isSubtype && classHierarchy.sameLevel(
167+
isSubtype &= classHierarchy.sameLevel(
142168
assignedArgType.asReferenceType().getQualifiedName(),
143169
assigneeArgType.asReferenceType().getQualifiedName());
144170
break;
145171
default:
146-
return true;
172+
break;
147173
}
148174
}
149175
return isSubtype;
150176
}
151177

152178
for (int i = 0; i < assigneeArgs.size(); ++i)
153-
isSubtype = isSubtype && isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i));
154-
return classHierarchy.isDescendant(assigneeType.getQualifiedName(),
155-
assignedType.getQualifiedName(), -1);
179+
isSubtype &= isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i));
180+
181+
return isSubtype;
182+
}
183+
184+
private boolean checkRequiredTypes(ResolvedType type, MyVariance variance) {
185+
for (Class<?> cls : variance.requiredSubtypes()) {
186+
if (!classHierarchy.isDescendant(type.asReferenceType().getQualifiedName(), cls.getName(), -1)) {
187+
messager.printMessage(Kind.ERROR,
188+
type.asReferenceType().getQualifiedName() + " does not have " + cls.getName() + " as subtype");
189+
return false;
190+
}
191+
}
192+
for (Class<?> cls : variance.requiredSupertypes()) {
193+
if (!classHierarchy.isDescendant(cls.getName(), type.asReferenceType().getQualifiedName(), -1)) {
194+
messager.printMessage(Kind.ERROR, type.asReferenceType().getQualifiedName() + " does not have "
195+
+ cls.getName() + " as supertype");
196+
return false;
197+
}
198+
199+
}
200+
return true;
156201
}
157202
}

0 commit comments

Comments
 (0)