final Set<Element> checked = new HashSet<Element>();
final List<Element> toCheck = new ArrayList<Element>();
toCheck.add(target);
boolean firstIteration = true;
while (true) {
Element current;
// get next element
while (true) {
// may be no more elements to check
if (toCheck.isEmpty()) {
return false;
}
// try to get next element
current = toCheck.remove(toCheck.size() - 1);
if (target.equals(current)) {
if (firstIteration) {
firstIteration = false;
break;
} else {
return true;
}
}
if (current != null && !checked.contains(current)) {
break;
}
}
// check current element
current.accept(new GeneralizingElementVisitor<Void>() {
private boolean inClass;
@Override
public Void visitClassElement(ClassElement element) {
addTypeToCheck(element.getSupertype());
for (InterfaceType mixin : element.getMixins()) {
addTypeToCheck(mixin);
}
inClass = !element.isTypedef();
try {
return super.visitClassElement(element);
} finally {
inClass = false;
}
}
@Override
public Void visitExecutableElement(ExecutableElement element) {
if (element.isSynthetic()) {
return null;
}
addTypeToCheck(element.getReturnType());
return super.visitExecutableElement(element);
}
@Override
public Void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
addTypeToCheck(element.getReturnType());
return super.visitFunctionTypeAliasElement(element);
}
@Override
public Void visitParameterElement(ParameterElement element) {
addTypeToCheck(element.getType());
return super.visitParameterElement(element);
}
@Override
public Void visitTypeParameterElement(TypeParameterElement element) {
addTypeToCheck(element.getBound());
return super.visitTypeParameterElement(element);
}
@Override
public Void visitVariableElement(VariableElement element) {
addTypeToCheck(element.getType());
return super.visitVariableElement(element);
}
private void addTypeToCheck(Type type) {
if (type == null) {
return;
}
Element element = type.getElement();
// it is OK to reference target from class
if (inClass && target.equals(element)) {
return;
}
// schedule for checking