Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fixes #3099] Added ability to solve type with a list of expected type arguments #3213

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -25,6 +25,7 @@
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.PatternExpr;
import com.github.javaparser.quality.Nullable;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.declarations.*;
import com.github.javaparser.resolution.types.ResolvedType;
Expand Down Expand Up @@ -82,20 +83,71 @@ default Optional<ResolvedType> solveGenericTypeInParentContext(String name) {
*
* @param name For example, solving {@code List} or {@code java.util.List}.
* @return The declaration associated with the given type name.
*
* @deprecated Consider using method {@link #solveType(String, List)} that also consider the type arguments.
* If you want to keep to use the new function, but keep the same behavior consider passing type
* arguments as {@code null}.
*/
@Deprecated
default SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return solveType(name, null);
}

/**
* Method used to solve a name with an expected list of type arguments.
* <br>
* This method differs from {@link Context#solveType(String)} by taking the type arguments in consideration.
* For example, lets imagine that we have a project containing the following classes:
* <ul>
* <li>com/example/Alpha.java</li>
* <li>com/example/Beta.java</li>
* </ul>
* Where Alpha creates a inner interface called CustomInterface and Beta implements Alpha.CustomInterface and
* also declares a inner interface called CustomInterface with type arguments. Using this method we can
* specify which type arguments we are expecting and will be resolved with the type matching that declaration.
*
* @param name The name to be solved.
* @param typeArguments The list of expected type arguments.
*
* @return The declaration associated with the given type name.
*/
default SymbolReference<ResolvedTypeDeclaration> solveType(String name, @Nullable List<ResolvedType> typeArguments) {
// Default to solving within the parent context.
return solveTypeInParentContext(name);
return solveTypeInParentContext(name, typeArguments);
}

/**
* Solve a name in the parent context.
*
* @param name The name to be solved.
*
* @return The declaration associated with the given type name.
*
* @deprecated Consider using method {@link #solveTypeInParentContext(String, List)} that also consider the type arguments.
* If you want to keep to use the new function, but keep the same behavior consider passing type
* arguments as {@code null}.
*/
@Deprecated
default SymbolReference<ResolvedTypeDeclaration> solveTypeInParentContext(String name) {
return solveTypeInParentContext(name, null);
}

/**
* Solve a name with type arguments in the parent context.
*
* @param name The name to be solved.
* @param typeArguments The list of expected type arguments.
*
* @return The declaration associated with the given type name.
*/
default SymbolReference<ResolvedTypeDeclaration> solveTypeInParentContext(String name, @Nullable List<ResolvedType> typeArguments) {
Optional<Context> optionalParentContext = getParent();
if (!optionalParentContext.isPresent()) {
return SymbolReference.unsolved();
}

// Delegate solving to the parent context.
return optionalParentContext.get().solveType(name);
return optionalParentContext.get().solveType(name, typeArguments);
}

/* Symbol resolution */
Expand Down
Expand Up @@ -97,7 +97,7 @@ public final Optional<Value> solveSymbolAsValue(String name) {
}

@Override
public final SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
public final SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
// TODO: Is null check required?
if (wrappedNode.getTypeParameters() != null) {
for (TypeParameter tp : wrappedNode.getTypeParameters()) {
Expand All @@ -119,7 +119,7 @@ public final SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
}
}

return solveTypeInParentContext(name);
return solveTypeInParentContext(name, typeArguments);
}

@Override
Expand Down
Expand Up @@ -59,8 +59,8 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return javaParserTypeDeclarationAdapter.solveType(name);
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> resolvedTypes) {
return javaParserTypeDeclarationAdapter.solveType(name, resolvedTypes);
}

@Override
Expand Down
Expand Up @@ -21,10 +21,6 @@

package com.github.javaparser.symbolsolver.javaparsermodel.contexts;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
Expand All @@ -46,6 +42,10 @@
import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
import com.google.common.base.Preconditions;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
* A symbol resolution context for an object creation node.
*/
Expand Down Expand Up @@ -122,7 +122,7 @@ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name,
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
List<TypeDeclaration> typeDeclarations = myDeclaration.findMembersOfKind(TypeDeclaration.class);

Optional<SymbolReference<ResolvedTypeDeclaration>> exactMatch =
Expand Down Expand Up @@ -193,7 +193,7 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
}
}

return solveTypeInParentContext(name);
return solveTypeInParentContext(name, typeArguments);
}

@Override
Expand Down
Expand Up @@ -21,10 +21,6 @@

package com.github.javaparser.symbolsolver.javaparsermodel.contexts;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.type.TypeParameter;
Expand All @@ -42,6 +38,10 @@
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.model.resolution.Value;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

/**
* @author Federico Tomassetti
*/
Expand Down Expand Up @@ -102,8 +102,8 @@ public Optional<ResolvedType> solveGenericType(String name) {
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return javaParserTypeDeclarationAdapter.solveType(name);
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
return javaParserTypeDeclarationAdapter.solveType(name, typeArguments);
}

@Override
Expand Down
Expand Up @@ -24,10 +24,13 @@
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;

import java.util.List;

/**
* Limited version of ClassOrInterfaceDeclarationContext that only resolves type parameters for use by
* extends and implements part of declaration.
Expand All @@ -38,13 +41,13 @@ public ClassOrInterfaceDeclarationExtendsContext(ClassOrInterfaceDeclaration wra
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
for (TypeParameter typeParameter : wrappedNode.getTypeParameters()) {
if (typeParameter.getName().getId().equals(name)) {
return SymbolReference.solved(new JavaParserTypeParameter(typeParameter, typeSolver));
}
}

return super.solveType(name);
return super.solveType(name, typeArguments);
}
}
Expand Up @@ -120,7 +120,7 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {

if (wrappedNode.getTypes() != null) {
// Look for types in this compilation unit. For instance, if the given name is "A", there may be a class or
Expand Down
Expand Up @@ -68,8 +68,8 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return javaParserTypeDeclarationAdapter.solveType(name);
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> resolvedTypes) {
return javaParserTypeDeclarationAdapter.solveType(name, resolvedTypes);
}

@Override
Expand Down
Expand Up @@ -69,8 +69,8 @@ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String na
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return JavaParserFactory.getContext(demandParentNode(wrappedNode), typeSolver).solveType(name);
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
return JavaParserFactory.getContext(demandParentNode(wrappedNode), typeSolver).solveType(name, typeArguments);
}

@Override
Expand Down
Expand Up @@ -22,12 +22,15 @@
package com.github.javaparser.symbolsolver.javaparsermodel.contexts;

import com.github.javaparser.ast.AccessSpecifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.nodeTypes.NodeWithExtends;
import com.github.javaparser.ast.nodeTypes.NodeWithImplements;
import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments;
import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.TypeParameter;
import com.github.javaparser.resolution.declarations.*;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
Expand Down Expand Up @@ -65,53 +68,63 @@ public JavaParserTypeDeclarationAdapter(com.github.javaparser.ast.body.TypeDecla
this.context = context;
}

/**
* @deprecated Consider using {@link #solveType(String, List)} to consider type arguments.
*/
@Deprecated
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
return solveType(name, null);
}

public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
if (this.wrappedNode.getName().getId().equals(name)) {
return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(wrappedNode));
}

// Internal classes
for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
if (member instanceof TypeDeclaration) {
TypeDeclaration<?> internalType = (TypeDeclaration<?>) member;
if (internalType.getName().getId().equals(name)) {
if (member.isTypeDeclaration()) {
TypeDeclaration<?> internalType = member.asTypeDeclaration();
if (internalType.getName().getId().equals(name) && compareTypeParameters(internalType, typeArguments)) {
return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(internalType));
} else if (name.startsWith(wrappedNode.getName().getId() + "." + internalType.getName().getId())) {
return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(wrappedNode.getName().getId().length() + 1));
return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(wrappedNode.getName().getId().length() + 1), typeArguments);
} else if (name.startsWith(internalType.getName().getId() + ".")) {
return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(internalType.getName().getId().length() + 1));
return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(internalType.getName().getId().length() + 1), typeArguments);
}
}
}

// Check if is a type parameter
if (wrappedNode instanceof NodeWithTypeParameters) {
NodeWithTypeParameters<?> nodeWithTypeParameters = (NodeWithTypeParameters<?>) wrappedNode;
for (TypeParameter astTpRaw : nodeWithTypeParameters.getTypeParameters()) {
TypeParameter astTp = astTpRaw;
if (astTp.getName().getId().equals(name)) {
return SymbolReference.solved(new JavaParserTypeParameter(astTp, typeSolver));
if (astTpRaw.getName().getId().equals(name)) {
return SymbolReference.solved(new JavaParserTypeParameter(astTpRaw, typeSolver));
}
}
}

// Check if the node implements other types
if (wrappedNode instanceof NodeWithImplements) {
NodeWithImplements<?> nodeWithImplements = (NodeWithImplements<?>) wrappedNode;
for (ClassOrInterfaceType implementedType : nodeWithImplements.getImplementedTypes()) {
if (implementedType.getName().getId().equals(name)) {
return context.getParent()
.orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
.solveType(implementedType.getNameWithScope());
.solveType(implementedType.getNameWithScope(), typeArguments);
}
}
}

// Check if the node extends other types
if (wrappedNode instanceof NodeWithExtends) {
NodeWithExtends<?> nodeWithExtends = (NodeWithExtends<?>) wrappedNode;
for (ClassOrInterfaceType extendedType : nodeWithExtends.getExtendedTypes()) {
if (extendedType.getName().getId().equals(name)) {
if (extendedType.getName().getId().equals(name) && compareTypeArguments(extendedType, typeArguments)) {
return context.getParent()
.orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
.solveType(extendedType.getNameWithScope());
.orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
.solveType(extendedType.getNameWithScope(), typeArguments);
}
}
}
Expand All @@ -125,7 +138,34 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
// Else check parents
return context.getParent()
.orElseThrow(() -> new RuntimeException("Parent context unexpectedly empty."))
.solveType(name);
.solveType(name, typeArguments);
}

private <T extends NodeWithTypeArguments<?>> boolean compareTypes(List<? extends Type> types,
List<ResolvedType> resolvedTypeArguments) {
// If the user want's to solve the type without having prior knowledge of the type arguments.
if (resolvedTypeArguments == null) {
return true;
}

return types.size() == resolvedTypeArguments.size();
}

private <T extends NodeWithTypeArguments<?>> boolean compareTypeArguments(T type, List<ResolvedType> resolvedTypeArguments) {
return compareTypes(type.getTypeArguments().orElse(new NodeList<>()), resolvedTypeArguments);
}

private <T extends NodeWithTypeParameters<?>> boolean compareTypeParameters(T type,
List<ResolvedType> resolvedTypeArguments) {
return compareTypes(type.getTypeParameters(), resolvedTypeArguments);
}

private boolean compareTypeParameters(TypeDeclaration<?> typeDeclaration, List<ResolvedType> resolvedTypeArguments) {
if (typeDeclaration instanceof NodeWithTypeParameters) {
return compareTypeParameters((NodeWithTypeParameters<?>) typeDeclaration, resolvedTypeArguments);
} else {
return true;
}
}

/**
Expand Down
Expand Up @@ -48,7 +48,7 @@ public ObjectCreationContext(ObjectCreationExpr wrappedNode, TypeSolver typeSolv
}

@Override
public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
public SymbolReference<ResolvedTypeDeclaration> solveType(String name, List<ResolvedType> typeArguments) {
if (wrappedNode.hasScope()) {
Expression scope = wrappedNode.getScope().get();
ResolvedType scopeType = JavaParserFacade.get(typeSolver).getType(scope);
Expand All @@ -67,7 +67,7 @@ public SymbolReference<ResolvedTypeDeclaration> solveType(String name) {
while (parentNode instanceof ObjectCreationExpr) {
parentNode = demandParentNode(parentNode);
}
return JavaParserFactory.getContext(parentNode, typeSolver).solveType(name);
return JavaParserFactory.getContext(parentNode, typeSolver).solveType(name, typeArguments);
}

@Override
Expand Down