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.corext.dom; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; 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.ForStatement; 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.ImportDeclaration; import org.eclipse.jdt.core.dom.Initializer; 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.QualifiedName; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.jdt.core.dom.SuperFieldAccess; 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.Type; import org.eclipse.jdt.core.dom.TypeDeclarationStatement; 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.VariableDeclarationStatement; import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
Evaluates all fields, methods and types available (declared) at a given offset in a compilation unit (Code assist that returns IBindings)
/** * Evaluates all fields, methods and types available (declared) at a given offset * in a compilation unit (Code assist that returns IBindings) */
public class ScopeAnalyzer { private static final IBinding[] NO_BINDING= new IBinding[0];
Flag to specify that method should be reported.
/** * Flag to specify that method should be reported. */
public static final int METHODS= 1;
Flag to specify that variables should be reported.
/** * Flag to specify that variables should be reported. */
public static final int VARIABLES= 2;
Flag to specify that types should be reported.
/** * Flag to specify that types should be reported. */
public static final int TYPES= 4;
Flag to specify that fields should not be reported.
/** * Flag to specify that fields should not be reported. */
public static final int NO_FIELDS= 8;
Flag to specify that only visible elements should be added.
/** * Flag to specify that only visible elements should be added. */
public static final int CHECK_VISIBILITY= 16; private static interface IBindingRequestor { boolean acceptBinding(IBinding binding); } private static class DefaultBindingRequestor implements IBindingRequestor { private final List<IBinding> fResult; private final HashSet<String> fNamesAdded; private final int fFlags; private final ITypeBinding fParentTypeBinding; public DefaultBindingRequestor(ITypeBinding parentTypeBinding, int flags) { fParentTypeBinding= parentTypeBinding; fFlags= flags; fResult= new ArrayList<>(); fNamesAdded= new HashSet<>(); } public DefaultBindingRequestor() { this(null, 0); } @Override public boolean acceptBinding(IBinding binding) { if (binding == null) return false; String signature= getSignature(binding, fFlags); if (signature != null && fNamesAdded.add(signature)) { // avoid duplicated results from inheritance fResult.add(binding); } return false; } public List<IBinding> getResult() { if (hasFlag(CHECK_VISIBILITY, fFlags)) { for (int i= fResult.size() - 1; i >= 0; i--) { IBinding binding= fResult.get(i); if (!isVisible(binding, fParentTypeBinding)) { fResult.remove(i); } } } if (hasFlag(NO_FIELDS, fFlags)) { for (int i= fResult.size() - 1; i >= 0; i--) { IBinding binding= fResult.get(i); if (binding instanceof IVariableBinding && ((IVariableBinding) binding).isField()) { fResult.remove(i); } } } return fResult; } } private final HashSet<ITypeBinding> fTypesVisited; private final CompilationUnit fRoot; public ScopeAnalyzer(CompilationUnit root) { fTypesVisited= new HashSet<>(); fRoot= root; } private void clearLists() { fTypesVisited.clear(); } private static String getSignature(IBinding binding, int flags) { if (binding != null) { switch (binding.getKind()) { case IBinding.METHOD: StringBuilder buf= new StringBuilder(); buf.append('M'); buf.append(binding.getName()).append('('); ITypeBinding[] parameters= ((IMethodBinding) binding).getParameterTypes(); for (int i= 0; i < parameters.length; i++) { if (i > 0) { buf.append(','); } ITypeBinding paramType= parameters[i].getErasure(); buf.append(paramType.getQualifiedName()); } buf.append(')'); return buf.toString(); case IBinding.VARIABLE: if (hasFlag(NO_FIELDS, flags) && ((IVariableBinding) binding).isField()) { return 'F' + binding.getName(); } return 'V' + binding.getName(); case IBinding.TYPE: return 'T' + binding.getName(); } } return null; } static final boolean hasFlag(int property, int flags) { return (flags & property) != 0; }
Collects all elements available in a type and its hierarchy
Params:
  • binding – The type binding
  • flags – Flags defining the elements to report
  • requestor – the requestor to which all results are reported
Returns:return true if the requestor has reported the binding as found and no further results are required
/** * Collects all elements available in a type and its hierarchy * @param binding The type binding * @param flags Flags defining the elements to report * @param requestor the requestor to which all results are reported * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required */
private boolean addInherited(ITypeBinding binding, int flags, IBindingRequestor requestor) { return addInherited(binding, false, flags, requestor); } private boolean addInherited(ITypeBinding binding, boolean isSuperInterfaceBinding, int flags, IBindingRequestor requestor) { if (!fTypesVisited.add(binding)) { return false; } if (hasFlag(VARIABLES, flags)) { IVariableBinding[] variableBindings= binding.getDeclaredFields(); for (int i= 0; i < variableBindings.length; i++) { if (requestor.acceptBinding(variableBindings[i])) return true; } } if (hasFlag(METHODS, flags)) { IMethodBinding[] methodBindings= binding.getDeclaredMethods(); for (int i= 0; i < methodBindings.length; i++) { IMethodBinding curr= methodBindings[i]; if (isSuperInterfaceBinding && Modifier.isStatic(curr.getModifiers())) { continue; } if (!curr.isSynthetic() && !curr.isConstructor()) { if (requestor.acceptBinding(curr)) return true; } } } if (hasFlag(TYPES, flags)) { ITypeBinding[] typeBindings= binding.getDeclaredTypes(); for (int i= 0; i < typeBindings.length; i++) { ITypeBinding curr= typeBindings[i]; if (requestor.acceptBinding(curr)) return true; } } ITypeBinding superClass= binding.getSuperclass(); if (superClass != null) { if (addInherited(superClass, flags, requestor)) // recursive return true; } else if (binding.isArray()) { if (addInherited(fRoot.getAST().resolveWellKnownType("java.lang.Object"), flags, requestor)) //$NON-NLS-1$ return true; } ITypeBinding[] interfaces= binding.getInterfaces(); // includes looking for methods: abstract, unimplemented methods for (int i= 0; i < interfaces.length; i++) { if (addInherited(interfaces[i], true, flags, requestor)) // recursive return true; } return false; }
Collects all elements available in a type: its hierarchy and its outer scopes.
Params:
  • binding – The type binding
  • flags – Flags defining the elements to report
  • requestor – the requestor to which all results are reported
Returns:return true if the requestor has reported the binding as found and no further results are required
/** * Collects all elements available in a type: its hierarchy and its outer scopes. * @param binding The type binding * @param flags Flags defining the elements to report * @param requestor the requestor to which all results are reported * @return return <code>true</code> if the requestor has reported the binding as found and no further results are required */
private boolean addTypeDeclarations(ITypeBinding binding, int flags, IBindingRequestor requestor) { if (hasFlag(TYPES, flags) && !binding.isAnonymous()) { if (requestor.acceptBinding(binding)) return true; ITypeBinding[] typeParameters= binding.getTypeParameters(); for (int i= 0; i < typeParameters.length; i++) { if (requestor.acceptBinding(typeParameters[i])) return true; } } addInherited(binding, flags, requestor); // add inherited if (binding.isLocal()) { addOuterDeclarationsForLocalType(binding, flags, requestor); } else { ITypeBinding declaringClass= binding.getDeclaringClass(); if (declaringClass != null) { if (addTypeDeclarations(declaringClass, flags, requestor)) // Recursively add inherited return true; } else if (hasFlag(TYPES, flags)) { if (fRoot.findDeclaringNode(binding) != null) { List<AbstractTypeDeclaration> types= fRoot.types(); for (int i= 0; i < types.size(); i++) { if (requestor.acceptBinding(types.get(i).resolveBinding())) return true; } } } } return false; } private boolean addOuterDeclarationsForLocalType(ITypeBinding localBinding, int flags, IBindingRequestor requestor) { ASTNode node= fRoot.findDeclaringNode(localBinding); if (node == null) { return false; } if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) { if (addLocalDeclarations(node.getParent(), flags, requestor)) return true; ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(node.getParent()); if (parentTypeBinding != null) { if (addTypeDeclarations(parentTypeBinding, flags, requestor)) return true; } } return false; } private static ITypeBinding getBinding(Expression node) { if (node != null) { return node.resolveTypeBinding(); } return null; } private static ITypeBinding getQualifier(SimpleName selector) { ASTNode parent= selector.getParent(); switch (parent.getNodeType()) { case ASTNode.METHOD_INVOCATION: MethodInvocation decl= (MethodInvocation) parent; if (selector == decl.getName()) { return getBinding(decl.getExpression()); } return null; case ASTNode.QUALIFIED_NAME: QualifiedName qualifiedName= (QualifiedName) parent; if (selector == qualifiedName.getName()) { return getBinding(qualifiedName.getQualifier()); } return null; case ASTNode.FIELD_ACCESS: FieldAccess fieldAccess= (FieldAccess) parent; if (selector == fieldAccess.getName()) { return getBinding(fieldAccess.getExpression()); } return null; case ASTNode.SUPER_FIELD_ACCESS: SuperFieldAccess superFieldAccess= (SuperFieldAccess) parent; if (selector == superFieldAccess.getName()) { ITypeBinding curr= Bindings.getBindingOfParentType(parent); return curr.getSuperclass(); } return null; case ASTNode.SUPER_METHOD_INVOCATION: { SuperMethodInvocation superInv= (SuperMethodInvocation) parent; if (selector == superInv.getName()) { ITypeBinding curr= Bindings.getBindingOfParentType(parent); return curr.getSuperclass(); } return null; } default: if (parent instanceof Type) { // bug 67644: in 'a.new X()', all member types of A are visible as location of X. ASTNode normalizedNode= ASTNodes.getNormalizedNode(parent); if (normalizedNode.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) { ClassInstanceCreation creation= (ClassInstanceCreation) normalizedNode.getParent(); return getBinding(creation.getExpression()); } } return null; } } public IBinding[] getDeclarationsInScope(SimpleName selector, int flags) { try { // special case for switch on enum StructuralPropertyDescriptor locationInParent= selector.getLocationInParent(); if (locationInParent == SwitchCase.EXPRESSION_PROPERTY || locationInParent == SwitchCase.EXPRESSIONS2_PROPERTY) { ASTNode caseParent= selector.getParent().getParent(); ITypeBinding binding= null; if (caseParent instanceof SwitchStatement) { binding= ((SwitchStatement) caseParent).getExpression().resolveTypeBinding(); } else if (caseParent instanceof SwitchExpression) { binding= ((SwitchExpression) caseParent).getExpression().resolveTypeBinding(); } if (binding != null && binding.isEnum()) { return getEnumContants(binding); } } ITypeBinding parentTypeBinding= Bindings.getBindingOfParentType(selector); if (parentTypeBinding != null) { ITypeBinding binding= getQualifier(selector); DefaultBindingRequestor requestor= new DefaultBindingRequestor(parentTypeBinding, flags); if (binding == null) { addLocalDeclarations(selector, flags, requestor); addTypeDeclarations(parentTypeBinding, flags, requestor); } else { addInherited(binding, flags, requestor); } List<IBinding> result= requestor.getResult(); return result.toArray(new IBinding[result.size()]); } return NO_BINDING; } finally { clearLists(); } } private static class SearchRequestor implements IBindingRequestor { private final int fFlags; private final ITypeBinding fParentTypeBinding; private final IBinding fToSearch; private boolean fFound; private boolean fIsVisible; public SearchRequestor(IBinding toSearch, ITypeBinding parentTypeBinding, int flag) { fFlags= flag; fToSearch= toSearch; fParentTypeBinding= parentTypeBinding; fFound= false; fIsVisible= true; } @Override public boolean acceptBinding(IBinding binding) { if (fFound) return true; if (binding == null) return false; if (fToSearch.getKind() != binding.getKind()) { return false; } boolean checkVisibility= hasFlag(CHECK_VISIBILITY, fFlags); if (binding == fToSearch) { fFound= true; } else { IBinding bindingDeclaration= Bindings.getDeclaration(binding); if (bindingDeclaration == fToSearch) { fFound= true; } else if (bindingDeclaration.getName().equals(fToSearch.getName())) { String signature= getSignature(bindingDeclaration, fFlags); if (signature != null && signature.equals(getSignature(fToSearch, fFlags))) { if (checkVisibility) { fIsVisible= false; } return true; // found element that hides the binding to find } } } if (fFound && checkVisibility) { fIsVisible= ScopeAnalyzer.isVisible(binding, fParentTypeBinding); } return fFound; } public boolean found() { return fFound; } public boolean isVisible() { return fIsVisible; } } public boolean isDeclaredInScope(IBinding declaration, SimpleName selector, int flags) { try { // special case for switch on enum StructuralPropertyDescriptor locationInParent= selector.getLocationInParent(); if (locationInParent == SwitchCase.EXPRESSION_PROPERTY || locationInParent == SwitchCase.EXPRESSIONS2_PROPERTY) { ASTNode caseParent= selector.getParent().getParent(); ITypeBinding binding= null; if (caseParent instanceof SwitchStatement) { binding= ((SwitchStatement) caseParent).getExpression().resolveTypeBinding(); } else if (caseParent instanceof SwitchExpression) { binding= ((SwitchExpression) caseParent).getExpression().resolveTypeBinding(); } if (binding != null && binding.isEnum()) { return hasEnumContants(declaration, binding.getTypeDeclaration()); } } ITypeBinding parentTypeBinding= Bindings.getBindingOfParentTypeContext(selector); if (parentTypeBinding != null) { ITypeBinding binding= getQualifier(selector); SearchRequestor requestor= new SearchRequestor(declaration, parentTypeBinding, flags); if (binding == null) { addLocalDeclarations(selector, flags, requestor); if (requestor.found()) return requestor.isVisible(); addTypeDeclarations(parentTypeBinding, flags, requestor); if (requestor.found()) return requestor.isVisible(); } else { addInherited(binding, flags, requestor); if (requestor.found()) return requestor.isVisible(); } } return false; } finally { clearLists(); } } private IVariableBinding[] getEnumContants(ITypeBinding binding) { IVariableBinding[] declaredFields= binding.getDeclaredFields(); ArrayList<IVariableBinding> res= new ArrayList<>(declaredFields.length); for (int i= 0; i < declaredFields.length; i++) { IVariableBinding curr= declaredFields[i]; if (curr.isEnumConstant()) { res.add(curr); } } return res.toArray(new IVariableBinding[res.size()]); } private boolean hasEnumContants(IBinding declaration, ITypeBinding binding) { IVariableBinding[] declaredFields= binding.getDeclaredFields(); for (int i= 0; i < declaredFields.length; i++) { IVariableBinding curr= declaredFields[i]; if (curr == declaration) return true; } return false; } public IBinding[] getDeclarationsInScope(int offset, int flags) { org.eclipse.jdt.core.dom.NodeFinder finder= new org.eclipse.jdt.core.dom.NodeFinder(fRoot, offset, 0); ASTNode node= finder.getCoveringNode(); if (node == null) { return NO_BINDING; } if (node instanceof SimpleName) { return getDeclarationsInScope((SimpleName) node, flags); } try { ITypeBinding binding= Bindings.getBindingOfParentType(node); DefaultBindingRequestor requestor= new DefaultBindingRequestor(binding, flags); addLocalDeclarations(node, offset, flags, requestor); if (binding != null) { addTypeDeclarations(binding, flags, requestor); } List<IBinding> result= requestor.getResult(); return result.toArray(new IBinding[result.size()]); } finally { clearLists(); } } private static ITypeBinding getDeclaringType(IBinding binding) { switch (binding.getKind()) { case IBinding.VARIABLE: return ((IVariableBinding) binding).getDeclaringClass(); case IBinding.METHOD: return ((IMethodBinding) binding).getDeclaringClass(); case IBinding.TYPE: ITypeBinding typeBinding= (ITypeBinding) binding; if (typeBinding.getDeclaringClass() != null) { return typeBinding; } return typeBinding; } return null; }
Evaluates if the declaration is visible in a certain context.
Params:
  • binding – The binding of the declaration to examine
  • context – The context to test in
Returns:Returns
/** * Evaluates if the declaration is visible in a certain context. * @param binding The binding of the declaration to examine * @param context The context to test in * @return Returns */
public static boolean isVisible(IBinding binding, ITypeBinding context) { if (binding.getKind() == IBinding.VARIABLE && !((IVariableBinding) binding).isField()) { return true; // all local variables found are visible } ITypeBinding declaring= getDeclaringType(binding); if (declaring == null) { return false; } declaring= declaring.getTypeDeclaration(); int modifiers= binding.getModifiers(); int contextModifiers= context.getModifiers(); if (context.isClass() && Modifier.isStatic(contextModifiers)) { if (binding.getKind() == IBinding.VARIABLE && !Modifier.isStatic(modifiers)) { return context == declaring; } } if (Modifier.isPublic(modifiers) || declaring.isInterface()) { return true; } else if (Modifier.isProtected(modifiers) || !Modifier.isPrivate(modifiers)) { if (declaring.getPackage() == context.getPackage()) { return true; } return isTypeInScope(declaring, context, Modifier.isProtected(modifiers)); } // private visibility return isTypeInScope(declaring, context, false); } private static boolean isTypeInScope(ITypeBinding declaring, ITypeBinding context, boolean includeHierarchy) { ITypeBinding curr= context.getTypeDeclaration(); while (curr != null && curr != declaring) { if (includeHierarchy && isInSuperTypeHierarchy(declaring, curr)) { return true; } curr= curr.getDeclaringClass(); } return curr == declaring; } /* * This method is different from Binding.isSuperType as type declarations are compared */ private static boolean isInSuperTypeHierarchy(ITypeBinding possibleSuperTypeDecl, ITypeBinding type) { if (type == possibleSuperTypeDecl) { return true; } ITypeBinding superClass= type.getSuperclass(); if (superClass != null) { if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superClass.getTypeDeclaration())) { return true; } } if (possibleSuperTypeDecl.isInterface()) { ITypeBinding[] superInterfaces= type.getInterfaces(); for (int i= 0; i < superInterfaces.length; i++) { if (isInSuperTypeHierarchy(possibleSuperTypeDecl, superInterfaces[i].getTypeDeclaration())) { return true; } } } return false; } public IBinding[] getDeclarationsAfter(int offset, int flags) { try { org.eclipse.jdt.core.dom.NodeFinder finder= new org.eclipse.jdt.core.dom.NodeFinder(fRoot, offset, 0); ASTNode node= finder.getCoveringNode(); if (node == null) { return null; } ASTNode declaration= ASTResolving.findParentStatement(node); while (declaration instanceof Statement && declaration.getNodeType() != ASTNode.BLOCK) { declaration= declaration.getParent(); } if (declaration instanceof Block) { DefaultBindingRequestor requestor= new DefaultBindingRequestor(); DeclarationsAfterVisitor visitor= new DeclarationsAfterVisitor(node.getStartPosition(), flags, requestor); declaration.accept(visitor); List<IBinding> result= requestor.getResult(); return result.toArray(new IBinding[result.size()]); } return NO_BINDING; } finally { clearLists(); } } private class ScopeAnalyzerVisitor extends HierarchicalASTVisitor { private final int fPosition; private final int fFlags; private final IBindingRequestor fRequestor; private boolean fBreak; public ScopeAnalyzerVisitor(int position, int flags, IBindingRequestor requestor) { fPosition= position; fFlags= flags; fRequestor= requestor; fBreak= false; } private boolean isInside(ASTNode node) { int start= node.getStartPosition(); int end= start + node.getLength(); return start <= fPosition && fPosition < end; } @Override public boolean visit(MethodDeclaration node) { if (isInside(node)) { Block body= node.getBody(); if (body != null) { body.accept(this); } visitBackwards(node.parameters()); visitBackwards(node.typeParameters()); } return false; } @Override public boolean visit(TypeParameter node) { if (hasFlag(TYPES, fFlags) && node.getStartPosition() < fPosition) { fBreak= fRequestor.acceptBinding(node.getName().resolveBinding()); } return !fBreak; } @Override public boolean visit(SwitchCase node) { // switch on enum allows to use enum constants without qualification if (hasFlag(VARIABLES, fFlags) && !node.isDefault()) { if (node.getAST().isPreviewEnabled()) { List<Expression> expressions= node.expressions(); for (Expression expression : expressions) { visitExpression(node, expression); } } else { Expression expression= node.getExpression(); visitExpression(node, expression); } } return false; } private void visitExpression(SwitchCase node, Expression expression) { if (isInside(expression)) { ASTNode caseParent= node.getParent(); ITypeBinding binding= null; if (caseParent instanceof SwitchStatement) { SwitchStatement switchStatement= (SwitchStatement) caseParent; binding= switchStatement.getExpression().resolveTypeBinding(); } else if (caseParent instanceof SwitchExpression) { SwitchExpression switchExpression= (SwitchExpression) caseParent; binding= switchExpression.getExpression().resolveTypeBinding(); } if (binding != null && binding.isEnum()) { IVariableBinding[] declaredFields= binding.getDeclaredFields(); for (int i= 0; i < declaredFields.length; i++) { IVariableBinding curr= declaredFields[i]; if (curr.isEnumConstant()) { fBreak= fRequestor.acceptBinding(curr); if (fBreak) return; } } } } } @Override public boolean visit(Initializer node) { return !fBreak && isInside(node); } @Override public boolean visit(FieldDeclaration node) { return !fBreak && isInside(node); } @Override public boolean visit(Expression node) { return !fBreak && isInside(node); } @Override public boolean visit(Statement node) { return !fBreak && isInside(node); } @Override public boolean visit(ASTNode node) { return false; } @Override public boolean visit(Block node) { if (isInside(node)) { visitBackwards(node.statements()); } return false; } @Override public boolean visit(VariableDeclaration node) { if (hasFlag(VARIABLES, fFlags) && node.getStartPosition() < fPosition) { fBreak= fRequestor.acceptBinding(node.resolveBinding()); } return !fBreak; } @Override public boolean visit(VariableDeclarationStatement node) { visitBackwards(node.fragments()); return false; } @Override public boolean visit(VariableDeclarationExpression node) { visitBackwards(node.fragments()); return false; } @Override public boolean visit(CatchClause node) { if (isInside(node)) { node.getBody().accept(this); node.getException().accept(this); } return false; } @Override public boolean visit(ForStatement node) { if (isInside(node)) { node.getBody().accept(this); visitBackwards(node.initializers()); } return false; } @Override public boolean visit(TypeDeclarationStatement node) { if (hasFlag(TYPES, fFlags) && node.getStartPosition() + node.getLength() < fPosition) { fBreak= fRequestor.acceptBinding(node.resolveBinding()); return false; } return !fBreak && isInside(node); } private void visitBackwards(List<? extends ASTNode> list) { if (fBreak) return; for (int i= list.size() - 1; i >= 0; i--) { ASTNode curr= list.get(i); if (curr.getStartPosition() < fPosition) { curr.accept(this); } } } } private class DeclarationsAfterVisitor extends HierarchicalASTVisitor { private final int fPosition; private final int fFlags; private final IBindingRequestor fRequestor; private boolean fBreak; public DeclarationsAfterVisitor(int position, int flags, IBindingRequestor requestor) { fPosition= position; fFlags= flags; fRequestor= requestor; fBreak= false; } @Override public boolean visit(ASTNode node) { return !fBreak; } @Override public boolean visit(VariableDeclaration node) { if (hasFlag(VARIABLES, fFlags) && fPosition < node.getStartPosition()) { fBreak= fRequestor.acceptBinding(node.resolveBinding()); } return false; } @Override public boolean visit(AnonymousClassDeclaration node) { return false; } @Override public boolean visit(TypeDeclarationStatement node) { if (hasFlag(TYPES, fFlags) && fPosition < node.getStartPosition()) { fBreak= fRequestor.acceptBinding(node.resolveBinding()); } return false; } } private boolean addLocalDeclarations(ASTNode node, int flags, IBindingRequestor requestor) { return addLocalDeclarations(node, node.getStartPosition(), flags, requestor); } private boolean addLocalDeclarations(ASTNode node, int offset, int flags, IBindingRequestor requestor) { if (hasFlag(VARIABLES, flags) || hasFlag(TYPES, flags)) { BodyDeclaration declaration= ASTResolving.findParentBodyDeclaration(node); if (declaration instanceof MethodDeclaration || declaration instanceof Initializer || declaration instanceof FieldDeclaration) { ScopeAnalyzerVisitor visitor= new ScopeAnalyzerVisitor(offset, flags, requestor); declaration.accept(visitor); return visitor.fBreak; } } return false; } public Collection<String> getUsedVariableNames(int offset, int length) { HashSet<String> result= new HashSet<>(); IBinding[] bindingsBefore= getDeclarationsInScope(offset, VARIABLES | CHECK_VISIBILITY); for (int i= 0; i < bindingsBefore.length; i++) { result.add(bindingsBefore[i].getName()); } IBinding[] bindingsAfter= getDeclarationsAfter(offset + length, VARIABLES | CHECK_VISIBILITY); for (int i= 0; i < bindingsAfter.length; i++) { result.add(bindingsAfter[i].getName()); } List<ImportDeclaration> imports= fRoot.imports(); for (int i= 0; i < imports.size(); i++) { ImportDeclaration decl= imports.get(i); if (decl.isStatic() && !decl.isOnDemand()) { result.add(ASTNodes.getSimpleNameIdentifier(decl.getName())); } } return result; } }