diff --git a/src/main/java/me/tomassetti/symbolsolver/SourceFileInfoExtractor.java b/src/main/java/me/tomassetti/symbolsolver/SourceFileInfoExtractor.java index 98e230f4f9..b429202202 100644 --- a/src/main/java/me/tomassetti/symbolsolver/SourceFileInfoExtractor.java +++ b/src/main/java/me/tomassetti/symbolsolver/SourceFileInfoExtractor.java @@ -12,6 +12,7 @@ import com.github.javaparser.ast.body.VariableDeclarator; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.stmt.Statement; +import me.tomassetti.symbolsolver.model.typesystem.ReferenceTypeUsage; import me.tomassetti.symbolsolver.resolution.TypeSolver; import me.tomassetti.symbolsolver.model.declarations.TypeDeclaration; @@ -78,7 +79,7 @@ private void solveTypeDecl(ClassOrInterfaceDeclaration node) { TypeDeclaration typeDeclaration = JavaParserFacade.get(typeSolver).getTypeDeclaration(node); if (typeDeclaration.isClass()) { out.println("\n[ Class "+ typeDeclaration.getQualifiedName() + " ]"); - for (TypeDeclaration sc : typeDeclaration.asClass().getAllSuperClasses(typeSolver)) { + for (ReferenceTypeUsage sc : typeDeclaration.asClass().getAllSuperClasses(typeSolver)) { out.println(" superclass: " + sc.getQualifiedName()); } for (TypeDeclaration sc : typeDeclaration.asClass().getAllInterfaces(typeSolver)) { diff --git a/src/main/java/me/tomassetti/symbolsolver/model/declarations/ClassDeclaration.java b/src/main/java/me/tomassetti/symbolsolver/model/declarations/ClassDeclaration.java index f1cd4de404..7f87701176 100644 --- a/src/main/java/me/tomassetti/symbolsolver/model/declarations/ClassDeclaration.java +++ b/src/main/java/me/tomassetti/symbolsolver/model/declarations/ClassDeclaration.java @@ -1,5 +1,6 @@ package me.tomassetti.symbolsolver.model.declarations; +import me.tomassetti.symbolsolver.model.typesystem.ReferenceTypeUsage; import me.tomassetti.symbolsolver.resolution.TypeSolver; import java.util.ArrayList; @@ -15,16 +16,16 @@ default boolean isClass() { return true; } - ClassDeclaration getSuperClass(TypeSolver typeSolvers); + ReferenceTypeUsage getSuperClass(TypeSolver typeSolvers); List getInterfaces(TypeSolver typeSolver); - default List getAllSuperClasses(TypeSolver typeSolver) { + default List getAllSuperClasses(TypeSolver typeSolver) { // TODO it could specify type parameters: they should appear - List superclasses = new ArrayList<>(); - ClassDeclaration superClass = getSuperClass(typeSolver); + List superclasses = new ArrayList<>(); + ReferenceTypeUsage superClass = getSuperClass(typeSolver); if (superClass != null) { superclasses.add(superClass); - superclasses.addAll(superClass.getAllSuperClasses(typeSolver)); + superclasses.addAll(superClass.getAllAncestors()); } return superclasses; } @@ -32,7 +33,7 @@ default List getAllSuperClasses(TypeSolver typeSolver) { default List getAllInterfaces(TypeSolver typeSolver) { // TODO it could specify type parameters: they should appear List interfaces = new ArrayList<>(); - ClassDeclaration superClass = getSuperClass(typeSolver); + //ClassDeclaration superClass = getSuperClass(typeSolver); for (InterfaceDeclaration interfaceDeclaration : getInterfaces(typeSolver)){ interfaces.add(interfaceDeclaration); interfaces.addAll(interfaceDeclaration.getAllInterfacesExtended(typeSolver)); diff --git a/src/main/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsage.java b/src/main/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsage.java index a7f8f4a8c3..89d534a1a8 100644 --- a/src/main/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsage.java +++ b/src/main/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsage.java @@ -1,5 +1,6 @@ package me.tomassetti.symbolsolver.model.typesystem; +import com.github.javaparser.ast.type.WildcardType; import me.tomassetti.symbolsolver.resolution.*; import me.tomassetti.symbolsolver.model.declarations.MethodDeclaration; import me.tomassetti.symbolsolver.model.declarations.TypeDeclaration; @@ -8,6 +9,7 @@ import me.tomassetti.symbolsolver.resolution.reflection.ReflectionClassDeclaration; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -181,8 +183,11 @@ public TypeUsage replaceParam(String name, TypeUsage replaced) { } public List getAllAncestors() { + List ancestors = typeDeclaration.getAllAncestors(); + + ancestors = ancestors.stream().map((a)->replaceTypeParams(a).asReferenceTypeUsage()).collect(Collectors.toList()); // TODO replace type parameters - return typeDeclaration.getAllAncestors(); + return ancestors; } public TypeUsage replaceTypeParams(TypeUsage typeUsage){ @@ -268,11 +273,11 @@ public boolean isAssignableBy(TypeUsage other) { return this.getQualifiedName().equals(Predicate.class.getCanonicalName()) || this.getQualifiedName().equals(Function.class.getCanonicalName()); } else if (other instanceof ReferenceTypeUsage) { ReferenceTypeUsage otherRef = (ReferenceTypeUsage) other; - if (this.equals(otherRef)) { + if (compareConsideringTypeParameters(otherRef)) { return true; } for (ReferenceTypeUsage otherAncestor : otherRef.getAllAncestors()) { - if (otherAncestor.equals(this)) { + if (compareConsideringTypeParameters(otherAncestor)) { return true; } } @@ -285,6 +290,37 @@ public boolean isAssignableBy(TypeUsage other) { } } + private boolean compareConsideringTypeParameters(ReferenceTypeUsage other) { + if (other.equals(this)) { + return true; + } + if (this.getQualifiedName().equals(other.getQualifiedName())){ + if (this.parameters().size() != other.parameters().size()) { + throw new IllegalStateException(); + } + for (int i=0;i ref = solveType(wrappedNode.getExtends().get(0).getName(), typeSolver); if (!ref.isSolved()) { throw new UnsolvedSymbolException(wrappedNode.getExtends().get(0).getName()); } - return ref.getCorrespondingDeclaration().asClass(); + return new ReferenceTypeUsage(ref.getCorrespondingDeclaration().asClass(), typeSolver); } } @@ -199,7 +199,7 @@ public boolean canBeAssignedTo(TypeDeclaration other) { if (this.getQualifiedName().equals(other.getQualifiedName())) { return true; } - ClassDeclaration superclass = getSuperClass(typeSolver); + ClassDeclaration superclass = (ClassDeclaration) getSuperClass(typeSolver).getTypeDeclaration(); if (superclass != null) { if (superclass.canBeAssignedTo(other)) { return true; @@ -236,7 +236,7 @@ public FieldDeclaration getField(String name) { } } - ClassDeclaration superclass = this.getSuperClass(typeSolver); + ClassDeclaration superclass = (ClassDeclaration) this.getSuperClass(typeSolver).getTypeDeclaration(); if (superclass != null) { return superclass.getField(name); } else { @@ -265,7 +265,7 @@ public boolean hasField(String name) { } } - ClassDeclaration superclass = this.getSuperClass(typeSolver); + ClassDeclaration superclass = (ClassDeclaration) this.getSuperClass(typeSolver).getTypeDeclaration(); if (superclass != null) { return superclass.hasField(name); } else { @@ -329,9 +329,9 @@ public SymbolReference solveType(String name, TypeSolver typeSo @Override public List getAllAncestors() { List ancestors = new ArrayList<>(); - ClassDeclaration superclass = getSuperClass(typeSolver); + ReferenceTypeUsage superclass = getSuperClass(typeSolver); if (superclass != null) { - ancestors.add(new ReferenceTypeUsage(superclass, typeSolver)); + ancestors.add(superclass); ancestors.addAll(superclass.getAllAncestors()); } if (wrappedNode.getImplements() != null) { diff --git a/src/main/java/me/tomassetti/symbolsolver/resolution/javassist/JavassistClassDeclaration.java b/src/main/java/me/tomassetti/symbolsolver/resolution/javassist/JavassistClassDeclaration.java index 25420f09f2..ca4af586b9 100644 --- a/src/main/java/me/tomassetti/symbolsolver/resolution/javassist/JavassistClassDeclaration.java +++ b/src/main/java/me/tomassetti/symbolsolver/resolution/javassist/JavassistClassDeclaration.java @@ -186,7 +186,7 @@ public SymbolReference solveType(String substring, TypeSolver t public List getAllAncestors() { List ancestors = new LinkedList<>(); if (getSuperClass(typeSolver) != null) { - ancestors.add(new ReferenceTypeUsage(getSuperClass(typeSolver), typeSolver)); + ancestors.add(getSuperClass(typeSolver)); ancestors.addAll(getSuperClass(typeSolver).getAllAncestors()); } ancestors.addAll(getAllInterfaces(typeSolver).stream().map((i)->new ReferenceTypeUsage(i, typeSolver)).collect(Collectors.toList())); @@ -318,12 +318,12 @@ public boolean isClass() { } @Override - public ClassDeclaration getSuperClass(TypeSolver typeSolvers) { + public ReferenceTypeUsage getSuperClass(TypeSolver typeSolvers) { try { if (ctClass.getSuperclass() == null) { throw new UnsupportedOperationException(); } - return new JavassistClassDeclaration(ctClass.getSuperclass(), typeSolver).asClass(); + return new ReferenceTypeUsage(new JavassistClassDeclaration(ctClass.getSuperclass(), typeSolver).asClass(), typeSolver); } catch (NotFoundException e) { throw new RuntimeException(e); } diff --git a/src/main/java/me/tomassetti/symbolsolver/resolution/reflection/ReflectionClassDeclaration.java b/src/main/java/me/tomassetti/symbolsolver/resolution/reflection/ReflectionClassDeclaration.java index 59ed6b8db5..2c697771bb 100644 --- a/src/main/java/me/tomassetti/symbolsolver/resolution/reflection/ReflectionClassDeclaration.java +++ b/src/main/java/me/tomassetti/symbolsolver/resolution/reflection/ReflectionClassDeclaration.java @@ -9,10 +9,7 @@ import me.tomassetti.symbolsolver.resolution.javaparser.UnsolvedSymbolException; import me.tomassetti.symbolsolver.model.typesystem.*; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.lang.reflect.TypeVariable; +import java.lang.reflect.*; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; @@ -28,7 +25,8 @@ public class ReflectionClassDeclaration implements ClassDeclaration { public List getAllAncestors() { List ancestors = new LinkedList<>(); if (getSuperClass(typeSolver) != null) { - ancestors.add(new ReferenceTypeUsage(getSuperClass(typeSolver), typeSolver)); + ReferenceTypeUsage superClass = getSuperClass(typeSolver); + ancestors.add(superClass); ancestors.addAll(getSuperClass(typeSolver).getAllAncestors()); } ancestors.addAll(getAllInterfaces(typeSolver).stream().map((i)->new ReferenceTypeUsage(i, typeSolver)).collect(Collectors.toList())); @@ -133,8 +131,8 @@ public SymbolReference solveMethod(String name, List ref = superClass.solveMethod(name, parameterTypes, typeSolver); if (ref.isSolved()) { methods.add(ref.getCorrespondingDeclaration()); @@ -175,8 +173,8 @@ public Optional solveMethodAsUsage(String name, List par } methods.add(methodUsage); } - ClassDeclaration superClass = getSuperClass(typeSolver); - if (superClass != null) { + if (getSuperClass(typeSolver) != null) { + ClassDeclaration superClass = (ClassDeclaration)getSuperClass(typeSolver).getTypeDeclaration(); Optional ref = superClass.solveMethodAsUsage(name, parameterTypes, typeSolver, invokationContext, typeParameterValues); if (ref.isPresent()) { methods.add(ref.get()); @@ -306,11 +304,11 @@ public boolean hasField(String name) { return true; } } - ClassDeclaration superclass = getSuperClass(typeSolver); + ReferenceTypeUsage superclass = getSuperClass(typeSolver); if (superclass == null) { return false; } else { - return superclass.hasField(name); + return superclass.getTypeDeclaration().hasField(name); } } @@ -350,16 +348,25 @@ public boolean isClass() { } @Override - public ClassDeclaration getSuperClass(TypeSolver typeSolvers) { - if (clazz.getSuperclass() == null) { + public ReferenceTypeUsage getSuperClass(TypeSolver typeSolvers) { + if (clazz.getGenericSuperclass() == null) { return null; } - return new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver); + Type superType = clazz.getGenericSuperclass(); + if (superType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)superType; + List typeParameters = Arrays.stream(parameterizedType.getActualTypeArguments()) + .map((t) -> ReflectionFactory.typeUsageFor(t, typeSolver)) + .collect(Collectors.toList()); + return new ReferenceTypeUsage(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeParameters, typeSolvers); + } + return new ReferenceTypeUsage(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeSolvers); } @Override public List getInterfaces(TypeSolver typeSolver) { List interfaces = new ArrayList<>(); + // TODO use genericInterfaces for (Class i : clazz.getInterfaces()) { interfaces.add(new ReflectionInterfaceDeclaration(i, typeSolver)); } diff --git a/src/test/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsageTest.java b/src/test/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsageTest.java index 2eefda372f..a68f75d541 100644 --- a/src/test/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsageTest.java +++ b/src/test/java/me/tomassetti/symbolsolver/model/typesystem/ReferenceTypeUsageTest.java @@ -212,6 +212,7 @@ class MoreBazzing extends Bazzer { public void testGetAllAncestorsConsideringGenericsCases() { ReferenceTypeUsage foo = new ReferenceTypeUsage(new ReflectionClassDeclaration(Foo.class, typeSolver), typeSolver); ReferenceTypeUsage bar = new ReferenceTypeUsage(new ReflectionClassDeclaration(Bar.class, typeSolver), typeSolver); + ReferenceTypeUsage left, right; //YES MoreBazzing e1 = new MoreBazzing(); assertEquals(true, @@ -254,24 +255,22 @@ public void testGetAllAncestorsConsideringGenericsCases() { ); //YES MoreBazzing e5 = new MoreBazzing(); - assertEquals(true, - new ReferenceTypeUsage( - new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), - ImmutableList.of(WildcardUsage.extendsBound(foo), WildcardUsage.extendsBound(foo)), typeSolver) - .isAssignableBy(new ReferenceTypeUsage( - new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), - ImmutableList.of(bar, bar), typeSolver)) - ); + left = new ReferenceTypeUsage( + new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), + ImmutableList.of(WildcardUsage.extendsBound(foo), WildcardUsage.extendsBound(foo)), typeSolver); + right = new ReferenceTypeUsage( + new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), + ImmutableList.of(bar, bar), typeSolver); + assertEquals(true, left.isAssignableBy(right)); //YES Bazzer e6 = new MoreBazzing(); - assertEquals(true, - new ReferenceTypeUsage( - new ReflectionClassDeclaration(Bazzer.class, typeSolver), - ImmutableList.of(object, string, string), typeSolver) - .isAssignableBy(new ReferenceTypeUsage( - new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), - ImmutableList.of(string, object), typeSolver)) - ); + left = new ReferenceTypeUsage( + new ReflectionClassDeclaration(Bazzer.class, typeSolver), + ImmutableList.of(object, string, string), typeSolver); + right = new ReferenceTypeUsage( + new ReflectionClassDeclaration(MoreBazzing.class, typeSolver), + ImmutableList.of(string, object), typeSolver); + assertEquals(true, left.isAssignableBy(right)); //YES Bazzer e7 = new MoreBazzing(); assertEquals(true, diff --git a/src/test/java/me/tomassetti/symbolsolver/resolution/GenericsTest.java b/src/test/java/me/tomassetti/symbolsolver/resolution/GenericsTest.java index 8e42d69738..5c08c7fdb4 100644 --- a/src/test/java/me/tomassetti/symbolsolver/resolution/GenericsTest.java +++ b/src/test/java/me/tomassetti/symbolsolver/resolution/GenericsTest.java @@ -271,7 +271,8 @@ public void typeParamOnReturnTypeStep3() throws ParseException { MethodDeclaration method = Navigator.demandMethod(clazz, "nodeEquals"); MethodCallExpr call = Navigator.findMethodCall(method, "accept"); - TypeUsage typeUsage = JavaParserFacade.get(new JreTypeSolver()).getType(call); + JavaParserFacade javaParserFacade = JavaParserFacade.get(new JreTypeSolver()); + TypeUsage typeUsage = javaParserFacade.getType(call); assertEquals(false, typeUsage.isTypeVariable()); assertEquals("java.lang.Boolean", typeUsage.describe()); diff --git a/src/test/java/me/tomassetti/symbolsolver/resolution/JavaParserFacadeTest.java b/src/test/java/me/tomassetti/symbolsolver/resolution/JavaParserFacadeTest.java index d7aad7c043..b852b0e28f 100644 --- a/src/test/java/me/tomassetti/symbolsolver/resolution/JavaParserFacadeTest.java +++ b/src/test/java/me/tomassetti/symbolsolver/resolution/JavaParserFacadeTest.java @@ -3,6 +3,7 @@ import com.github.javaparser.ParseException; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import me.tomassetti.symbolsolver.model.typesystem.ReferenceTypeUsage; import me.tomassetti.symbolsolver.resolution.javaparser.JavaParserFacade; import me.tomassetti.symbolsolver.javaparser.Navigator; import me.tomassetti.symbolsolver.model.declarations.TypeDeclaration; @@ -21,7 +22,7 @@ public void typeDeclarationSuperClassImplicitlyIncludeObject() throws ParseExcep CompilationUnit cu = parseSample("Generics"); ClassOrInterfaceDeclaration clazz = Navigator.demandClass(cu, "Generics"); TypeDeclaration typeDeclaration = JavaParserFacade.get(new JreTypeSolver()).getTypeDeclaration(clazz); - TypeDeclaration superclass = typeDeclaration.asClass().getSuperClass(new JreTypeSolver()); + ReferenceTypeUsage superclass = typeDeclaration.asClass().getSuperClass(new JreTypeSolver()); assertEquals(Object.class.getCanonicalName(), superclass.getQualifiedName()); } }