diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalAnnotator.java b/java/java-analysis-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalAnnotator.java index 82d75edcde45..a002e50bdb3f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalAnnotator.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalAnnotator.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; import com.intellij.codeInspection.reference.*; import com.intellij.psi.*; import com.intellij.psi.controlFlow.*; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -99,17 +100,22 @@ class CanBeFinalAnnotator extends RefGraphAnnotatorEx { RefElement refFrom, boolean referencedFromClassInitializer, boolean forReading, - boolean forWriting) { + boolean forWriting, + PsiElement referenceElement) { if (!(refWhat instanceof RefField)) return; if (!(refFrom instanceof RefMethod) || !((RefMethod)refFrom).isConstructor() || ((PsiField)refWhat.getElement()).hasInitializer() || ((RefMethod)refFrom).getOwnerClass() != ((RefField)refWhat).getOwnerClass() || ((RefField)refWhat).isStatic()) { - if (!referencedFromClassInitializer && forWriting) { + if (forWriting && + !(referencedFromClassInitializer && PsiTreeUtil.getParentOfType(referenceElement, PsiLambdaExpression.class, true) == null)) { ((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK); } } + else if (forWriting && PsiTreeUtil.getParentOfType(referenceElement, PsiLambdaExpression.class, true) != null) { + ((RefFieldImpl)refWhat).setFlag(false, CAN_BE_FINAL_MASK); + } } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefFieldImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefFieldImpl.java index efab8d209ff6..2116b4e7b51b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefFieldImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefFieldImpl.java @@ -76,7 +76,7 @@ public class RefFieldImpl extends RefJavaElementImpl implements RefField { } setUsedQualifiedOutsidePackageFlag(refFrom, expressionFrom); - getRefManager().fireNodeMarkedReferenced(this, refFrom, referencedFromClassInitializer, forReading, forWriting); + getRefManager().fireNodeMarkedReferenced(this, refFrom, referencedFromClassInitializer, forReading, forWriting, expressionFrom); } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java index 28208062735a..9d543e39c613 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaElementImpl.java @@ -260,7 +260,7 @@ public abstract class RefJavaElementImpl extends RefElementImpl implements RefJa protected void markReferenced(final RefElementImpl refFrom, PsiElement psiFrom, PsiElement psiWhat, final boolean forWriting, boolean forReading, PsiReferenceExpression expressionFrom) { addInReference(refFrom); setUsedQualifiedOutsidePackageFlag(refFrom, expressionFrom); - getRefManager().fireNodeMarkedReferenced(this, refFrom, false, forReading, forWriting); + getRefManager().fireNodeMarkedReferenced(this, refFrom, false, forReading, forWriting, expressionFrom); } void setUsedQualifiedOutsidePackageFlag(RefElementImpl refFrom, PsiReferenceExpression expressionFrom) { diff --git a/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/expected.xml b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/expected.xml new file mode 100644 index 000000000000..9ac879d78616 --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/expected.xml @@ -0,0 +1,3 @@ + + + diff --git a/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/src/Foo.java b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/src/Foo.java new file mode 100644 index 000000000000..0617acd68c1b --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambda/src/Foo.java @@ -0,0 +1,8 @@ +abstract class Foo { + private String foo; + + public A(String foo) { + this.foo = foo; + Runnable r = () -> {this.foo = "";}; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/expected.xml b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/expected.xml new file mode 100644 index 000000000000..9ac879d78616 --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/expected.xml @@ -0,0 +1,3 @@ + + + diff --git a/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/src/Foo.java b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/src/Foo.java new file mode 100644 index 000000000000..c970101d56a9 --- /dev/null +++ b/java/java-tests/testData/inspection/canBeFinal/assignedFromLambdaInClassInitializer/src/Foo.java @@ -0,0 +1,6 @@ +abstract class Foo { + private String foo = "1"; + { + Runnable r = () -> {this.foo = "";}; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/CanBeFinalTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/CanBeFinalTest.java index 043c9a655c91..602166aa1aea 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/CanBeFinalTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/CanBeFinalTest.java @@ -47,6 +47,14 @@ public class CanBeFinalTest extends InspectionTestCase { doTest(); } + public void testassignedFromLambda() { + doTest(); + } + + public void testassignedFromLambdaInClassInitializer() { + doTest(); + } + public void testsimpleClassInheritance() { doTest(); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefGraphAnnotator.java b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefGraphAnnotator.java index ebae56ed25d0..752dff46259c 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefGraphAnnotator.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefGraphAnnotator.java @@ -55,6 +55,26 @@ public abstract class RefGraphAnnotator { boolean referencedFromClassInitializer) { } + /** + * Called when a reference to the specified element has been found. + * + * @param refWhat the referenced element. + * @param refFrom the referencing element. + * @param referencedFromClassInitializer if true, {@code refFrom} is a class and the reference + * has been found in its initializer block. + * @param forReading used for reading + * @param forWriting used for writing + * @param referenceElement reference element in refFrom + */ + public void onMarkReferenced(RefElement refWhat, + RefElement refFrom, + boolean referencedFromClassInitializer, + boolean forReading, + boolean forWriting, + PsiElement referenceElement) { + onMarkReferenced(refWhat, refFrom, referencedFromClassInitializer, forReading, forWriting); + } + /** * Called when a reference to the specified element has been found. * diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index ffd9b1706008..cac652b97a97 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -169,6 +169,17 @@ public class RefManagerImpl extends RefManager { annotator.onMarkReferenced(refWhat, refFrom, referencedFromClassInitializer, forReading, forWriting); } } + + void fireNodeMarkedReferenced(RefElement refWhat, + RefElement refFrom, + boolean referencedFromClassInitializer, + final boolean forReading, + final boolean forWriting, + PsiElement element) { + for (RefGraphAnnotator annotator : myGraphAnnotators) { + annotator.onMarkReferenced(refWhat, refFrom, referencedFromClassInitializer, forReading, forWriting, element); + } + } public void fireNodeMarkedReferenced(PsiElement what, PsiElement from) { for (RefGraphAnnotator annotator : myGraphAnnotators) {