blob: 03ab0c0379f391ffaa1077a2b7a0f973eee01360 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.manipulation.dom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NameQualifiedType;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.PrimitiveType.Code;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchExpression;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.core.manipulation.TypeKinds;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.jdt.internal.corext.dom.TypeBindingVisitor;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.ui.util.ASTHelper;
/**
* Helper methods to find AST nodes or bindings.
*/
// @see JDTUIHelperClasses
// @see org.eclipse.jdt.internal.ui.text.correction.ASTResolving (subclass of this one)
public class ASTResolving {
@SuppressWarnings("nls")
public static ITypeBinding guessBindingForReference(ASTNode node) {
String i= "";
if ((((i))).equals(((("a"))))) {
return Bindings.normalizeTypeBinding(null);
}
return Bindings.normalizeTypeBinding(getPossibleReferenceBinding(node));
}
private static ITypeBinding getPossibleReferenceBinding(ASTNode node) {
ASTNode parent= node.getParent();
switch (parent.getNodeType()) {
case ASTNode.ASSIGNMENT:
Assignment assignment= (Assignment) parent;
if (node.equals(assignment.getLeftHandSide())) {
// field write access: xx= expression
return assignment.getRightHandSide().resolveTypeBinding();
}
// read access
return assignment.getLeftHandSide().resolveTypeBinding();
case ASTNode.INFIX_EXPRESSION:
InfixExpression infix= (InfixExpression) parent;
InfixExpression.Operator op= infix.getOperator();
if (op == InfixExpression.Operator.CONDITIONAL_AND || op == InfixExpression.Operator.CONDITIONAL_OR) {
// boolean operation
return infix.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$
} else if (op == InfixExpression.Operator.LEFT_SHIFT || op == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED || op == InfixExpression.Operator.RIGHT_SHIFT_SIGNED) {
// asymmetric operation
return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
}
if (node.equals(infix.getLeftOperand())) {
// xx operation expression
ITypeBinding rigthHandBinding= infix.getRightOperand().resolveTypeBinding();
if (rigthHandBinding != null) {
return rigthHandBinding;
}
} else {
// expression operation xx
ITypeBinding leftHandBinding= infix.getLeftOperand().resolveTypeBinding();
if (leftHandBinding != null) {
return leftHandBinding;
}
}
if (op != InfixExpression.Operator.EQUALS && op != InfixExpression.Operator.NOT_EQUALS) {
return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
}
break;
case ASTNode.INSTANCEOF_EXPRESSION:
InstanceofExpression instanceofExpression= (InstanceofExpression) parent;
return instanceofExpression.getRightOperand().resolveBinding();
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
if (frag.getInitializer().equals(node)) {
return frag.getName().resolveTypeBinding();
}
break;
case ASTNode.SUPER_METHOD_INVOCATION:
SuperMethodInvocation superMethodInvocation= (SuperMethodInvocation) parent;
IMethodBinding superMethodBinding= ASTNodes.getMethodBinding(superMethodInvocation.getName());
if (superMethodBinding != null) {
return getParameterTypeBinding(node, superMethodInvocation.arguments(), superMethodBinding);
}
break;
case ASTNode.METHOD_INVOCATION:
MethodInvocation methodInvocation= (MethodInvocation) parent;
IMethodBinding methodBinding= methodInvocation.resolveMethodBinding();
if (methodBinding != null) {
return getParameterTypeBinding(node, methodInvocation.arguments(), methodBinding);
}
break;
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION: {
SuperConstructorInvocation superInvocation= (SuperConstructorInvocation) parent;
IMethodBinding superBinding= superInvocation.resolveConstructorBinding();
if (superBinding != null) {
return getParameterTypeBinding(node, superInvocation.arguments(), superBinding);
}
break;
}
case ASTNode.CONSTRUCTOR_INVOCATION: {
ConstructorInvocation constrInvocation= (ConstructorInvocation) parent;
IMethodBinding constrBinding= constrInvocation.resolveConstructorBinding();
if (constrBinding != null) {
return getParameterTypeBinding(node, constrInvocation.arguments(), constrBinding);
}
break;
}
case ASTNode.CLASS_INSTANCE_CREATION: {
ClassInstanceCreation creation= (ClassInstanceCreation) parent;
IMethodBinding creationBinding= creation.resolveConstructorBinding();
if (creationBinding != null) {
return getParameterTypeBinding(node, creation.arguments(), creationBinding);
}
break;
}
case ASTNode.PARENTHESIZED_EXPRESSION:
return guessBindingForReference(parent);
case ASTNode.ARRAY_ACCESS:
if (((ArrayAccess) parent).getIndex().equals(node)) {
return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
} else {
ITypeBinding parentBinding= getPossibleReferenceBinding(parent);
if (parentBinding == null) {
parentBinding= parent.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
return parentBinding.createArrayType(1);
}
case ASTNode.ARRAY_CREATION:
if (((ArrayCreation) parent).dimensions().contains(node)) {
return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
}
break;
case ASTNode.ARRAY_INITIALIZER:
ASTNode initializerParent= parent.getParent();
int dim= 1;
while (initializerParent instanceof ArrayInitializer) {
initializerParent= initializerParent.getParent();
dim++;
}
Type creationType= null;
if (initializerParent instanceof ArrayCreation) {
creationType= ((ArrayCreation) initializerParent).getType();
} else if (initializerParent instanceof VariableDeclaration) {
VariableDeclaration varDecl= (VariableDeclaration) initializerParent;
creationType= ASTNodes.getType(varDecl);
dim-= varDecl.getExtraDimensions();
} else if (initializerParent instanceof MemberValuePair) {
String name= ((MemberValuePair) initializerParent).getName().getIdentifier();
IMethodBinding annotMember= findAnnotationMember((Annotation) initializerParent.getParent(), name);
if (annotMember != null) {
return getReducedDimensionBinding(annotMember.getReturnType(), dim);
}
}
if (creationType instanceof ArrayType) {
ITypeBinding creationTypeBinding= ((ArrayType) creationType).resolveBinding();
if (creationTypeBinding != null) {
return Bindings.getComponentType(creationTypeBinding, dim);
}
}
break;
case ASTNode.CONDITIONAL_EXPRESSION:
ReturnStatement parentReturnStatement= (ReturnStatement) ASTNodes.getParent(node, ASTNode.RETURN_STATEMENT);
if (parentReturnStatement != null) {
return getPossibleReferenceBinding(parentReturnStatement.getExpression());
}
VariableDeclarationStatement variableDeclarationStatement= (VariableDeclarationStatement) ASTNodes.getParent(node, ASTNode.VARIABLE_DECLARATION_STATEMENT);
if (variableDeclarationStatement != null) {
return variableDeclarationStatement.getType().resolveBinding();
}
ConditionalExpression expression= (ConditionalExpression) parent;
if (node.equals(expression.getExpression())) {
return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$
}
if (node.equals(expression.getElseExpression())) {
return expression.getThenExpression().resolveTypeBinding();
}
return expression.getElseExpression().resolveTypeBinding();
case ASTNode.POSTFIX_EXPRESSION:
return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
case ASTNode.PREFIX_EXPRESSION:
if (((PrefixExpression) parent).getOperator() == PrefixExpression.Operator.NOT) {
return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$
}
return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
case ASTNode.IF_STATEMENT:
case ASTNode.WHILE_STATEMENT:
case ASTNode.DO_STATEMENT:
if (node instanceof Expression) {
return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$
}
break;
case ASTNode.SWITCH_STATEMENT:
if (((SwitchStatement) parent).getExpression().equals(node)) {
return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$
}
break;
case ASTNode.RETURN_STATEMENT:
MethodDeclaration decl= findParentMethodDeclaration(parent);
if (decl != null && !decl.isConstructor()) {
return decl.getReturnType2().resolveBinding();
}
LambdaExpression lambdaExpr= findEnclosingLambdaExpression(parent);
if (lambdaExpr != null) {
IMethodBinding lambdaMethodBinding= lambdaExpr.resolveMethodBinding();
if (lambdaMethodBinding != null && lambdaMethodBinding.getReturnType() != null) {
return lambdaMethodBinding.getReturnType();
}
}
break;
case ASTNode.CAST_EXPRESSION:
return ((CastExpression) parent).getType().resolveBinding();
case ASTNode.THROW_STATEMENT:
case ASTNode.CATCH_CLAUSE:
return parent.getAST().resolveWellKnownType("java.lang.Exception"); //$NON-NLS-1$
case ASTNode.FIELD_ACCESS:
if (node.equals(((FieldAccess) parent).getName())) {
return getPossibleReferenceBinding(parent);
}
break;
case ASTNode.SUPER_FIELD_ACCESS:
return getPossibleReferenceBinding(parent);
case ASTNode.QUALIFIED_NAME:
if (node.equals(((QualifiedName) parent).getName())) {
return getPossibleReferenceBinding(parent);
}
break;
case ASTNode.SWITCH_CASE:
SwitchCase switchCase= (SwitchCase) parent;
if (node.equals(switchCase.getExpression()) || (ASTHelper.isSwitchCaseExpressionsSupportedInAST(switchCase.getAST()) && switchCase.expressions().contains(node))) {
ASTNode caseParent= switchCase.getParent();
if (caseParent instanceof SwitchStatement) {
return ((SwitchStatement) caseParent).getExpression().resolveTypeBinding();
}
if (caseParent instanceof SwitchExpression) {
return ((SwitchExpression) caseParent).getExpression().resolveTypeBinding();
}
}
break;
case ASTNode.ASSERT_STATEMENT:
if (node.getLocationInParent() == AssertStatement.EXPRESSION_PROPERTY) {
return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$
}
return parent.getAST().resolveWellKnownType("java.lang.String"); //$NON-NLS-1$
case ASTNode.SINGLE_MEMBER_ANNOTATION: {
IMethodBinding annotMember= findAnnotationMember((Annotation) parent, "value"); //$NON-NLS-1$
if (annotMember != null) {
return annotMember.getReturnType();
}
break;
}
case ASTNode.MEMBER_VALUE_PAIR: {
String name= ((MemberValuePair) parent).getName().getIdentifier();
IMethodBinding annotMember= findAnnotationMember((Annotation) parent.getParent(), name);
if (annotMember != null) {
return annotMember.getReturnType();
}
break;
}
default:
// do nothing
}
return null;
}
public static IMethodBinding findAnnotationMember(Annotation annotation, String name) {
ITypeBinding annotBinding= annotation.resolveTypeBinding();
if (annotBinding != null) {
return Bindings.findMethodInType(annotBinding, name, (String[]) null);
}
return null;
}
public static ITypeBinding getReducedDimensionBinding(ITypeBinding arrayBinding, int dimsToReduce) {
while (dimsToReduce > 0) {
arrayBinding= arrayBinding.getComponentType();
dimsToReduce--;
}
return arrayBinding;
}
public static ITypeBinding getParameterTypeBinding(ASTNode node, List<Expression> args, IMethodBinding binding) {
int index= args.indexOf(node);
return getParameterTypeBinding(binding, index);
}
public static ITypeBinding getParameterTypeBinding(IMethodBinding methodBinding, int argumentIndex) {
ITypeBinding[] paramTypes= methodBinding.getParameterTypes();
if (methodBinding.isVarargs() && argumentIndex >= paramTypes.length - 1) {
return paramTypes[paramTypes.length - 1].getComponentType();
}
if (argumentIndex >= 0 && argumentIndex < paramTypes.length) {
return paramTypes[argumentIndex];
}
return null;
}
public static ITypeBinding guessBindingForTypeReference(ASTNode node) {
StructuralPropertyDescriptor locationInParent= node.getLocationInParent();
if (locationInParent == QualifiedName.QUALIFIER_PROPERTY) {
return null; // can't guess type for X.A
}
if (locationInParent == SimpleType.NAME_PROPERTY ||
locationInParent == NameQualifiedType.NAME_PROPERTY) {
node= node.getParent();
}
ITypeBinding binding= Bindings.normalizeTypeBinding(getPossibleTypeBinding(node));
if (binding != null) {
if (binding.isWildcardType()) {
return normalizeWildcardType(binding, true, node.getAST());
}
}
return binding;
}
private static ITypeBinding getPossibleTypeBinding(ASTNode node) {
ASTNode parent= node.getParent();
switch (parent.getNodeType()) {
case ASTNode.ARRAY_TYPE: {
int dim= ((ArrayType) parent).getDimensions();
ITypeBinding parentBinding= getPossibleTypeBinding(parent);
if (parentBinding != null && parentBinding.getDimensions() == dim) {
return parentBinding.getElementType();
}
return null;
}
case ASTNode.PARAMETERIZED_TYPE: {
ITypeBinding parentBinding= getPossibleTypeBinding(parent);
if (parentBinding == null || !parentBinding.isParameterizedType()) {
return null;
}
if (node.getLocationInParent() == ParameterizedType.TYPE_PROPERTY) {
return parentBinding;
}
ITypeBinding[] typeArguments= parentBinding.getTypeArguments();
List<Type> argumentNodes= ((ParameterizedType) parent).typeArguments();
int index= argumentNodes.indexOf(node);
if (index != -1 && typeArguments.length == argumentNodes.size()) {
return typeArguments[index];
}
return null;
}
case ASTNode.WILDCARD_TYPE: {
ITypeBinding parentBinding= getPossibleTypeBinding(parent);
if (parentBinding == null || !parentBinding.isWildcardType()) {
return null;
}
WildcardType wildcardType= (WildcardType) parent;
if (parentBinding.isUpperbound() == wildcardType.isUpperBound()) {
return parentBinding.getBound();
}
return null;
}
case ASTNode.QUALIFIED_TYPE: {
ITypeBinding parentBinding= getPossibleTypeBinding(parent);
if (parentBinding == null || !parentBinding.isMember()) {
return null;
}
if (node.getLocationInParent() == QualifiedType.QUALIFIER_PROPERTY) {
return parentBinding.getDeclaringClass();
}
return parentBinding;
}
case ASTNode.NAME_QUALIFIED_TYPE: {
ITypeBinding parentBinding= getPossibleTypeBinding(parent);
if (parentBinding == null || !parentBinding.isMember()) {
return null;
}
if (node.getLocationInParent() == NameQualifiedType.QUALIFIER_PROPERTY) {
return parentBinding.getDeclaringClass();
}
return parentBinding;
}
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
return guessVariableType(((VariableDeclarationStatement) parent).fragments());
case ASTNode.FIELD_DECLARATION:
return guessVariableType(((FieldDeclaration) parent).fragments());
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
return guessVariableType(((VariableDeclarationExpression) parent).fragments());
case ASTNode.SINGLE_VARIABLE_DECLARATION:
SingleVariableDeclaration varDecl= (SingleVariableDeclaration) parent;
if (varDecl.getInitializer() != null) {
return Bindings.normalizeTypeBinding(varDecl.getInitializer().resolveTypeBinding());
}
break;
case ASTNode.ARRAY_CREATION:
ArrayCreation creation= (ArrayCreation) parent;
if (creation.getInitializer() != null) {
return creation.getInitializer().resolveTypeBinding();
}
return getPossibleReferenceBinding(parent);
case ASTNode.TYPE_LITERAL:
return ((TypeLiteral) parent).getType().resolveBinding();
case ASTNode.CLASS_INSTANCE_CREATION:
return getPossibleReferenceBinding(parent);
case ASTNode.CAST_EXPRESSION:
return getPossibleReferenceBinding(parent);
case ASTNode.TAG_ELEMENT:
TagElement tagElement= (TagElement) parent;
if (TagElement.TAG_THROWS.equals(tagElement.getTagName()) || TagElement.TAG_EXCEPTION.equals(tagElement.getTagName())) {
ASTNode methNode= tagElement.getParent().getParent();
if (methNode instanceof MethodDeclaration) {
List<Type> thrownExceptions= ((MethodDeclaration) methNode).thrownExceptionTypes();
if (thrownExceptions.size() == 1) {
return thrownExceptions.get(0).resolveBinding();
}
}
}
break;
}
return null;
}
public static ITypeBinding guessVariableType(List<VariableDeclarationFragment> fragments) {
for (VariableDeclarationFragment frag : fragments) {
if (frag.getInitializer() != null) {
return Bindings.normalizeTypeBinding(frag.getInitializer().resolveTypeBinding());
}
}
return null;
}
/**
* Finds all type bindings that contain a method of a given signature
* @param searchRoot the ast node to start the search from
* @param selector the method name
* @param arguments the method arguments
* @param context the context in which the method would be called
* @return returns all types known in the AST that have a method with a given name
*/
public static ITypeBinding[] getQualifierGuess(ASTNode searchRoot, final String selector, List<Expression> arguments, final IBinding context) {
final int nArgs= arguments.size();
final ArrayList<ITypeBinding> result= new ArrayList<>();
// test if selector is a object method
ITypeBinding binding= searchRoot.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
for (IMethodBinding meth : binding.getDeclaredMethods()) {
if (meth.getName().equals(selector) && meth.getParameterTypes().length == nArgs) {
return new ITypeBinding[] { binding };
}
}
visitAllBindings(searchRoot, new TypeBindingVisitor() {
private HashSet<String> fVisitedBindings= new HashSet<>(100);
@Override
public boolean visit(ITypeBinding node) {
node= Bindings.normalizeTypeBinding(node);
if (node == null) {
return true;
}
if (!fVisitedBindings.add(node.getKey())) {
return true;
}
if (node.isGenericType()) {
return true; // only look at parameterized types
}
if (context != null && !isUseableTypeInContext(node, context, false)) {
return true;
}
for (IMethodBinding meth : node.getDeclaredMethods()) {
if (meth.getName().equals(selector) && meth.getParameterTypes().length == nArgs) {
result.add(node);
}
}
return true;
}
});
return result.toArray(new ITypeBinding[result.size()]);
}
public static void visitAllBindings(ASTNode astRoot, TypeBindingVisitor visitor) {
try {
astRoot.accept(new AllBindingsVisitor(visitor));
} catch (AllBindingsVisitor.VisitCancelledException e) {
// visit cancelled
}
}
private static class AllBindingsVisitor extends GenericVisitor {
private final TypeBindingVisitor fVisitor;
private static class VisitCancelledException extends RuntimeException {
private static final long serialVersionUID= 1L;
}
public AllBindingsVisitor(TypeBindingVisitor visitor) {
super(true);
fVisitor= visitor;
}
@Override
public boolean visit(SimpleName node) {
ITypeBinding binding= node.resolveTypeBinding();
if (binding != null) {
boolean res= fVisitor.visit(binding);
if (res) {
res= Bindings.visitHierarchy(binding, fVisitor);
}
if (!res) {
throw new VisitCancelledException();
}
}
return false;
}
}
public static IBinding getParentMethodOrTypeBinding(ASTNode node) {
do {
if (node instanceof MethodDeclaration) {
return ((MethodDeclaration) node).resolveBinding();
} else if (node instanceof AbstractTypeDeclaration) {
return ((AbstractTypeDeclaration) node).resolveBinding();
} else if (node instanceof AnonymousClassDeclaration) {
return ((AnonymousClassDeclaration) node).resolveBinding();
}
node= node.getParent();
} while (node != null);
return null;
}
public static BodyDeclaration findParentBodyDeclaration(ASTNode node) {
while ((node != null) && (!(node instanceof BodyDeclaration))) {
node= node.getParent();
}
return (BodyDeclaration) node;
}
public static BodyDeclaration findParentBodyDeclaration(ASTNode node, boolean treatModifiersOutside) {
StructuralPropertyDescriptor lastLocation= null;
while (node != null) {
if (node instanceof BodyDeclaration) {
BodyDeclaration decl= (BodyDeclaration) node;
if (!treatModifiersOutside || lastLocation != decl.getModifiersProperty()) {
return decl;
}
treatModifiersOutside= false;
}
lastLocation= node.getLocationInParent();
node= node.getParent();
}
return null;
}
public static CompilationUnit findParentCompilationUnit(ASTNode node) {
return (CompilationUnit) findAncestor(node, ASTNode.COMPILATION_UNIT);
}
/**
* Finds the ancestor type of <code>node</code> (includes <code>node</code> in the search).
*
* @param node the node to start the search from, can be <code>null</code>
* @param treatModifiersOutside if set, modifiers are not part of their type, but of the type's
* parent
* @return returns the ancestor type of <code>node</code> (AbstractTypeDeclaration or
* AnonymousTypeDeclaration) if any (including <code>node</code>), <code>null</code>
* otherwise
*/
public static ASTNode findParentType(ASTNode node, boolean treatModifiersOutside) {
StructuralPropertyDescriptor lastLocation= null;
while (node != null) {
if (node instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration decl= (AbstractTypeDeclaration) node;
if (!treatModifiersOutside || lastLocation != decl.getModifiersProperty()) {
return decl;
}
} else if (node instanceof AnonymousClassDeclaration) {
return node;
}
lastLocation= node.getLocationInParent();
node= node.getParent();
}
return null;
}
/**
* Finds the ancestor type of <code>node</code> (includes <code>node</code> in the search).
*
* @param node the node to start the search from, can be <code>null</code>
* @return returns the ancestor type of <code>node</code> (AbstractTypeDeclaration or
* AnonymousTypeDeclaration) if any (including <code>node</code>), <code>null</code>
* otherwise
*/
public static ASTNode findParentType(ASTNode node) {
return findParentType(node, false);
}
/**
* The node's enclosing method declaration or <code>null</code> if
* the node is not inside a method and is not a method declaration itself.
*
* @param node a node
* @return the enclosing method declaration or <code>null</code>
*/
public static MethodDeclaration findParentMethodDeclaration(ASTNode node) {
while (node != null) {
if (node instanceof MethodDeclaration) {
return (MethodDeclaration) node;
} else if (node instanceof BodyDeclaration || node instanceof AnonymousClassDeclaration || node instanceof LambdaExpression) {
return null;
}
node= node.getParent();
}
return null;
}
/**
* Returns the lambda expression node which encloses the given <code>node</code>, or
* <code>null</code> if none.
*
* @param node the node
* @return the enclosing lambda expression node for the given <code>node</code>, or
* <code>null</code> if none
*
* @since 3.10
*/
public static LambdaExpression findEnclosingLambdaExpression(ASTNode node) {
node= node.getParent();
while (node != null) {
if (node instanceof LambdaExpression) {
return (LambdaExpression) node;
}
if (node instanceof BodyDeclaration || node instanceof AnonymousClassDeclaration) {
return null;
}
node= node.getParent();
}
return null;
}
/**
* Returns the closest ancestor of <code>node</code> (including <code>node</code> itself)
* whose type is <code>nodeType</code>, or <code>null</code> if none.
* <p>
* <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc.
* The resulting node may be in a totally different scope than the given node.
* Consider using one of the other {@link ASTResolving}<code>.find(..)</code> methods instead.
* </p>
* @param node the node
* @param nodeType the node type constant from {@link ASTNode}
* @return the closest ancestor of <code>node</code> (including <code>node</code> itself)
* whose type is <code>nodeType</code>, or <code>null</code> if none
*/
public static ASTNode findAncestor(ASTNode node, int nodeType) {
while ((node != null) && (node.getNodeType() != nodeType)) {
node= node.getParent();
}
return node;
}
public static Statement findParentStatement(ASTNode node) {
while ((node != null) && (!(node instanceof Statement))) {
node= node.getParent();
if (node instanceof BodyDeclaration) {
return null;
}
}
return (Statement) node;
}
public static TryStatement findParentTryStatement(ASTNode node) {
while ((node != null) && (!(node instanceof TryStatement))) {
node= node.getParent();
if (node instanceof BodyDeclaration || node instanceof LambdaExpression) {
return null;
}
}
return (TryStatement) node;
}
public static boolean isInsideConstructorInvocation(MethodDeclaration methodDeclaration, ASTNode node) {
if (methodDeclaration.isConstructor()) {
Statement statement= findParentStatement(node);
if (statement instanceof ConstructorInvocation || statement instanceof SuperConstructorInvocation) {
return true; // argument in a this or super call
}
}
return false;
}
public static boolean isInsideModifiers(ASTNode node) {
while (node != null && !(node instanceof BodyDeclaration)) {
if (node instanceof Annotation) {
return true;
}
node= node.getParent();
}
return false;
}
public static boolean isInStaticContext(ASTNode selectedNode) {
BodyDeclaration decl= findParentBodyDeclaration(selectedNode);
if (decl instanceof MethodDeclaration) {
if (isInsideConstructorInvocation((MethodDeclaration) decl, selectedNode)) {
return true;
}
return Modifier.isStatic(decl.getModifiers());
} else if (decl instanceof Initializer) {
return Modifier.isStatic(((Initializer)decl).getModifiers());
} else if (decl instanceof FieldDeclaration) {
return JdtFlags.isStatic(decl);
}
return false;
}
public static boolean isWriteAccess(Name selectedNode) {
ASTNode curr= selectedNode;
ASTNode parent= curr.getParent();
while (parent != null) {
switch (parent.getNodeType()) {
case ASTNode.QUALIFIED_NAME:
if (((QualifiedName) parent).getQualifier() == curr) {
return false;
}
break;
case ASTNode.FIELD_ACCESS:
if (((FieldAccess) parent).getExpression() == curr) {
return false;
}
break;
case ASTNode.SUPER_FIELD_ACCESS:
break;
case ASTNode.ASSIGNMENT:
return ((Assignment) parent).getLeftHandSide() == curr;
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
case ASTNode.SINGLE_VARIABLE_DECLARATION:
return ((VariableDeclaration) parent).getName() == curr;
case ASTNode.POSTFIX_EXPRESSION:
return true;
case ASTNode.PREFIX_EXPRESSION:
PrefixExpression.Operator op= ((PrefixExpression) parent).getOperator();
return op == PrefixExpression.Operator.DECREMENT || op == PrefixExpression.Operator.INCREMENT;
default:
return false;
}
curr= parent;
parent= curr.getParent();
}
return false;
}
public static String getFullName(Name name) {
return name.getFullyQualifiedName();
}
public static ICompilationUnit findCompilationUnitForBinding(ICompilationUnit cu, CompilationUnit astRoot, ITypeBinding binding) throws JavaModelException {
if (binding == null || !binding.isFromSource() || binding.isTypeVariable() || binding.isWildcardType()) {
return null;
}
ASTNode node= astRoot.findDeclaringNode(binding.getTypeDeclaration());
if (node == null) {
ICompilationUnit targetCU= Bindings.findCompilationUnit(binding, cu.getJavaProject());
if (targetCU != null) {
return targetCU;
}
return null;
} else if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) {
return cu;
}
return null;
}
private static final Code[] CODE_ORDER= { PrimitiveType.CHAR, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.LONG, PrimitiveType.FLOAT, PrimitiveType.DOUBLE };
public static ITypeBinding[] getNarrowingTypes(AST ast, ITypeBinding type) {
ArrayList<ITypeBinding> res= new ArrayList<>();
res.add(type);
if (type.isPrimitive()) {
Code code= PrimitiveType.toCode(type.getName());
for (int i= 0; i < CODE_ORDER.length && code != CODE_ORDER[i]; i++) {
String typeName= CODE_ORDER[i].toString();
res.add(ast.resolveWellKnownType(typeName));
}
}
return res.toArray(new ITypeBinding[res.size()]);
}
public static ITypeBinding[] getRelaxingTypes(AST ast, ITypeBinding type) {
ArrayList<ITypeBinding> res= new ArrayList<>();
res.add(type);
if (type.isArray()) {
res.add(ast.resolveWellKnownType("java.lang.Object")); //$NON-NLS-1$
// The following two types are not available in some j2me implementations, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=288060 :
ITypeBinding serializable= ast.resolveWellKnownType("java.io.Serializable"); //$NON-NLS-1$
if (serializable != null)
res.add(serializable);
ITypeBinding cloneable= ast.resolveWellKnownType("java.lang.Cloneable"); //$NON-NLS-1$
if (cloneable != null)
res.add(cloneable);
} else if (type.isPrimitive()) {
Code code= PrimitiveType.toCode(type.getName());
boolean found= false;
for (Code order : CODE_ORDER) {
if (found) {
String typeName= order.toString();
res.add(ast.resolveWellKnownType(typeName));
}
if (code == order) {
found= true;
}
}
} else {
collectRelaxingTypes(res, type);
}
return res.toArray(new ITypeBinding[res.size()]);
}
private static void collectRelaxingTypes(Collection<ITypeBinding> res, ITypeBinding type) {
for (ITypeBinding curr : type.getInterfaces()) {
if (!res.contains(curr)) {
res.add(curr);
}
collectRelaxingTypes(res, curr);
}
ITypeBinding binding= type.getSuperclass();
if (binding != null) {
if (!res.contains(binding)) {
res.add(binding);
}
collectRelaxingTypes(res, binding);
}
}
public static String[] getUsedVariableNames(ASTNode node) {
CompilationUnit root= (CompilationUnit) node.getRoot();
Collection<String> res= (new ScopeAnalyzer(root)).getUsedVariableNames(node.getStartPosition(), node.getLength());
return res.toArray(new String[res.size()]);
}
private static boolean isVariableDefinedInContext(IBinding binding, ITypeBinding typeVariable) {
if (binding.getKind() == IBinding.VARIABLE) {
IVariableBinding var= (IVariableBinding) binding;
binding= var.getDeclaringMethod();
if (binding == null) {
binding= var.getDeclaringClass();
}
}
if (binding instanceof IMethodBinding) {
if (binding == typeVariable.getDeclaringMethod()) {
return true;
}
binding= ((IMethodBinding) binding).getDeclaringClass();
}
while (binding instanceof ITypeBinding) {
if (binding == typeVariable.getDeclaringClass()) {
return true;
}
if (Modifier.isStatic(binding.getModifiers())) {
break;
}
binding= ((ITypeBinding) binding).getDeclaringClass();
}
return false;
}
public static boolean isUseableTypeInContext(ITypeBinding type, IBinding context, boolean noWildcards) {
if (type.isArray()) {
type= type.getElementType();
}
if (type.isAnonymous()) {
return false;
}
if (type.isRawType() || type.isPrimitive()) {
return true;
}
if (type.isTypeVariable()) {
return isVariableDefinedInContext(context, type);
}
if (type.isGenericType()) {
for (ITypeBinding typeParameter : type.getTypeParameters()) {
if (!isUseableTypeInContext(typeParameter, context, noWildcards)) {
return false;
}
}
return true;
}
if (type.isParameterizedType()) {
for (ITypeBinding typeArgument : type.getTypeArguments()) {
if (!isUseableTypeInContext(typeArgument, context, noWildcards)) {
return false;
}
}
return true;
}
if (type.isCapture()) {
type= type.getWildcard();
}
if (type.isWildcardType()) {
if (noWildcards) {
return false;
}
if (type.getBound() != null) {
return isUseableTypeInContext(type.getBound(), context, noWildcards);
}
}
return true;
}
/**
* Use this method before creating a type for a wildcard. Either to assign a wildcard to a new type or for a type to be assigned.
*
* @param wildcardType the wildcard type to normalize
* @param isBindingToAssign if true, then the type X for new variable x is returned (X x= s);
* if false, the type of an expression x (R r= x)
* @param ast the current AST
* @return the normalized binding or null when only the 'null' binding
*
* @see Bindings#normalizeForDeclarationUse(ITypeBinding, AST)
*/
public static ITypeBinding normalizeWildcardType(ITypeBinding wildcardType, boolean isBindingToAssign, AST ast) {
ITypeBinding bound= wildcardType.getBound();
if (isBindingToAssign) {
if (bound == null || !wildcardType.isUpperbound()) {
ITypeBinding[] typeBounds= wildcardType.getTypeBounds();
if (typeBounds.length > 0) {
return typeBounds[0];
} else {
return wildcardType.getErasure();
}
}
} else {
if (bound == null || wildcardType.isUpperbound()) {
return null;
}
}
return bound;
}
public static CompilationUnit createQuickFixAST(ICompilationUnit compilationUnit, IProgressMonitor monitor) {
ASTParser astParser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
astParser.setSource(compilationUnit);
astParser.setResolveBindings(true);
astParser.setStatementsRecovery(IASTSharedValues.SHARED_AST_STATEMENT_RECOVERY);
astParser.setBindingsRecovery(IASTSharedValues.SHARED_BINDING_RECOVERY);
return (CompilationUnit) astParser.createAST(monitor);
}
public static int getPossibleTypeKinds(ASTNode node, boolean is50OrHigher) {
int kinds= internalGetPossibleTypeKinds(node);
if (!is50OrHigher) {
kinds &= (TypeKinds.INTERFACES | TypeKinds.CLASSES);
}
return kinds;
}
private static int internalGetPossibleTypeKinds(ASTNode node) {
int kind= TypeKinds.ALL_TYPES;
int mask= TypeKinds.ALL_TYPES | TypeKinds.VOIDTYPE;
ASTNode parent= node.getParent();
while (parent instanceof QualifiedName) {
if (node.getLocationInParent() == QualifiedName.QUALIFIER_PROPERTY) {
return TypeKinds.REF_TYPES;
}
node= parent;
parent= parent.getParent();
mask= TypeKinds.REF_TYPES;
}
while (parent instanceof Type) {
if (parent instanceof QualifiedType) {
if (node.getLocationInParent() == QualifiedType.QUALIFIER_PROPERTY) {
return mask & (TypeKinds.REF_TYPES);
}
mask&= TypeKinds.REF_TYPES;
} else if (parent instanceof NameQualifiedType) {
if (node.getLocationInParent() == NameQualifiedType.QUALIFIER_PROPERTY) {
return mask & (TypeKinds.REF_TYPES);
}
mask&= TypeKinds.REF_TYPES;
} else if (parent instanceof ParameterizedType) {
if (node.getLocationInParent() == ParameterizedType.TYPE_ARGUMENTS_PROPERTY) {
return mask & TypeKinds.REF_TYPES_AND_VAR;
}
mask&= TypeKinds.CLASSES | TypeKinds.INTERFACES;
} else if (parent instanceof WildcardType) {
if (node.getLocationInParent() == WildcardType.BOUND_PROPERTY) {
return mask & TypeKinds.REF_TYPES_AND_VAR;
}
}
node= parent;
parent= parent.getParent();
}
if (parent != null) {
switch (parent.getNodeType()) {
case ASTNode.TYPE_DECLARATION:
if (node.getLocationInParent() == TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY) {
kind= TypeKinds.INTERFACES;
} else if (node.getLocationInParent() == TypeDeclaration.SUPERCLASS_TYPE_PROPERTY) {
kind= TypeKinds.CLASSES;
} else if (node.getLocationInParent() == TypeDeclaration.PERMITS_TYPES_PROPERTY) {
if (ASTHelper.isSealedTypeSupportedInAST(node.getAST())) {
kind = TypeKinds.CLASSES;
if (((TypeDeclaration) parent).isInterface()) {
kind |= TypeKinds.INTERFACES;
}
}
}
break;
case ASTNode.ENUM_DECLARATION:
kind= TypeKinds.INTERFACES;
break;
case ASTNode.METHOD_DECLARATION:
if (node.getLocationInParent() == MethodDeclaration.THROWN_EXCEPTION_TYPES_PROPERTY) {
kind= TypeKinds.CLASSES;
} else if (node.getLocationInParent() == MethodDeclaration.RETURN_TYPE2_PROPERTY) {
kind= TypeKinds.ALL_TYPES | TypeKinds.VOIDTYPE;
}
break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
kind= TypeKinds.PRIMITIVETYPES | TypeKinds.ANNOTATIONS | TypeKinds.ENUMS;
break;
case ASTNode.INSTANCEOF_EXPRESSION:
kind= TypeKinds.REF_TYPES;
break;
case ASTNode.THROW_STATEMENT:
kind= TypeKinds.CLASSES;
break;
case ASTNode.CLASS_INSTANCE_CREATION:
if (((ClassInstanceCreation) parent).getAnonymousClassDeclaration() == null) {
kind= TypeKinds.CLASSES;
} else {
kind= TypeKinds.CLASSES | TypeKinds.INTERFACES;
}
break;
case ASTNode.SINGLE_VARIABLE_DECLARATION:
int superParent= parent.getParent().getNodeType();
if (superParent == ASTNode.CATCH_CLAUSE) {
kind= TypeKinds.CLASSES;
} else if (superParent == ASTNode.ENHANCED_FOR_STATEMENT) {
kind= TypeKinds.REF_TYPES;
}
break;
case ASTNode.TAG_ELEMENT:
kind= TypeKinds.REF_TYPES;
break;
case ASTNode.MARKER_ANNOTATION:
case ASTNode.SINGLE_MEMBER_ANNOTATION:
case ASTNode.NORMAL_ANNOTATION:
kind= TypeKinds.ANNOTATIONS;
break;
case ASTNode.TYPE_PARAMETER:
if (((TypeParameter) parent).typeBounds().indexOf(node) > 0) {
kind= TypeKinds.INTERFACES;
} else {
kind= TypeKinds.REF_TYPES_AND_VAR;
}
break;
case ASTNode.TYPE_LITERAL:
kind= TypeKinds.REF_TYPES;
break;
default:
}
}
return kind & mask;
}
}