Skip to content

Commit

Permalink
Made the symbol solver more robust by enabling the possibility to ign…
Browse files Browse the repository at this point in the history
…ore ancestors that cannot be resolved without giving up completely.
  • Loading branch information
malteskoruppa committed Sep 10, 2018
1 parent 92e8ecb commit 26bc041
Show file tree
Hide file tree
Showing 20 changed files with 147 additions and 60 deletions.
Expand Up @@ -38,7 +38,7 @@
* @author Federico Tomassetti
*/
public interface ResolvedReferenceTypeDeclaration extends ResolvedTypeDeclaration,
ResolvedTypeParametrizable {
ResolvedTypeParametrizable {

@Override
default ResolvedReferenceTypeDeclaration asReferenceType() {
Expand All @@ -50,14 +50,41 @@ default ResolvedReferenceTypeDeclaration asReferenceType() {
///

/**
* The list of all the direct ancestors of the current declaration.
* Note that the ancestor can be parametrized types with values specified. For example:
* Resolves the types of all direct ancestors (i.e., the directly extended class and the directly implemented
* interfaces) and returns the list of ancestors as a list of resolved reference types.
* <p>
* In case any ancestor cannot be resolved, an {@code UnsolvedSymbolException} is thrown. In order to obtain a list
* of only the resolvable direct ancestors, use {@link #getAncestors(boolean)} and pass the value {@code true}.
* <p>
* Note that an ancestor can be parametrized types with values specified. For example:
* <p>
* class A implements Comparable&lt;String&gt; {}
* <p>
* In this case the ancestor is Comparable&lt;String&gt;
*
* @return The list of resolved ancestors.
* @throws UnsolvedSymbolException if some ancestor could not be resolved.
*/
default List<ResolvedReferenceType> getAncestors() {
return getAncestors(false);
}

/**
* Resolves the types of all direct ancestors (i.e., the directly extended class and the directly implemented
* interfaces) and returns the list of ancestors as a list of resolved reference types.
* <p>
* If {@code acceptIncompleteList} is {@code false}, then an {@code UnsolvedSymbolException} is thrown if any
* ancestor cannot be resolved. Otherwise, a list of only the resolvable direct ancestors is returned.
*
* @param acceptIncompleteList When set to {@code false}, this method throws an {@link UnsolvedSymbolException} if
* one or more ancestor could not be resolved. When set to {@code true}, this method
* does not throw an {@link UnsolvedSymbolException}, but the list of returned ancestors
* may be incomplete in case one or more ancestor could not be resolved.
* @return The list of resolved ancestors.
* @throws UnsolvedSymbolException if some ancestor could not be resolved and {@code acceptIncompleteList} is set to
* {@code false}.
*/
List<ResolvedReferenceType> getAncestors();
List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList);

/**
* The list of all the ancestors of the current declaration, direct and indirect.
Expand All @@ -66,9 +93,9 @@ default ResolvedReferenceTypeDeclaration asReferenceType() {
default List<ResolvedReferenceType> getAllAncestors() {
List<ResolvedReferenceType> ancestors = new ArrayList<>();
// We want to avoid infinite recursion in case of Object having Object as ancestor
if (!(Object.class.getCanonicalName().equals(getQualifiedName()))) {
if (!(Object.class.getCanonicalName().equals(getQualifiedName()))) {
for (ResolvedReferenceType ancestor : getAncestors()) {
ancestors.add(ancestor);
ancestors.add(ancestor);
for (ResolvedReferenceType inheritedAncestor : ancestor.getAllAncestors()) {
if (!ancestors.contains(inheritedAncestor)) {
ancestors.add(inheritedAncestor);
Expand Down Expand Up @@ -139,8 +166,8 @@ default boolean hasVisibleField(String name) {
*/
default List<ResolvedFieldDeclaration> getVisibleFields() {
return getAllFields().stream()
.filter(f -> f.declaringType().equals(this) || f.accessSpecifier() != AccessSpecifier.PRIVATE)
.collect(Collectors.toList());
.filter(f -> f.declaringType().equals(this) || f.accessSpecifier() != AccessSpecifier.PRIVATE)
.collect(Collectors.toList());
}

/**
Expand All @@ -162,7 +189,7 @@ default List<ResolvedFieldDeclaration> getAllStaticFields() {
*/
default List<ResolvedFieldDeclaration> getDeclaredFields() {
return getAllFields().stream().filter(it -> it.declaringType().getQualifiedName()
.equals(getQualifiedName())).collect(Collectors.toList());
.equals(getQualifiedName())).collect(Collectors.toList());
}

///
Expand Down
Expand Up @@ -79,7 +79,7 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolve
* @return A ResolvedTypeDeclaration matching the {@param name}, null otherwise
*/
private ResolvedTypeDeclaration checkAncestorsForType(String name, ResolvedReferenceTypeDeclaration declaration) {
for (ResolvedReferenceType ancestor : declaration.getAncestors()) {
for (ResolvedReferenceType ancestor : declaration.getAncestors(true)) {
try {
for (ResolvedTypeDeclaration internalTypeDeclaration : ancestor.getTypeDeclaration().internalTypes()) {
boolean visible = true;
Expand Down Expand Up @@ -112,7 +112,7 @@ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<
.collect(Collectors.toList());
// We want to avoid infinite recursion in case of Object having Object as ancestor
if (!Object.class.getCanonicalName().equals(typeDeclaration.getQualifiedName())) {
for (ResolvedReferenceType ancestor : typeDeclaration.getAncestors()) {
for (ResolvedReferenceType ancestor : typeDeclaration.getAncestors(true)) {
// Avoid recursion on self
if (typeDeclaration != ancestor.getTypeDeclaration()) {
candidateMethods.addAll(ancestor.getAllMethodsVisibleToInheritors()
Expand Down
Expand Up @@ -28,7 +28,7 @@ public JavaParserAnnotationDeclaration(AnnotationDeclaration wrappedNode, TypeSo
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down
Expand Up @@ -95,12 +95,12 @@ public AccessSpecifier accessSpecifier() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
return
ImmutableList.
<ResolvedReferenceType>builder()
.add(getSuperClass())
.addAll(superTypeDeclaration.asReferenceType().getAncestors())
.addAll(superTypeDeclaration.asReferenceType().getAncestors(acceptIncompleteList))
.build();
}

Expand Down
Expand Up @@ -269,30 +269,7 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolve
return getContext().getParent().solveType(name, typeSolver);
}

/**
* Resolves the type of all ancestors (i.e., the extended class and the implemented interfaces) and returns the list
* of ancestors as a list of resolved reference types.
*
* @return The list of resolved ancestors.
* @throws UnsolvedSymbolException if some ancestor could not be resolved.
*/
@Override
public List<ResolvedReferenceType> getAncestors() {
return getAncestors(false);
}

/**
* Resolves the type of all ancestors (i.e., the extended class and the implemented interfaces) and returns the list
* of ancestors as a list of resolved reference types.
*
* @param acceptIncompleteList When set to {@code false}, this method throws an {@link UnsolvedSymbolException} if
* one or more ancestor could not be resolved. When set to {@code true}, this method
* does not throw an {@link UnsolvedSymbolException}, but the list of returned ancestors
* may be incomplete in case one or more ancestor could not be resolved.
* @return The list of resolved ancestors.
* @throws UnsolvedSymbolException if some ancestor could not be resolved and {@code acceptIncompleteList} is set to
* {@code false}.
*/
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
List<ResolvedReferenceType> ancestors = new ArrayList<>();

Expand Down
Expand Up @@ -209,7 +209,7 @@ public List<ResolvedFieldDeclaration> getAllFields() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
List<ResolvedReferenceType> ancestors = new ArrayList<>();
ResolvedReferenceType enumClass = ReflectionFactory.typeUsageFor(Enum.class, typeSolver).asReferenceType();
ResolvedTypeParameterDeclaration eTypeParameter = enumClass.getTypeDeclaration().getTypeParameters().get(0);
Expand All @@ -218,7 +218,7 @@ public List<ResolvedReferenceType> getAncestors() {
if (wrappedNode.getImplementedTypes() != null) {
for (ClassOrInterfaceType implementedType : wrappedNode.getImplementedTypes()) {
SymbolReference<ResolvedTypeDeclaration> implementedDeclRef = new SymbolSolver(typeSolver).solveTypeInType(this, implementedType.getName().getId());
if (!implementedDeclRef.isSolved()) {
if (!implementedDeclRef.isSolved() && !acceptIncompleteList) {
throw new UnsolvedSymbolException(implementedType.getName().getId());
}
ancestors.add(new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) implementedDeclRef.getCorrespondingDeclaration(), typeSolver));
Expand Down
Expand Up @@ -242,16 +242,30 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolve
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
List<ResolvedReferenceType> ancestors = new ArrayList<>();
if (wrappedNode.getExtendedTypes() != null) {
for (ClassOrInterfaceType extended : wrappedNode.getExtendedTypes()) {
ancestors.add(toReferenceType(extended));
try {
ancestors.add(toReferenceType(extended));
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
}
}
if (wrappedNode.getImplementedTypes() != null) {
for (ClassOrInterfaceType implemented : wrappedNode.getImplementedTypes()) {
ancestors.add(toReferenceType(implemented));
try {
ancestors.add(toReferenceType(implemented));
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
}
}
return ancestors;
Expand Down
Expand Up @@ -179,7 +179,7 @@ public List<ResolvedFieldDeclaration> getAllFields() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down
Expand Up @@ -115,7 +115,7 @@ public List<ResolvedFieldDeclaration> getAllFields() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down
Expand Up @@ -94,7 +94,7 @@ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down
Expand Up @@ -168,12 +168,27 @@ private String getSuperclassFQN() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
List<ResolvedReferenceType> ancestors = new ArrayList<>();
if (getSuperClass() != null) {
ancestors.add(getSuperClass());
try {
ResolvedReferenceType superClass = getSuperClass();
if (superClass != null) {
ancestors.add(superClass);
}
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
try {
ancestors.addAll(getInterfaces());
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
ancestors.addAll(getInterfaces());
return ancestors;
}

Expand Down
Expand Up @@ -88,18 +88,32 @@ public String getQualifiedName() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
// Direct ancestors of an enum are java.lang.Enum and interfaces
List<ResolvedReferenceType> ancestors = new ArrayList<>();

String superClassName = ctClass.getClassFile().getSuperclass();

if (superClassName != null) {
ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(superClassName), typeSolver));
try {
ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(superClassName), typeSolver));
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
}

for (String interfazeName : ctClass.getClassFile().getInterfaces()) {
ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(interfazeName), typeSolver));
try {
ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(interfazeName), typeSolver));
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
}

return ancestors;
Expand Down
Expand Up @@ -153,12 +153,19 @@ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
List<ResolvedReferenceType> ancestors = new ArrayList<>();
try {
for (CtClass interfaze : ctClass.getInterfaces()) {
ResolvedReferenceType superInterfaze = JavassistFactory.typeUsageFor(interfaze, typeSolver).asReferenceType();
ancestors.add(superInterfaze);
try {
ResolvedReferenceType superInterfaze = JavassistFactory.typeUsageFor(interfaze, typeSolver).asReferenceType();
ancestors.add(superInterfaze);
} catch (UnsolvedSymbolException e) {
if (!acceptIncompleteList) {
// we only throw an exception if we require a complete list; otherwise, we attempt to continue gracefully
throw e;
}
}
}
} catch (NotFoundException e) {
throw new RuntimeException(e);
Expand Down
Expand Up @@ -129,7 +129,7 @@ public List<ResolvedFieldDeclaration> getAllFields() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down
Expand Up @@ -87,7 +87,9 @@ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
// we do not attempt to perform any symbol solving when analyzing ancestors in the reflection model, so we can
// simply ignore the boolean parameter here; an UnsolvedSymbolException cannot occur
return reflectionClassAdapter.getAncestors();
}

Expand Down
Expand Up @@ -106,7 +106,9 @@ public String getQualifiedName() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
// we do not attempt to perform any symbol solving when analyzing ancestors in the reflection model, so we can
// simply ignore the boolean parameter here; an UnsolvedSymbolException cannot occur
return reflectionClassAdapter.getAncestors();
}

Expand Down
Expand Up @@ -240,7 +240,9 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
// we do not attempt to perform any symbol solving when analyzing ancestors in the reflection model, so we can
// simply ignore the boolean parameter here; an UnsolvedSymbolException cannot occur
return reflectionClassAdapter.getAncestors();
}

Expand Down
Expand Up @@ -54,7 +54,7 @@ public String getName() {
}

@Override
public List<ResolvedReferenceType> getAncestors() {
public List<ResolvedReferenceType> getAncestors(boolean acceptIncompleteList) {
throw new UnsupportedOperationException();
}

Expand Down

0 comments on commit 26bc041

Please sign in to comment.