diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index 8bbc7567cbdc..c72c5fcceb5d 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -206,7 +206,7 @@ public abstract class QuickFixFactory { @NotNull public abstract IntentionAction createDeferFinalAssignmentFix(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression); - @NotNull public abstract IntentionAction createVariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiClass aClass); + @NotNull public abstract IntentionAction createVariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiElement scope); @NotNull public abstract IntentionAction createCreateConstructorParameterFromFieldFix(@NotNull PsiField field); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java index 73603037a187..40463be3d6e1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightControlFlowUtil.java @@ -590,7 +590,7 @@ public class HighlightControlFlowUtil { JavaErrorMessages.message("assignment.to.final.variable", name); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(reference.getTextRange()).descriptionAndTooltip(description).create(); - final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, expression); + final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression); if (innerClass == null || variable instanceof PsiField) { QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(variable, PsiModifier.FINAL, false, false)); @@ -607,7 +607,7 @@ public class HighlightControlFlowUtil { private static boolean canWriteToFinal(PsiVariable variable, PsiExpression expression, final PsiReferenceExpression reference, @NotNull PsiFile containingFile) { if (variable.hasInitializer()) return false; if (variable instanceof PsiParameter) return false; - PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, expression); + PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, expression); if (variable instanceof PsiField) { // if inside some field initializer if (HighlightUtil.findEnclosingFieldInitializer(expression) != null) return true; @@ -642,8 +642,8 @@ public class HighlightControlFlowUtil { @NotNull PsiJavaCodeReferenceElement context, @NotNull LanguageLevel languageLevel) { if (variable.hasModifierProperty(PsiModifier.FINAL)) return null; - final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, context); - if (innerClass != null) { + final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context); + if (innerClass instanceof PsiClass) { if (variable instanceof PsiParameter) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression && @@ -672,8 +672,12 @@ public class HighlightControlFlowUtil { return null; } if (!isEffectivelyFinal(variable, lambdaExpression, context)) { - return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(context).descriptionAndTooltip( - "Variable used in lambda expression should be effectively final").create(); + final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) + .range(context) + .descriptionAndTooltip("Variable used in lambda expression should be effectively final") + .create(); + QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, lambdaExpression)); + return highlightInfo; } } return null; @@ -716,7 +720,7 @@ public class HighlightControlFlowUtil { } @Nullable - public static PsiClass getInnerClassVariableReferencedFrom(@NotNull PsiVariable variable, @NotNull PsiElement context) { + public static PsiElement getInnerClassVariableReferencedFrom(@NotNull PsiVariable variable, @NotNull PsiElement context) { final PsiElement[] scope; if (variable instanceof PsiResourceVariable) { scope = ((PsiResourceVariable)variable).getDeclarationScope(); @@ -741,7 +745,10 @@ public class HighlightControlFlowUtil { if (parent.equals(scopeElement)) break outer; } if (parent instanceof PsiClass && !(prevParent instanceof PsiExpressionList && parent instanceof PsiAnonymousClass)) { - return (PsiClass)parent; + return parent; + } + if (parent instanceof PsiLambdaExpression) { + return parent; } prevParent = parent; parent = parent.getParent(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java index 98a271629093..47ed78e48760 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/VariableAccessFromInnerClassFix.java @@ -42,16 +42,16 @@ import java.util.*; public class VariableAccessFromInnerClassFix implements IntentionAction { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.VariableAccessFromInnerClassFix"); private final PsiVariable myVariable; - private final PsiClass myClass; + private final PsiElement myContext; private final int myFixType; private static final int MAKE_FINAL = 0; private static final int MAKE_ARRAY = 1; private static final int COPY_TO_FINAL = 2; private static final Key>[] VARS = new Key[] {Key.create("VARS_TO_MAKE_FINAL"), Key.create("VARS_TO_TRANSFORM"), Key.create("???")}; - public VariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiClass aClass) { + public VariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiElement element) { myVariable = variable; - myClass = aClass; + myContext = element; myFixType = getQuickFixType(variable); if (myFixType == -1) return; @@ -87,21 +87,21 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return myClass.isValid() && - myClass.getManager().isInProject(myClass) && + return myContext.isValid() && + myContext.getManager().isInProject(myContext) && myVariable.isValid() && myFixType != -1 && !getVariablesToFix().isEmpty() && - !inOwnInitializer(myVariable, myClass); + !inOwnInitializer(myVariable, myContext); } - private static boolean inOwnInitializer(PsiVariable variable, PsiClass aClass) { - return PsiTreeUtil.isAncestor(variable, aClass, false); + private static boolean inOwnInitializer(PsiVariable variable, PsiElement context) { + return PsiTreeUtil.isAncestor(variable, context, false); } @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) { - if (!FileModificationService.getInstance().preparePsiElementsForWrite(myClass, myVariable)) return; + if (!FileModificationService.getInstance().preparePsiElementsForWrite(myContext, myVariable)) return; try { switch (myFixType) { case MAKE_FINAL: @@ -131,8 +131,8 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { @NotNull private Collection getVariablesToFix() { - Map vars = myClass.getUserData(VARS[myFixType]); - if (vars == null) myClass.putUserData(VARS[myFixType], vars = new ConcurrentWeakHashMap(1)); + Map vars = myContext.getUserData(VARS[myFixType]); + if (vars == null) myContext.putUserData(VARS[myFixType], vars = new ConcurrentWeakHashMap(1)); final Map finalVars = vars; return new AbstractCollection() { @Override @@ -164,7 +164,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { private void makeArray(PsiVariable variable) throws IncorrectOperationException { PsiType type = variable.getType(); - PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory(); + PsiElementFactory factory = JavaPsiFacade.getInstance(myContext.getProject()).getElementFactory(); PsiType newType = type.createArrayType(); PsiDeclarationStatement variableDeclarationStatement; @@ -195,9 +195,9 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { } private void copyToFinal() throws IncorrectOperationException { - PsiManager psiManager = myClass.getManager(); + PsiManager psiManager = myContext.getManager(); PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory(); - PsiExpression initializer = factory.createExpressionFromText(myVariable.getName(), myClass); + PsiExpression initializer = factory.createExpressionFromText(myVariable.getName(), myContext); String newName = suggestNewName(psiManager.getProject(), myVariable); PsiType type = myVariable.getType(); PsiDeclarationStatement copyDecl = factory.createVariableDeclarationStatement(newName, type, initializer); @@ -206,7 +206,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { PsiElement statement = getStatementToInsertBefore(); if (statement == null) return; PsiExpression newExpression = factory.createExpressionFromText(newName, myVariable); - replaceReferences(myClass, myVariable, newExpression); + replaceReferences(myContext, myVariable, newExpression); if (RefactoringUtil.isLoopOrIf(statement.getParent())) { RefactoringUtil.putStatementInLoopBody(copyDecl, statement.getParent(), statement); } else { @@ -219,7 +219,7 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { ? ((PsiParameter)myVariable).getDeclarationScope() : PsiUtil.getVariableCodeBlock(myVariable, null); if (declarationScope == null) return null; - PsiElement statement = myClass; + PsiElement statement = myContext; nextInnerClass: do { statement = RefactoringUtil.getParentStatement(statement, false); @@ -291,11 +291,11 @@ public class VariableAccessFromInnerClassFix implements IntentionAction { int type = MAKE_FINAL; for (PsiReferenceExpression expression : outerReferences) { // if it happens that variable referenced from another inner class, make sure it can be make final from there - PsiClass innerClass = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression); + PsiElement innerScope = HighlightControlFlowUtil.getInnerClassVariableReferencedFrom(variable, expression); - if (innerClass != null) { + if (innerScope != null) { int thisType = MAKE_FINAL; - if (writtenInside(variable, innerClass)) { + if (writtenInside(variable, innerScope)) { // cannot make parameter array if (variable instanceof PsiParameter) return -1; thisType = MAKE_ARRAY; diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 5f95da053db3..c8a6cfa0271f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -547,8 +547,8 @@ public class QuickFixFactoryImpl extends QuickFixFactory { @NotNull @Override - public IntentionAction createVariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiClass aClass) { - return new VariableAccessFromInnerClassFix(variable, aClass); + public IntentionAction createVariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiElement scope) { + return new VariableAccessFromInnerClassFix(variable, scope); } @NotNull diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/afterInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/afterInsideLambda.java new file mode 100644 index 000000000000..8754e8e4ebbe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/afterInsideLambda.java @@ -0,0 +1,11 @@ +// "Copy 'a' to temp final variable" "true" +class Test { + public void test() { + int a = 1; + a = 2; + final int finalA = a; + Runnable r = () -> { + System.out.println(finalA); + }; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/beforeInsideLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/beforeInsideLambda.java new file mode 100644 index 000000000000..7dabe1912e37 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18/beforeInsideLambda.java @@ -0,0 +1,10 @@ +// "Copy 'a' to temp final variable" "true" +class Test { + public void test() { + int a = 1; + a = 2; + Runnable r = () -> { + System.out.println(a); + }; + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClass18Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClass18Test.java index 84201d73e69b..dfdf74b15a33 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClass18Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClass18Test.java @@ -15,8 +15,6 @@ */ package com.intellij.codeInsight.daemon.quickFix; -import com.intellij.openapi.command.WriteCommandAction; - /** * User: anna */ @@ -29,21 +27,4 @@ public class VariableAccessFromInnerClass18Test extends LightQuickFixParameteriz protected String getBasePath() { return "/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal18"; } - - @Override - protected void beforeActionStarted(String testName, String contents) { - for (int i=0;i<10;i++) { - WriteCommandAction.runWriteCommandAction(null, new Runnable() { - @Override - public void run() { - myEditor.getDocument().insertString(myEditor.getCaretModel().getOffset(), "//"); - } - }); - - doHighlighting(); - delete(); - delete(); - doHighlighting(); - } - } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClassTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClassTest.java index 98b8a7432baf..e5bd455a9dd9 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClassTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/VariableAccessFromInnerClassTest.java @@ -3,8 +3,12 @@ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.pom.java.LanguageLevel; -public class VariableAccessFromInnerClassTest extends VariableAccessFromInnerClass18Test { +public class VariableAccessFromInnerClassTest extends LightQuickFixParameterizedTestCase { + public void test() throws Exception { + doAllTests(); + } + @Override protected String getBasePath() { return "/codeInsight/daemonCodeAnalyzer/quickFix/mustBeFinal";