blob: ddecc5f6673b1374264424c818852f067b8dc630 [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.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)
*/
public class ScopeAnalyzer {
private static final IBinding[] NO_BINDING= new IBinding[0];
/**
* Flag to specify that method should be reported.
*/
public static final int METHODS= 1;
/**
* Flag to specify that variables should be reported.
*/
public static final int VARIABLES= 2;
/**
* Flag to specify that types should be reported.
*/
public static final int TYPES= 4;
/**
* 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.
*/
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
* @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.
* @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.
* @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;
}
}