5
5
import java .util .Map .Entry ;
6
6
import java .util .Optional ;
7
7
import java .util .Set ;
8
-
9
8
import javax .annotation .processing .Messager ;
10
9
import javax .tools .Diagnostic .Kind ;
11
10
27
26
import com .github .javaparser .ast .type .Type ;
28
27
import com .github .javaparser .ast .visitor .VoidVisitorAdapter ;
29
28
29
+ import io .github .bldl .annotationProcessing .annotations .MyVariance ;
30
30
import io .github .bldl .astParsing .util .ClassData ;
31
31
import io .github .bldl .astParsing .util .ParamData ;
32
32
import io .github .bldl .graph .ClassHierarchyGraph ;
@@ -40,10 +40,12 @@ public class SubtypingCheckVisitor extends VoidVisitorAdapter<Void> {
40
40
private final ClassData classData ;
41
41
private final ClassHierarchyGraph <String > classHierarchy ;
42
42
43
- public SubtypingCheckVisitor (Map <String , Map <Integer , Type >> methodParams , Messager messager ,
43
+ public SubtypingCheckVisitor (Map <String , Map <Integer , Type >> methodParams , Map <String , Type > methodTypes ,
44
+ Messager messager ,
44
45
Set <Pair <String , ClassOrInterfaceType >> varsToWatch , ClassData classData ,
45
46
ClassHierarchyGraph <String > classHierarchy ) {
46
47
this .methodParams = methodParams ;
48
+ this .methodTypes = methodTypes ;
47
49
this .messager = messager ;
48
50
this .classData = classData ;
49
51
this .classHierarchy = classHierarchy ;
@@ -77,6 +79,16 @@ public void visit(AssignExpr assignExpr, Void arg) {
77
79
super .visit (assignExpr , arg );
78
80
ClassOrInterfaceType assignedType = resolveType (assignExpr .getValue ()),
79
81
assigneeType = resolveType (assignExpr .getTarget ());
82
+ if (assignedType == null || assigneeType == null ) {
83
+ messager .printMessage (Kind .WARNING , "Cannot resolve type for expression: " + assignExpr .toString ());
84
+ return ;
85
+ }
86
+ boolean valid = isValidSubtype (assigneeType , assignedType , null );
87
+ if (!valid )
88
+ messager .printMessage (Kind .ERROR ,
89
+ String .format ("Invalid subtype for assignment expression call: %s\n %s is not a subtype of %s" ,
90
+ assignExpr .toString (),
91
+ assignedType .toString (), assigneeType .toString ()));
80
92
}
81
93
82
94
public void visit (ForEachStmt n , Void arg ) {
@@ -92,12 +104,16 @@ public void visit(VariableDeclarator declaration, Void arg) {
92
104
if (initializer .isEmpty ())
93
105
return ;
94
106
ClassOrInterfaceType assignedType = resolveType (initializer .get ());
107
+ if (assignedType == null )
108
+ return ;
95
109
boolean valid = isValidSubtype ((ClassOrInterfaceType ) assigneeType ,
96
110
assignedType ,
97
111
classData .params ());
98
112
if (!valid )
99
113
messager .printMessage (Kind .ERROR ,
100
- String .format ("Invalid subtype for variable declaration: %s" , declaration .toString ()));
114
+ String .format ("Invalid subtype for variable declaration: %s\n %s is not a subtype of %s" ,
115
+ declaration .toString (),
116
+ assignedType .toString (), assigneeType .toString ()));
101
117
}
102
118
103
119
private ClassOrInterfaceType resolveType (Expression e ) {
@@ -147,14 +163,56 @@ private boolean isValidSubtype(ClassOrInterfaceType assigneeType, ClassOrInterfa
147
163
String .format ("%s is not a user defined type, so no subtyping checks can be made" , assignedType ));
148
164
return true ;
149
165
}
150
- return true ;
151
- // switch (annotation.variance()) {
152
- // case COVARIANT:
153
- // return classHierarchy.isDescendant(assignedType, assigneeType);
154
- // case CONTRAVARIANT:
155
- // return classHierarchy.isDescendant(assigneeType, assignedType);
156
- // default:
157
- // return false;
158
- // }
166
+ if (!assigneeType .getTypeArguments ().isPresent () || !assignedType .getTypeArguments ().isPresent ())
167
+ return true ;
168
+
169
+ var assigneeArgs = assigneeType .getTypeArguments ().get ();
170
+ var assignedArgs = assignedType .getTypeArguments ().get ();
171
+ boolean isSubtype = true ;
172
+
173
+ if (assignedArgs .size () == 0 )
174
+ return true ; // cannot perform type inference
175
+
176
+ if (assigneeType .getNameAsString ().equals (classData .className ())) {
177
+ Map <Integer , MyVariance > mp = new HashMap <>();
178
+ for (var param : classData .params ().values ())
179
+ mp .put (param .index (), param .variance ());
180
+ for (int i = 0 ; i < assigneeArgs .size (); ++i ) {
181
+ if (!(assignedArgs .get (i ) instanceof ClassOrInterfaceType )
182
+ || !(assignedArgs .get (i ) instanceof ClassOrInterfaceType )) {
183
+ continue ;
184
+ }
185
+ if (!mp .containsKey (i )) {
186
+ isSubtype = isSubtype && isValidSubtype ((ClassOrInterfaceType ) assigneeArgs .get (i ),
187
+ (ClassOrInterfaceType ) assignedArgs .get (i ),
188
+ params );
189
+ continue ;
190
+ }
191
+ switch (mp .get (i ).variance ()) {
192
+ case COVARIANT :
193
+ return classHierarchy .isDescendant (
194
+ ((ClassOrInterfaceType ) assigneeArgs .get (i )).getNameAsString (),
195
+ ((ClassOrInterfaceType ) assignedArgs .get (i )).getNameAsString (), mp .get (i ).depth ());
196
+ case CONTRAVARIANT :
197
+ return classHierarchy .isDescendant (
198
+ ((ClassOrInterfaceType ) assignedArgs .get (i )).getNameAsString (),
199
+ ((ClassOrInterfaceType ) assigneeArgs .get (i )).getNameAsString (), mp .get (i ).depth ());
200
+ default :
201
+ return false ;
202
+ }
203
+ }
204
+ }
205
+
206
+ for (int i = 0 ; i < assigneeArgs .size (); ++i ) {
207
+ if (!(assignedArgs .get (i ) instanceof ClassOrInterfaceType )
208
+ || !(assignedArgs .get (i ) instanceof ClassOrInterfaceType )) {
209
+ continue ;
210
+ }
211
+ isSubtype = isSubtype && isValidSubtype ((ClassOrInterfaceType ) assigneeArgs .get (i ),
212
+ (ClassOrInterfaceType ) assignedArgs .get (i ),
213
+ params );
214
+ }
215
+ return classHierarchy .isDescendant (assigneeType .getNameAsString (),
216
+ assignedType .getNameAsString (), -1 );
159
217
}
160
218
}
0 commit comments