From b5ea07a8656938019150d1656f5f721fc3a30937 Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Sun, 29 Dec 2024 17:19:48 +0100 Subject: [PATCH 1/6] move class hierarchy computation to standalone file --- .../bldl/astParsing/AstManipulator.java | 42 +-------- .../astParsing/ClassHierarchyComputer.java | 85 +++++++++++++++++++ 2 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 src/main/java/io/github/bldl/astParsing/ClassHierarchyComputer.java diff --git a/src/main/java/io/github/bldl/astParsing/AstManipulator.java b/src/main/java/io/github/bldl/astParsing/AstManipulator.java index 7b6408e..c3e5613 100644 --- a/src/main/java/io/github/bldl/astParsing/AstManipulator.java +++ b/src/main/java/io/github/bldl/astParsing/AstManipulator.java @@ -48,7 +48,8 @@ public AstManipulator(Messager messager, String sourceFolder) { this.sourceFolder = sourceFolder; sourceRoot = new SourceRoot( CodeGenerationUtils.mavenModuleRoot(AstManipulator.class).resolve(sourceFolder)); - classHierarchy = computeClassHierarchy(); + sourceRoot.getParserConfiguration().setSymbolResolver(symbolSolver); + classHierarchy = (new ClassHierarchyComputer(sourceRoot, messager, sourceFolder)).computeClassHierarchy(); } public void applyChanges() { @@ -110,13 +111,6 @@ public Visitable visit(TypeParameter n, Void arg) { }, null); } - public ClassHierarchyGraph computeClassHierarchy() { - ClassHierarchyGraph g = new ClassHierarchyGraph<>(); - g.addVertex("Object"); - computeClassHierarchyRec(g, Paths.get(sourceFolder).toFile(), ""); - return g; - } - private ClassData computeClassData(String cls, String packageName, Map mp) { CompilationUnit cu = sourceRoot.parse(packageName, cls); Map indexAndBound = new HashMap<>(); @@ -163,35 +157,7 @@ private void changeAST(File dir, ClassData classData, Map me } } - private void computeClassHierarchyRec(ClassHierarchyGraph g, File dir, String packageName) { - for (File file : dir.listFiles()) { - String fileName = file.getName(); - if (file.isDirectory()) { - if (!fileName.equals(OUTPUT_NAME)) - computeClassHierarchyRec(g, file, appendPackageDeclaration(packageName, fileName)); - continue; - } - if (!isJavaFile(file)) - continue; - - CompilationUnit cu = sourceRoot.parse(packageName, fileName); - - cu.findAll(ClassOrInterfaceDeclaration.class).forEach(cls -> { - NodeList supertypes = cls.getExtendedTypes(); - supertypes.addAll(cls.getImplementedTypes()); - g.addVertex(cls.getNameAsString()); - for (ClassOrInterfaceType supertype : supertypes) { - if (!g.containsVertex(supertype.getNameAsString())) - g.addVertex(supertype.getNameAsString()); - g.addEdge(supertype.getNameAsString(), cls.getNameAsString()); - } - if (supertypes.isEmpty()) - g.addEdge("Object", cls.getNameAsString()); - }); - } - } - - private boolean isJavaFile(File file) { + public static boolean isJavaFile(File file) { return file.getName().endsWith(".java"); } @@ -200,7 +166,7 @@ private void changePackageDeclaration(CompilationUnit cu) { cu.setPackageDeclaration(new PackageDeclaration(new Name(newPackageName))); } - private String appendPackageDeclaration(String existing, String toAppend) { + public static String appendPackageDeclaration(String existing, String toAppend) { if (existing.equals("")) return toAppend; return existing + "." + toAppend; diff --git a/src/main/java/io/github/bldl/astParsing/ClassHierarchyComputer.java b/src/main/java/io/github/bldl/astParsing/ClassHierarchyComputer.java new file mode 100644 index 0000000..1a0ed35 --- /dev/null +++ b/src/main/java/io/github/bldl/astParsing/ClassHierarchyComputer.java @@ -0,0 +1,85 @@ +package io.github.bldl.astParsing; + +import java.io.File; +import java.nio.file.Paths; + +import javax.annotation.processing.Messager; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.EnumDeclaration; +import com.github.javaparser.ast.body.RecordDeclaration; +import com.github.javaparser.ast.body.TypeDeclaration; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.utils.SourceRoot; + +import io.github.bldl.graph.ClassHierarchyGraph; + +public class ClassHierarchyComputer { + + private SourceRoot sourceRoot; + private Messager messager; + private String sourceFolder; + + public ClassHierarchyComputer(SourceRoot sourceRoot, Messager messager, String sourceFolder) { + this.sourceRoot = sourceRoot; + this.messager = messager; + this.sourceFolder = sourceFolder; + } + + public ClassHierarchyGraph computeClassHierarchy() { + ClassHierarchyGraph g = new ClassHierarchyGraph<>(); + g.addVertex("Object"); + computeClassHierarchyRec(g, Paths.get(sourceFolder).toFile(), ""); + return g; + } + + private void computeClassHierarchyRec(ClassHierarchyGraph g, File dir, String packageName) { + for (File file : dir.listFiles()) { + String fileName = file.getName(); + if (file.isDirectory()) { + if (!fileName.equals(AstManipulator.OUTPUT_NAME)) + computeClassHierarchyRec(g, file, AstManipulator.appendPackageDeclaration(packageName, fileName)); + continue; + } + if (!AstManipulator.isJavaFile(file)) + continue; + + CompilationUnit cu = sourceRoot.parse(packageName, fileName); + + cu.findAll(RecordDeclaration.class).forEach(record -> { + NodeList supertypes = record.getImplementedTypes(); + handleDeclaration(packageName, g, supertypes, record); + }); + + cu.findAll(EnumDeclaration.class).forEach(enm -> { + NodeList supertypes = enm.getImplementedTypes(); + handleDeclaration(packageName, g, supertypes, enm); + }); + + cu.findAll(ClassOrInterfaceDeclaration.class).forEach(cls -> { + NodeList supertypes = cls.getExtendedTypes(); + supertypes.addAll(cls.getImplementedTypes()); + handleDeclaration(packageName, g, supertypes, cls); + }); + } + } + + private void handleDeclaration(String packageName, ClassHierarchyGraph g, + NodeList supertypes, + TypeDeclaration declaration) { + g.addVertex(getQualifiedName(packageName, declaration.getNameAsString())); + for (ClassOrInterfaceType supertype : supertypes) { + if (!g.containsVertex(supertype.getNameAsString())) + g.addVertex(supertype.getNameAsString()); + g.addEdge(supertype.getNameAsString(), declaration.getNameAsString()); + } + if (supertypes.isEmpty()) + g.addEdge("Object", declaration.getNameAsString()); + } + + private String getQualifiedName(String packageName, String typeName) { + return packageName + "." + typeName; + } +} From d00c6a8449fcdb071309ee4185a0218109b2b47c Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Sun, 29 Dec 2024 17:20:00 +0100 Subject: [PATCH 2/6] set up type solver --- .../java/io/github/bldl/astParsing/AstManipulator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/io/github/bldl/astParsing/AstManipulator.java b/src/main/java/io/github/bldl/astParsing/AstManipulator.java index c3e5613..f1305e4 100644 --- a/src/main/java/io/github/bldl/astParsing/AstManipulator.java +++ b/src/main/java/io/github/bldl/astParsing/AstManipulator.java @@ -11,6 +11,10 @@ import com.github.javaparser.ast.type.TypeParameter; import com.github.javaparser.ast.visitor.ModifierVisitor; import com.github.javaparser.ast.visitor.Visitable; +import com.github.javaparser.symbolsolver.JavaSymbolSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; import com.github.javaparser.utils.CodeGenerationUtils; import com.github.javaparser.utils.SourceRoot; import io.github.bldl.annotationProcessing.annotations.MyVariance; @@ -46,6 +50,10 @@ public class AstManipulator { public AstManipulator(Messager messager, String sourceFolder) { this.messager = messager; this.sourceFolder = sourceFolder; + CombinedTypeSolver typeSolver = new CombinedTypeSolver(); + typeSolver.add(new ReflectionTypeSolver()); + typeSolver.add(new JavaParserTypeSolver(Paths.get("src/main/java"))); + JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver); sourceRoot = new SourceRoot( CodeGenerationUtils.mavenModuleRoot(AstManipulator.class).resolve(sourceFolder)); sourceRoot.getParserConfiguration().setSymbolResolver(symbolSolver); From a67cb98af6267aa7ef45283e0411157042c7fdf4 Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Sun, 29 Dec 2024 22:33:33 +0100 Subject: [PATCH 3/6] refactor --- .../io/github/bldl/astParsing/AstManipulator.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/github/bldl/astParsing/AstManipulator.java b/src/main/java/io/github/bldl/astParsing/AstManipulator.java index f1305e4..8fa70f9 100644 --- a/src/main/java/io/github/bldl/astParsing/AstManipulator.java +++ b/src/main/java/io/github/bldl/astParsing/AstManipulator.java @@ -181,7 +181,7 @@ public static String appendPackageDeclaration(String existing, String toAppend) } private Map> collectMethodParams(CompilationUnit cu, ClassData classData) { - Map> mp = new HashMap<>(); + Map> methodParams = new HashMap<>(); cu.findAll(MethodDeclaration.class).forEach(dec -> { NodeList params = dec.getParameters(); for (int i = 0; i < params.size(); ++i) { @@ -191,21 +191,21 @@ private Map> collectMethodParams(CompilationUnit cu, ClassOrInterfaceType type = ((ClassOrInterfaceType) param.getType()); String methodName = dec.getNameAsString(); if (type.getNameAsString().equals(classData.className())) { - mp.putIfAbsent(methodName, new HashMap<>()); - mp.get(methodName).put(i, + methodParams.putIfAbsent(methodName, new HashMap<>()); + methodParams.get(methodName).put(i, type); } } }); - return mp; + return methodParams; } private Map collectMethodTypes(CompilationUnit cu) { - Map mp = new HashMap<>(); + Map methodTypes = new HashMap<>(); cu.findAll(MethodDeclaration.class).forEach(dec -> { String methodName = dec.getNameAsString(); - mp.put(methodName, dec.getType()); + methodTypes.put(methodName, dec.getType()); }); - return mp; + return methodTypes; } } \ No newline at end of file From 55a718b4ee1f3b22ddb63f3e20378fa716571180 Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Sun, 29 Dec 2024 22:33:50 +0100 Subject: [PATCH 4/6] use type solver to do subtyping checks --- .../visitors/SubtypingCheckVisitor.java | 201 +++++++----------- 1 file changed, 71 insertions(+), 130 deletions(-) diff --git a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java index f33add9..d8830b1 100644 --- a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java +++ b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java @@ -2,7 +2,6 @@ import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import javax.annotation.processing.Messager; @@ -10,33 +9,23 @@ import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.AssignExpr; -import com.github.javaparser.ast.expr.BinaryExpr; -import com.github.javaparser.ast.expr.CastExpr; -import com.github.javaparser.ast.expr.ClassExpr; -import com.github.javaparser.ast.expr.EnclosedExpr; import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.ast.expr.FieldAccessExpr; -import com.github.javaparser.ast.expr.InstanceOfExpr; import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.NameExpr; -import com.github.javaparser.ast.expr.ObjectCreationExpr; -import com.github.javaparser.ast.expr.UnaryExpr; import com.github.javaparser.ast.stmt.ForEachStmt; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.resolution.types.ResolvedReferenceType; +import com.github.javaparser.resolution.types.ResolvedType; import io.github.bldl.annotationProcessing.annotations.MyVariance; import io.github.bldl.astParsing.util.ClassData; -import io.github.bldl.astParsing.util.ParamData; import io.github.bldl.graph.ClassHierarchyGraph; import io.github.bldl.util.Pair; public class SubtypingCheckVisitor extends VoidVisitorAdapter { private final Map> methodParams; - private Map methodTypes; private final Messager messager; - private final Map varsToWatchMap = new HashMap<>(); private final ClassData classData; private final ClassHierarchyGraph classHierarchy; @@ -45,44 +34,40 @@ public SubtypingCheckVisitor(Map> methodParams, Map> varsToWatch, ClassData classData, ClassHierarchyGraph classHierarchy) { this.methodParams = methodParams; - this.methodTypes = methodTypes; this.messager = messager; this.classData = classData; this.classHierarchy = classHierarchy; - varsToWatch.forEach(p -> { - varsToWatchMap.put(p.first, p.second); - }); } public void visit(MethodCallExpr methodCall, Void arg) { super.visit(methodCall, arg); - if (!methodParams.containsKey(methodCall.getNameAsString())) + if (!methodParams.containsKey(methodCall.getNameAsString())) { + messager.printMessage(Kind.NOTE, "Don't have key for method: " + methodCall.getNameAsString()); return; - for (Entry param : methodParams.get(methodCall.getNameAsString()).entrySet()) { - Expression e = methodCall.getArgument(param.getKey()); - ClassOrInterfaceType argumentType = resolveType(e); - if (argumentType == null) { - messager.printMessage(Kind.WARNING, "Cannot resolve type for expression: " + e.toString()); - continue; + } + // ResolvedMethodDeclaration methodDeclaration = methodCall.resolve(); + for (int i = 0; i < methodCall.getArguments().size(); ++i) { + try { + ResolvedType argumentType = methodCall.getArgument(i).calculateResolvedType(), + parameterType = methodParams.get(methodCall.getNameAsString()).get(i).resolve(); + if (!argumentType.isReferenceType() || !parameterType.isReferenceType()) + continue; + boolean valid = isValidSubtype(parameterType, argumentType); + if (!valid) + messager.printMessage(Kind.ERROR, + String.format("Invalid subtype for method call: %s", methodCall.toString())); + } catch (Exception e) { + messager.printMessage(Kind.NOTE, methodCall.getArguments().toString()); + messager.printMessage(Kind.NOTE, methodParams.get(methodCall.getNameAsString()).toString()); } - boolean valid = isValidSubtype((ClassOrInterfaceType) param.getValue(), - argumentType, - classData.params()); - if (!valid) - messager.printMessage(Kind.ERROR, - String.format("Invalid subtype for method call: %s", methodCall.toString())); } } public void visit(AssignExpr assignExpr, Void arg) { super.visit(assignExpr, arg); - ClassOrInterfaceType assignedType = resolveType(assignExpr.getValue()), - assigneeType = resolveType(assignExpr.getTarget()); - if (assignedType == null || assigneeType == null) { - messager.printMessage(Kind.WARNING, "Cannot resolve type for expression: " + assignExpr.toString()); - return; - } - boolean valid = isValidSubtype(assigneeType, assignedType, null); + ResolvedType assignedType = assignExpr.getValue().calculateResolvedType(), + assigneeType = assignExpr.getTarget().calculateResolvedType(); + boolean valid = isValidSubtype(assigneeType, assignedType); if (!valid) messager.printMessage(Kind.ERROR, String.format("Invalid subtype for assignment expression call: %s\n%s is not a subtype of %s", @@ -96,18 +81,13 @@ public void visit(ForEachStmt n, Void arg) { public void visit(VariableDeclarator declaration, Void arg) { super.visit(declaration, arg); - Type assigneeType = declaration.getType(); - if (!(assigneeType instanceof ClassOrInterfaceType)) - return; + ResolvedType assigneeType = declaration.getType().resolve(); Optional initializer = declaration.getInitializer(); if (initializer.isEmpty()) return; - ClassOrInterfaceType assignedType = resolveType(initializer.get()); - if (assignedType == null) - return; - boolean valid = isValidSubtype((ClassOrInterfaceType) assigneeType, - assignedType, - classData.params()); + ResolvedType assignedType = initializer.get().calculateResolvedType(); + boolean valid = isValidSubtype(assigneeType, + assignedType); if (!valid) messager.printMessage(Kind.ERROR, String.format("Invalid subtype for variable declaration: %s\n %s is not a subtype of %s", @@ -115,114 +95,75 @@ public void visit(VariableDeclarator declaration, Void arg) { assignedType.toString(), assigneeType.toString())); } - private ClassOrInterfaceType resolveType(Expression e) { - if (e instanceof EnclosedExpr) - return resolveType(((EnclosedExpr) e).getInner()); - if (e instanceof UnaryExpr) - return resolveType(((UnaryExpr) e).getExpression()); - if (e instanceof BinaryExpr) { - BinaryExpr binExp = (BinaryExpr) e; - ClassOrInterfaceType t1 = resolveType(binExp.getLeft()), t2 = resolveType(binExp.getRight()); - if (t1.asString().equals(t2.asString())) - return t1; - return null; - } - if (e instanceof MethodCallExpr) { - Type t = methodTypes.get(((MethodCallExpr) e).getNameAsString()); - return t instanceof ClassOrInterfaceType ? (ClassOrInterfaceType) t : null; - } - if (e instanceof NameExpr) - return varsToWatchMap.getOrDefault(((NameExpr) e).getNameAsString(), null); - if (e instanceof FieldAccessExpr) - return varsToWatchMap.getOrDefault(((FieldAccessExpr) e).getNameAsString(), null); - if (e instanceof CastExpr) { - Type t = ((CastExpr) e).getType(); - return t instanceof ClassOrInterfaceType ? (ClassOrInterfaceType) t : null; - } - if (e instanceof ObjectCreationExpr) - return ((ObjectCreationExpr) e).getType(); - if (e instanceof ClassExpr) { - Type t = ((ClassExpr) e).getType(); - return t instanceof ClassOrInterfaceType ? (ClassOrInterfaceType) t : null; - } - if (e instanceof InstanceOfExpr) - return new ClassOrInterfaceType(null, "Boolean"); - return null; - } + private boolean isValidSubtype(ResolvedType aType, ResolvedType assignType) { + if (!aType.isReferenceType() || !assignType.isReferenceType()) + return true; - private boolean isValidSubtype(ClassOrInterfaceType assigneeType, ClassOrInterfaceType assignedType, - Map params) { - if (!classHierarchy.containsVertex(assigneeType.getNameAsString())) { + ResolvedReferenceType assigneeType = aType.asReferenceType(), assignedType = assignType.asReferenceType(); + if (!classHierarchy.containsVertex(assigneeType.getQualifiedName())) { messager.printMessage(Kind.WARNING, String.format("%s is not a user defined type, so no subtyping checks can be made", assigneeType)); return true; } - if (!classHierarchy.containsVertex(assignedType.getNameAsString())) { + if (!classHierarchy.containsVertex(assignedType.getQualifiedName())) { messager.printMessage(Kind.WARNING, String.format("%s is not a user defined type, so no subtyping checks can be made", assignedType)); return true; } - if (!assigneeType.getTypeArguments().isPresent() || !assignedType.getTypeArguments().isPresent()) + if (!assigneeType.typeParametersValues().isEmpty() || !assignedType.typeParametersValues().isEmpty()) return true; - var assigneeArgs = assigneeType.getTypeArguments().get(); - var assignedArgs = assignedType.getTypeArguments().get(); + var assigneeArgs = assigneeType.typeParametersValues(); + var assignedArgs = assignedType.typeParametersValues(); boolean isSubtype = true; - - if (assignedArgs.size() == 0) - return true; // cannot perform type inference - - if (assigneeType.getNameAsString().equals(classData.className())) { - Map mp = new HashMap<>(); + // TODO update classData to use qualified name + if (assigneeType.getQualifiedName().equals(classData.className())) { + Map paramVariance = new HashMap<>(); for (var param : classData.params().values()) - mp.put(param.index(), param.variance()); + paramVariance.put(param.index(), param.variance()); for (int i = 0; i < assigneeArgs.size(); ++i) { - if (!(assignedArgs.get(i) instanceof ClassOrInterfaceType) - || !(assignedArgs.get(i) instanceof ClassOrInterfaceType)) { - continue; - } - if (!mp.containsKey(i)) { - isSubtype = isSubtype && isValidSubtype((ClassOrInterfaceType) assigneeArgs.get(i), - (ClassOrInterfaceType) assignedArgs.get(i), - params); + isSubtype = isSubtype && isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i)); + ResolvedType assigneeArgType = assigneeArgs.get(i); + ResolvedType assignedArgType = assignedArgs.get(i); + if (!paramVariance.containsKey(i) || !assigneeArgType.isReferenceType() + || !assignedArgType.isReferenceType()) continue; - } - switch (mp.get(i).variance()) { + + switch (paramVariance.get(i).variance()) { case COVARIANT: - return classHierarchy.isDescendant( - ((ClassOrInterfaceType) assigneeArgs.get(i)).getNameAsString(), - ((ClassOrInterfaceType) assignedArgs.get(i)).getNameAsString(), mp.get(i).depth()); + isSubtype = isSubtype && classHierarchy.isDescendant( + assigneeArgType.asReferenceType().getQualifiedName(), + assignedArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth()); + break; case CONTRAVARIANT: - return classHierarchy.isDescendant( - ((ClassOrInterfaceType) assignedArgs.get(i)).getNameAsString(), - ((ClassOrInterfaceType) assigneeArgs.get(i)).getNameAsString(), mp.get(i).depth()); + isSubtype = isSubtype && classHierarchy.isDescendant( + assignedArgType.asReferenceType().getQualifiedName(), + assigneeArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth()); + break; case BIVARIANT: - return classHierarchy.isDescendant( - ((ClassOrInterfaceType) assigneeArgs.get(i)).getNameAsString(), - ((ClassOrInterfaceType) assignedArgs.get(i)).getNameAsString(), mp.get(i).depth()) + isSubtype = isSubtype && classHierarchy.isDescendant( + assigneeArgType.asReferenceType().getQualifiedName(), + assignedArgType.asReferenceType().getQualifiedName(), paramVariance.get(i).depth()) || classHierarchy.isDescendant( - ((ClassOrInterfaceType) assignedArgs.get(i)).getNameAsString(), - ((ClassOrInterfaceType) assigneeArgs.get(i)).getNameAsString(), - mp.get(i).depth()); + assignedArgType.asReferenceType().getQualifiedName(), + assigneeArgType.asReferenceType().getQualifiedName(), + paramVariance.get(i).depth()); + break; case SIDEVARIANT: - return classHierarchy.sameLevel(((ClassOrInterfaceType) assignedArgs.get(i)).getNameAsString(), - ((ClassOrInterfaceType) assigneeArgs.get(i)).getNameAsString()); + isSubtype = isSubtype && classHierarchy.sameLevel( + assignedArgType.asReferenceType().getQualifiedName(), + assigneeArgType.asReferenceType().getQualifiedName()); + break; default: - return false; + return true; } } + return isSubtype; } - for (int i = 0; i < assigneeArgs.size(); ++i) { - if (!(assignedArgs.get(i) instanceof ClassOrInterfaceType) - || !(assignedArgs.get(i) instanceof ClassOrInterfaceType)) { - continue; - } - isSubtype = isSubtype && isValidSubtype((ClassOrInterfaceType) assigneeArgs.get(i), - (ClassOrInterfaceType) assignedArgs.get(i), - params); - } - return classHierarchy.isDescendant(assigneeType.getNameAsString(), - assignedType.getNameAsString(), -1); + for (int i = 0; i < assigneeArgs.size(); ++i) + isSubtype = isSubtype && isValidSubtype(assigneeArgs.get(i), assignedArgs.get(i)); + return classHierarchy.isDescendant(assigneeType.getQualifiedName(), + assignedType.getQualifiedName(), -1); } } From ebcff8e23449584b130aca7becfdc56339df35f7 Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Mon, 30 Dec 2024 22:47:26 +0100 Subject: [PATCH 5/6] remove unused params --- .../io/github/bldl/astParsing/AstManipulator.java | 13 +------------ .../astParsing/visitors/SubtypingCheckVisitor.java | 7 ++----- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/github/bldl/astParsing/AstManipulator.java b/src/main/java/io/github/bldl/astParsing/AstManipulator.java index 8fa70f9..2426a05 100644 --- a/src/main/java/io/github/bldl/astParsing/AstManipulator.java +++ b/src/main/java/io/github/bldl/astParsing/AstManipulator.java @@ -153,9 +153,7 @@ private void changeAST(File dir, ClassData classData, Map me Set> varsToWatch = new HashSet<>(); cu.accept(new VariableCollector(classData), varsToWatch); cu.accept( - new SubtypingCheckVisitor(collectMethodParams(cu, classData), collectMethodTypes(cu), messager, - varsToWatch, classData, - classHierarchy), + new SubtypingCheckVisitor(collectMethodParams(cu, classData), messager, classData, classHierarchy), null); cu.accept(new TypeEraserVisitor(classData), null); for (Pair var : varsToWatch) { @@ -199,13 +197,4 @@ private Map> collectMethodParams(CompilationUnit cu, }); return methodParams; } - - private Map collectMethodTypes(CompilationUnit cu) { - Map methodTypes = new HashMap<>(); - cu.findAll(MethodDeclaration.class).forEach(dec -> { - String methodName = dec.getNameAsString(); - methodTypes.put(methodName, dec.getType()); - }); - return methodTypes; - } } \ No newline at end of file diff --git a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java index d8830b1..923ef71 100644 --- a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java +++ b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java @@ -3,7 +3,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.Set; import javax.annotation.processing.Messager; import javax.tools.Diagnostic.Kind; @@ -12,7 +11,6 @@ import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.stmt.ForEachStmt; -import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.Type; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.types.ResolvedReferenceType; @@ -21,7 +19,6 @@ import io.github.bldl.annotationProcessing.annotations.MyVariance; import io.github.bldl.astParsing.util.ClassData; import io.github.bldl.graph.ClassHierarchyGraph; -import io.github.bldl.util.Pair; public class SubtypingCheckVisitor extends VoidVisitorAdapter { private final Map> methodParams; @@ -29,9 +26,9 @@ public class SubtypingCheckVisitor extends VoidVisitorAdapter { private final ClassData classData; private final ClassHierarchyGraph classHierarchy; - public SubtypingCheckVisitor(Map> methodParams, Map methodTypes, + public SubtypingCheckVisitor(Map> methodParams, Messager messager, - Set> varsToWatch, ClassData classData, + ClassData classData, ClassHierarchyGraph classHierarchy) { this.methodParams = methodParams; this.messager = messager; From a8be75f7c291e0d0d778198b4361a5efff5da5c0 Mon Sep 17 00:00:00 2001 From: FelixAnthonisen Date: Sun, 5 Jan 2025 16:25:05 +0100 Subject: [PATCH 6/6] collect all methods --- .../bldl/astParsing/AstManipulator.java | 18 +++++++-------- .../visitors/SubtypingCheckVisitor.java | 23 ++++++------------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/bldl/astParsing/AstManipulator.java b/src/main/java/io/github/bldl/astParsing/AstManipulator.java index 2426a05..4e40fbe 100644 --- a/src/main/java/io/github/bldl/astParsing/AstManipulator.java +++ b/src/main/java/io/github/bldl/astParsing/AstManipulator.java @@ -181,18 +181,16 @@ public static String appendPackageDeclaration(String existing, String toAppend) private Map> collectMethodParams(CompilationUnit cu, ClassData classData) { Map> methodParams = new HashMap<>(); cu.findAll(MethodDeclaration.class).forEach(dec -> { + String methodName = dec.getNameAsString(); + if (methodParams.containsKey(methodName)) { + messager.printMessage(Kind.ERROR, "Duplicate methods inside a class. Can't handle polymorphism."); + return; + } + methodParams.put(methodName, new HashMap<>()); NodeList params = dec.getParameters(); for (int i = 0; i < params.size(); ++i) { - Parameter param = params.get(i); - if (!(param.getType() instanceof ClassOrInterfaceType)) - continue; - ClassOrInterfaceType type = ((ClassOrInterfaceType) param.getType()); - String methodName = dec.getNameAsString(); - if (type.getNameAsString().equals(classData.className())) { - methodParams.putIfAbsent(methodName, new HashMap<>()); - methodParams.get(methodName).put(i, - type); - } + Type type = params.get(i).getType(); + methodParams.get(methodName).put(i, type); } }); return methodParams; diff --git a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java index 923ef71..c8d5ba3 100644 --- a/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java +++ b/src/main/java/io/github/bldl/astParsing/visitors/SubtypingCheckVisitor.java @@ -38,25 +38,16 @@ public SubtypingCheckVisitor(Map> methodParams, public void visit(MethodCallExpr methodCall, Void arg) { super.visit(methodCall, arg); - if (!methodParams.containsKey(methodCall.getNameAsString())) { - messager.printMessage(Kind.NOTE, "Don't have key for method: " + methodCall.getNameAsString()); + if (!methodParams.containsKey(methodCall.getNameAsString())) return; - } // ResolvedMethodDeclaration methodDeclaration = methodCall.resolve(); for (int i = 0; i < methodCall.getArguments().size(); ++i) { - try { - ResolvedType argumentType = methodCall.getArgument(i).calculateResolvedType(), - parameterType = methodParams.get(methodCall.getNameAsString()).get(i).resolve(); - if (!argumentType.isReferenceType() || !parameterType.isReferenceType()) - continue; - boolean valid = isValidSubtype(parameterType, argumentType); - if (!valid) - messager.printMessage(Kind.ERROR, - String.format("Invalid subtype for method call: %s", methodCall.toString())); - } catch (Exception e) { - messager.printMessage(Kind.NOTE, methodCall.getArguments().toString()); - messager.printMessage(Kind.NOTE, methodParams.get(methodCall.getNameAsString()).toString()); - } + ResolvedType argumentType = methodCall.getArgument(i).calculateResolvedType(), + parameterType = methodParams.get(methodCall.getNameAsString()).get(i).resolve(); + boolean valid = isValidSubtype(parameterType, argumentType); + if (!valid) + messager.printMessage(Kind.ERROR, + String.format("Invalid subtype for method call: %s", methodCall.toString())); } }