Files
openide/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java
Anna Kozlova abc4a9ae72 no need in overriding getDisplayName for inspections
in production InspectionEP#displayName should be used; highlighting tests just do not use default names

GitOrigin-RevId: f0c172e568219499e7b96982fe352c7d1d3acc69
2019-12-11 08:03:54 +00:00

594 lines
26 KiB
Java

// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.controlFlow.AnalysisCanceledException;
import com.intellij.psi.controlFlow.ControlFlow;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.util.*;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.LinkedHashMap;
import com.intellij.util.text.UniqueNameGenerator;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.*;
import java.util.function.UnaryOperator;
import static com.intellij.codeInsight.AnnotationUtil.CHECK_EXTERNAL;
public class AnonymousCanBeLambdaInspection extends AbstractBaseJavaLocalInspectionTool {
public static final Logger LOG = Logger.getInstance(AnonymousCanBeLambdaInspection.class);
public boolean reportNotAnnotatedInterfaces = true;
@Nls
@NotNull
@Override
public String getGroupDisplayName() {
return GroupNames.LANGUAGE_LEVEL_SPECIFIC_GROUP_NAME;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@NotNull
@Override
public String getShortName() {
return "Convert2Lambda";
}
@Nullable
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel("Report when interface is not annotated with @FunctionalInterface", this, "reportNotAnnotatedInterfaces");
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitAnonymousClass(final PsiAnonymousClass aClass) {
super.visitAnonymousClass(aClass);
final PsiElement parent = aClass.getParent();
if (canBeConvertedToLambda(aClass, false, isOnTheFly || reportNotAnnotatedInterfaces, Collections.emptySet())) {
final PsiElement lBrace = aClass.getLBrace();
LOG.assertTrue(lBrace != null);
final TextRange rangeInElement = new TextRange(0, aClass.getStartOffsetInParent() + lBrace.getStartOffsetInParent());
ProblemHighlightType type = ProblemHighlightType.LIKE_UNUSED_SYMBOL;
if (isOnTheFly && !reportNotAnnotatedInterfaces) {
final PsiClass baseClass = aClass.getBaseClassType().resolve();
LOG.assertTrue(baseClass != null);
if (!AnnotationUtil.isAnnotated(baseClass, CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE, CHECK_EXTERNAL)) {
type = ProblemHighlightType.INFORMATION;
}
}
holder.registerProblem(parent, "Anonymous #ref #loc can be replaced with lambda",
type, rangeInElement, new ReplaceWithLambdaFix());
}
}
};
}
public static boolean hasRuntimeAnnotations(PsiModifierListOwner listOwner, @NotNull Set<String> runtimeAnnotationsToIgnore) {
PsiModifierList modifierList = listOwner.getModifierList();
if (modifierList == null) return false;
PsiAnnotation[] annotations = modifierList.getAnnotations();
for (PsiAnnotation annotation : annotations) {
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
PsiElement target = ref != null ? ref.resolve() : null;
if (target instanceof PsiClass) {
if (runtimeAnnotationsToIgnore.contains(((PsiClass)target).getQualifiedName())) {
continue;
}
final PsiAnnotation retentionAnno = AnnotationUtil.findAnnotation((PsiClass)target, Retention.class.getName());
if (retentionAnno != null) {
PsiAnnotationMemberValue value = retentionAnno.findAttributeValue("value");
if (value instanceof PsiReferenceExpression) {
final PsiElement resolved = ((PsiReferenceExpression)value).resolve();
if (resolved instanceof PsiField && RetentionPolicy.RUNTIME.name().equals(((PsiField)resolved).getName())) {
final PsiClass containingClass = ((PsiField)resolved).getContainingClass();
if (containingClass != null && RetentionPolicy.class.getName().equals(containingClass.getQualifiedName())) {
return true;
}
}
}
}
}
}
return false;
}
public static boolean hasForbiddenRefsInsideBody(PsiMethod method, PsiAnonymousClass aClass) {
final ForbiddenRefsChecker checker = new ForbiddenRefsChecker(method, aClass);
final PsiCodeBlock body = method.getBody();
LOG.assertTrue(body != null);
body.accept(checker);
return checker.hasForbiddenRefs();
}
private static PsiType getInferredType(PsiAnonymousClass aClass, PsiMethod method) {
final PsiExpression expression = (PsiExpression)aClass.getParent();
final PsiType psiType = PsiTypesUtil.getExpectedTypeByParent(expression);
if (psiType != null) {
return psiType;
}
PsiExpression topExpr = expression;
while (topExpr.getParent() instanceof PsiParenthesizedExpression) {
topExpr = (PsiExpression)topExpr.getParent();
}
final PsiCall call = LambdaUtil.treeWalkUp(topExpr);
if (call != null && call.resolveMethod() != null) {
Object marker = new Object();
PsiTreeUtil.mark(aClass, marker);
PsiCall copyCall = LambdaUtil.copyTopLevelCall(call);
if (copyCall == null) return null;
final PsiElement classArg = PsiTreeUtil.releaseMark(copyCall, marker);
if (classArg instanceof PsiAnonymousClass) {
PsiExpression lambda = JavaPsiFacade.getElementFactory(aClass.getProject())
.createExpressionFromText(ReplaceWithLambdaFix.composeLambdaText(method), expression);
lambda = (PsiExpression)classArg.getParent().replace(lambda);
((PsiLambdaExpression)lambda).getBody().replace(method.getBody());
final PsiType interfaceType;
if (copyCall.resolveMethod() == null) {
return PsiType.NULL;
}
else {
interfaceType = ((PsiLambdaExpression)lambda).getFunctionalInterfaceType();
}
return interfaceType;
}
}
return PsiType.NULL;
}
public static boolean canBeConvertedToLambda(PsiAnonymousClass aClass,
boolean acceptParameterizedFunctionTypes,
@NotNull Set<String> ignoredRuntimeAnnotations) {
return canBeConvertedToLambda(aClass, acceptParameterizedFunctionTypes, true, ignoredRuntimeAnnotations);
}
public static boolean isLambdaForm(PsiAnonymousClass aClass, Set<String> ignoredRuntimeAnnotations) {
PsiMethod[] methods = aClass.getMethods();
if(methods.length != 1) return false;
PsiMethod method = methods[0];
return aClass.getFields().length == 0 &&
aClass.getInnerClasses().length == 0 &&
aClass.getInitializers().length == 0 &&
method.getBody() != null &&
method.getDocComment() == null &&
!hasRuntimeAnnotations(method, ignoredRuntimeAnnotations) &&
!method.hasModifierProperty(PsiModifier.SYNCHRONIZED) &&
!method.hasModifierProperty(PsiModifier.STRICTFP) &&
!hasForbiddenRefsInsideBody(method, aClass);
}
public static boolean canBeConvertedToLambda(PsiAnonymousClass aClass,
boolean acceptParameterizedFunctionTypes,
boolean reportNotAnnotatedInterfaces,
@NotNull Set<String> ignoredRuntimeAnnotations) {
PsiElement parent = aClass.getParent();
final PsiElement lambdaContext = parent != null ? PsiUtil.skipParenthesizedExprUp(parent.getParent()) : null;
if (lambdaContext == null || !LambdaUtil.isValidLambdaContext(lambdaContext) && !(lambdaContext instanceof PsiReferenceExpression)) return false;
return isLambdaForm(aClass, acceptParameterizedFunctionTypes, reportNotAnnotatedInterfaces, ignoredRuntimeAnnotations);
}
public static boolean isLambdaForm(PsiAnonymousClass aClass,
boolean acceptParameterizedFunctionTypes,
@NotNull Set<String> ignoredRuntimeAnnotations) {
return isLambdaForm(aClass, acceptParameterizedFunctionTypes, true, ignoredRuntimeAnnotations);
}
public static boolean isLambdaForm(PsiAnonymousClass aClass,
boolean acceptParameterizedFunctionTypes,
boolean reportNotAnnotatedInterfaces,
@NotNull Set<String> ignoredRuntimeAnnotations) {
if (PsiUtil.getLanguageLevel(aClass).isAtLeast(LanguageLevel.JDK_1_8)) {
final PsiClassType baseClassType = aClass.getBaseClassType();
final PsiClassType.ClassResolveResult resolveResult = baseClassType.resolveGenerics();
final PsiClass baseClass = resolveResult.getElement();
if (baseClass == null ||
!reportNotAnnotatedInterfaces && !AnnotationUtil.isAnnotated(baseClass, CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE, CHECK_EXTERNAL)) {
return false;
}
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
if (interfaceMethod != null && (acceptParameterizedFunctionTypes || !interfaceMethod.hasTypeParameters())) {
if (isLambdaForm(aClass, ignoredRuntimeAnnotations)) {
final PsiMethod method = aClass.getMethods()[0];
return getInferredType(aClass, method) != null;
}
}
}
return false;
}
public static PsiExpression replaceAnonymousWithLambda(@NotNull PsiElement anonymousClass, PsiType expectedType) {
PsiNewExpression newArrayExpression = (PsiNewExpression)JavaPsiFacade.getElementFactory(anonymousClass.getProject())
.createExpressionFromText("new " + expectedType.getCanonicalText() + "[]{" + anonymousClass.getText() + "}", anonymousClass);
PsiArrayInitializerExpression initializer = newArrayExpression.getArrayInitializer();
LOG.assertTrue(initializer != null);
return replacePsiElementWithLambda(initializer.getInitializers()[0], true, false);
}
public static PsiExpression replacePsiElementWithLambda(@NotNull PsiElement element,
final boolean ignoreEqualsMethod,
boolean forceIgnoreTypeCast) {
if (!(element instanceof PsiNewExpression)) {
return null;
}
final PsiNewExpression newExpression = (PsiNewExpression)element;
final PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass();
if (anonymousClass == null) return null;
final PsiMethod method;
if (ignoreEqualsMethod) {
final List<PsiMethod> methods = ContainerUtil.filter(anonymousClass.getMethods(), method1 -> !"equals".equals(method1.getName()));
method = methods.get(0);
} else {
method = anonymousClass.getMethods()[0];
}
if (method == null || method.getBody() == null) return null;
return generateLambdaByMethod(anonymousClass, method, lambda -> (PsiLambdaExpression)newExpression.replace(lambda),
forceIgnoreTypeCast);
}
/**
* Try convert given method of given anonymous class into lambda and replace given element.
*
* @param anonymousClass physical anonymous class containing method
* @param method physical method to convert with non-empty body
* @param replacer an operator which actually inserts a lambda into the file (possibly removing anonymous class)
* and returns an inserted physical lambda
* @param forceIgnoreTypeCast if false, type cast might be added if necessary
* @return newly-generated lambda expression (possibly with typecast)
*/
@NotNull
static PsiExpression generateLambdaByMethod(PsiAnonymousClass anonymousClass,
PsiMethod method,
UnaryOperator<PsiLambdaExpression> replacer,
boolean forceIgnoreTypeCast) {
ChangeContextUtil.encodeContextInfo(anonymousClass, true);
final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText();
final PsiCodeBlock body = method.getBody();
LOG.assertTrue(body != null);
final Collection<PsiComment> comments = collectCommentsOutsideMethodBody(anonymousClass.getParent(), body);
final Project project = anonymousClass.getProject();
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
final String withoutTypesDeclared = ReplaceWithLambdaFix.composeLambdaText(method);
PsiLambdaExpression lambdaExpression =
(PsiLambdaExpression)elementFactory.createExpressionFromText(withoutTypesDeclared, anonymousClass);
PsiElement lambdaBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaBody != null);
lambdaBody.replace(body);
lambdaExpression = replacer.apply(lambdaExpression);
final Set<PsiVariable> variables = new HashSet<>();
final Set<String> usedLocalNames = new HashSet<>();
collectLocalVariablesDefinedInsideLambda(lambdaExpression, variables, usedLocalNames);
ReplaceWithLambdaFix
.giveUniqueNames(project, elementFactory, lambdaExpression,
usedLocalNames, variables.toArray(new PsiVariable[0]));
final PsiExpression singleExpr = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression.getBody());
if (singleExpr != null) {
lambdaExpression.getBody().replace(singleExpr);
}
ChangeContextUtil.decodeContextInfo(lambdaExpression, null, null);
restoreComments(comments, lambdaExpression);
final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
if (forceIgnoreTypeCast) {
return (PsiExpression)javaCodeStyleManager.shortenClassReferences(lambdaExpression);
}
PsiTypeCastExpression typeCast = (PsiTypeCastExpression)elementFactory
.createExpressionFromText("(" + canonicalText + ")" + withoutTypesDeclared, lambdaExpression);
final PsiExpression typeCastOperand = typeCast.getOperand();
LOG.assertTrue(typeCastOperand instanceof PsiLambdaExpression);
final PsiElement fromText = ((PsiLambdaExpression)typeCastOperand).getBody();
LOG.assertTrue(fromText != null);
lambdaBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaBody != null);
fromText.replace(lambdaBody);
((PsiLambdaExpression)typeCastOperand).getParameterList().replace(lambdaExpression.getParameterList());
typeCast = (PsiTypeCastExpression)lambdaExpression.replace(typeCast);
if (RedundantCastUtil.isCastRedundant(typeCast)) {
final PsiExpression operand = typeCast.getOperand();
LOG.assertTrue(operand != null);
return (PsiExpression)typeCast.replace(operand);
}
return (PsiExpression)javaCodeStyleManager.shortenClassReferences(typeCast);
}
@NotNull
static Collection<PsiComment> collectCommentsOutsideMethodBody(PsiElement anonymousClass, PsiCodeBlock body) {
final Collection<PsiComment> psiComments = PsiTreeUtil.findChildrenOfType(anonymousClass, PsiComment.class);
psiComments.removeIf(comment -> PsiTreeUtil.isAncestor(body, comment, false));
return ContainerUtil.map(psiComments, (comment) -> (PsiComment)comment.copy());
}
private static void collectLocalVariablesDefinedInsideLambda(PsiLambdaExpression lambdaExpression,
final Set<PsiVariable> variables,
Set<? super String> namesOfVariablesInTheBlock) {
PsiElement block = PsiUtil.getTopLevelEnclosingCodeBlock(lambdaExpression, null);
if (block == null) {
block = lambdaExpression;
}
block.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
if (!(variable instanceof PsiField)) {
variables.add(variable);
}
}
});
final PsiResolveHelper helper = PsiResolveHelper.SERVICE.getInstance(lambdaExpression.getProject());
for (Iterator<PsiVariable> iterator = variables.iterator(); iterator.hasNext(); ) {
PsiVariable local = iterator.next();
final String localName = local.getName();
if (localName == null ||
shadowingResolve(localName, lambdaExpression, helper) ||
!PsiTreeUtil.isAncestor(lambdaExpression, local, false)) {
iterator.remove();
namesOfVariablesInTheBlock.add(localName);
}
}
}
private static boolean shadowingResolve(String localName, PsiLambdaExpression lambdaExpression, PsiResolveHelper helper) {
final PsiVariable variable = helper.resolveReferencedVariable(localName, lambdaExpression);
return variable == null || variable instanceof PsiField;
}
private static class ReplaceWithLambdaFix implements LocalQuickFix, HighPriorityAction {
@NotNull
@Override
public String getFamilyName() {
return "Replace with lambda";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element != null) {
replacePsiElementWithLambda(element, false, false);
}
}
private static void giveUniqueNames(Project project,
final PsiElementFactory elementFactory,
PsiElement body,
Set<String> usedLocalNames, PsiVariable[] parameters) {
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
final Map<PsiVariable, String> names = new HashMap<>();
for (PsiVariable parameter : parameters) {
String parameterName = parameter.getName();
String uniqueVariableName = UniqueNameGenerator.generateUniqueName(codeStyleManager.suggestUniqueVariableName(parameterName, parameter.getParent(), false), usedLocalNames);
if (!Comparing.equal(parameterName, uniqueVariableName)) {
names.put(parameter, uniqueVariableName);
}
}
if (names.isEmpty()) return;
final LinkedHashMap<PsiElement, PsiElement> replacements = new LinkedHashMap<>();
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
final String newName = names.get(variable);
if (newName != null) {
replacements.put(variable.getNameIdentifier(), elementFactory.createIdentifier(newName));
}
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolve = expression.resolve();
if (resolve instanceof PsiVariable) {
final String newName = names.get(resolve);
if (newName != null) {
replacements.put(expression, elementFactory.createExpressionFromText(newName, expression));
}
}
}
});
for (PsiElement psiElement : replacements.keySet()) {
psiElement.replace(replacements.get(psiElement));
}
}
private static String composeLambdaText(PsiMethod method) {
final StringBuilder buf = new StringBuilder();
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 1) {
buf.append("(");
}
buf.append(StringUtil.join(parameters, ReplaceWithLambdaFix::composeParameter, ","));
if (parameters.length != 1) {
buf.append(")");
}
buf.append("-> {}");
return buf.toString();
}
private static String composeParameter(PsiParameter parameter) {
return parameter.getName();
}
}
public static boolean functionalInterfaceMethodReferenced(PsiMethod psiMethod,
PsiAnonymousClass anonymClass,
PsiCallExpression callExpression) {
if (psiMethod != null && !psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
final PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass != null &&
CommonClassNames.JAVA_LANG_OBJECT.equals(containingClass.getQualifiedName())) {
return !(callExpression instanceof PsiMethodCallExpression && ((PsiMethodCallExpression)callExpression).getMethodExpression().isQualified());
}
if (callExpression instanceof PsiMethodCallExpression &&
((PsiMethodCallExpression)callExpression).getMethodExpression().isQualified()) {
return false;
}
if (InheritanceUtil.isInheritorOrSelf(anonymClass, containingClass, true) &&
!InheritanceUtil.hasEnclosingInstanceInScope(containingClass, anonymClass.getParent(), true, true)) {
return true;
}
}
return false;
}
public static void restoreComments(Collection<? extends PsiComment> comments, PsiElement lambda) {
PsiElement anchor = PsiTreeUtil.getParentOfType(lambda, PsiStatement.class, PsiField.class);
if (anchor == null) {
anchor = lambda;
}
for (PsiComment comment : comments) {
anchor.getParent().addBefore(comment, anchor);
}
}
private static class ForbiddenRefsChecker extends JavaRecursiveElementWalkingVisitor {
private boolean myBodyContainsForbiddenRefs;
private final PsiMethod myMethod;
private final PsiAnonymousClass myAnonymClass;
ForbiddenRefsChecker(PsiMethod method, PsiAnonymousClass aClass) {
myMethod = method;
myAnonymClass = aClass;
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression methodCallExpression) {
if (myBodyContainsForbiddenRefs) return;
super.visitMethodCallExpression(methodCallExpression);
final PsiMethod psiMethod = methodCallExpression.resolveMethod();
if (psiMethod == myMethod ||
functionalInterfaceMethodReferenced(psiMethod, myAnonymClass, methodCallExpression)) {
myBodyContainsForbiddenRefs = true;
}
}
@Override
public void visitThisExpression(PsiThisExpression expression) {
if (myBodyContainsForbiddenRefs) return;
if (expression.getQualifier() == null) {
myBodyContainsForbiddenRefs = true;
}
}
@Override
public void visitSuperExpression(PsiSuperExpression expression) {
if (myBodyContainsForbiddenRefs) return;
if (expression.getQualifier() == null) {
myBodyContainsForbiddenRefs = true;
}
}
@Override
public void visitVariable(PsiVariable variable) {
if (myBodyContainsForbiddenRefs) return;
super.visitVariable(variable);
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
if (myBodyContainsForbiddenRefs) return;
super.visitReferenceExpression(expression);
if (!(expression.getParent() instanceof PsiMethodCallExpression)) {
final PsiMember member = PsiTreeUtil.getParentOfType(myAnonymClass, PsiMember.class);
if (member instanceof PsiField || member instanceof PsiClassInitializer) {
final PsiElement resolved = expression.resolve();
final PsiClass memberContainingClass = member.getContainingClass();
if (resolved instanceof PsiField &&
memberContainingClass != null &&
PsiTreeUtil.isAncestor(((PsiField)resolved).getContainingClass(), memberContainingClass, false) &&
expression.getQualifierExpression() == null) {
final PsiExpression initializer = ((PsiField)resolved).getInitializer();
if (initializer == null ||
resolved == member ||
initializer.getTextOffset() > myAnonymClass.getTextOffset() && ((PsiField)resolved).hasModifierProperty(PsiModifier.STATIC) == member.hasModifierProperty(PsiModifier.STATIC)) {
myBodyContainsForbiddenRefs = true;
}
}
} else {
final PsiMethod method = PsiTreeUtil.getParentOfType(myAnonymClass, PsiMethod.class);
if (method != null && method.isConstructor()) {
final PsiElement resolved = expression.resolve();
if (resolved instanceof PsiField &&
((PsiField)resolved).hasModifierProperty(PsiModifier.FINAL) &&
((PsiField)resolved).getInitializer() == null &&
((PsiField)resolved).getContainingClass() == method.getContainingClass()) {
try {
final PsiCodeBlock constructorBody = method.getBody();
if (constructorBody != null) {
final ControlFlow flow = HighlightControlFlowUtil.getControlFlowNoConstantEvaluate(constructorBody);
final int startOffset = flow.getStartOffset(myAnonymClass);
final Collection<PsiVariable> writtenVariables = ControlFlowUtil.getWrittenVariables(flow, 0, startOffset, false);
if (!writtenVariables.contains(resolved)) {
myBodyContainsForbiddenRefs = true;
}
}
}
catch (AnalysisCanceledException e) {
myBodyContainsForbiddenRefs = true;
}
}
}
}
}
}
public boolean hasForbiddenRefs() {
return myBodyContainsForbiddenRefs;
}
}
}