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
/******************************************************************************* * 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.Iterator; 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.manipulation.TypeKinds; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.QualifiedType; 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.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;
Helper methods to find AST nodes or bindings.
/** * 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 { public static ITypeBinding guessBindingForReference(ASTNode node) { 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: 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()) || (switchCase.getAST().isPreviewEnabled() && 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 (Iterator<VariableDeclarationFragment> iter= fragments.iterator(); iter.hasNext();) { VariableDeclarationFragment frag= iter.next(); if (frag.getInitializer() != null) { return Bindings.normalizeTypeBinding(frag.getInitializer().resolveTypeBinding()); } } return null; }
Finds all type bindings that contain a method of a given signature
Params:
  • searchRoot – the ast node to start the search from
  • selector – the method name
  • arguments – the method arguments
  • context – the context in which the method would be called
Returns:returns all types known in the AST that have a method with a given name
/** * 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$ IMethodBinding[] objectMethods= binding.getDeclaredMethods(); for (int i= 0; i < objectMethods.length; i++) { IMethodBinding meth= objectMethods[i]; 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; } IMethodBinding[] methods= node.getDeclaredMethods(); for (int i= 0; i < methods.length; i++) { IMethodBinding meth= methods[i]; 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 (BodyDeclaration) node; } public static CompilationUnit findParentCompilationUnit(ASTNode node) { return (CompilationUnit) findAncestor(node, ASTNode.COMPILATION_UNIT); }
Finds the ancestor type of node (includes node in the search).
Params:
  • node – the node to start the search from, can be null
  • treatModifiersOutside – if set, modifiers are not part of their type, but of the type's parent
Returns:returns the ancestor type of node (AbstractTypeDeclaration or AnonymousTypeDeclaration) if any (including node), null otherwise
/** * 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 node (includes node in the search).
Params:
  • node – the node to start the search from, can be null
Returns:returns the ancestor type of node (AbstractTypeDeclaration or AnonymousTypeDeclaration) if any (including node), null otherwise
/** * 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 null if the node is not inside a method and is not a method declaration itself.
Params:
  • node – a node
Returns:the enclosing method declaration or null
/** * 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 node, or null if none.
Params:
  • node – the node
Returns:the enclosing lambda expression node for the given node, or null if none
Since:3.10
/** * 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 node (including node itself) whose type is nodeType, or null if none.

Warning: 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 ASTResolving.find(..) methods instead.

Params:
  • node – the node
  • nodeType – the node type constant from ASTNode
Returns:the closest ancestor of node (including node itself) whose type is nodeType, or null if none
/** * 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 (int i= 0; i < CODE_ORDER.length; i++) { if (found) { String typeName= CODE_ORDER[i].toString(); res.add(ast.resolveWellKnownType(typeName)); } if (code == CODE_ORDER[i]) { found= true; } } } else { collectRelaxingTypes(res, type); } return res.toArray(new ITypeBinding[res.size()]); } private static void collectRelaxingTypes(Collection<ITypeBinding> res, ITypeBinding type) { ITypeBinding[] interfaces= type.getInterfaces(); for (int i= 0; i < interfaces.length; i++) { ITypeBinding curr= interfaces[i]; 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()) { ITypeBinding[] typeParameters= type.getTypeParameters(); for (int i= 0; i < typeParameters.length; i++) { if (!isUseableTypeInContext(typeParameters[i], context, noWildcards)) { return false; } } return true; } if (type.isParameterizedType()) { ITypeBinding[] typeArguments= type.getTypeArguments(); for (int i= 0; i < typeArguments.length; i++) { if (!isUseableTypeInContext(typeArguments[i], 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.
Params:
  • wildcardType – the wildcard type to normalize
  • 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)
  • ast – the current AST
See Also:
Returns:the normalized binding or null when only the 'null' binding
/** * 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; } 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; } }