diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionBase.java index 7e27be3a31d8..8c87ba1b32b1 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionBase.java @@ -26,6 +26,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.util.ObjectUtils; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -131,8 +132,7 @@ public class UnnecessaryLocalVariableInspectionBase extends BaseInspection { return false; } if (!(referent instanceof PsiLocalVariable || referent instanceof PsiParameter)) { - if (!(referent instanceof PsiField) || !((PsiField)referent).hasModifierProperty(PsiModifier.FINAL) - || ReferencesSearch.search(variable).findAll().size() != 1) { + if (!isFinalChain(reference) || ReferencesSearch.search(variable).findAll().size() != 1) { // only warn when variable is referenced once, to avoid warning when a field is cached in local variable // as in e.g. gnu.trove.TObjectHash#forEach() return false; @@ -142,13 +142,11 @@ public class UnnecessaryLocalVariableInspectionBase extends BaseInspection { if (containingScope == null) { return false; } - if (!variable.hasModifierProperty(PsiModifier.FINAL) && - VariableAccessUtils.variableIsAssigned(variable, containingScope, false)) { + if (variableMayChange(containingScope, null, variable)) { return false; } final PsiVariable initialization = (PsiVariable)referent; - if (!initialization.hasModifierProperty(PsiModifier.FINAL) && - VariableAccessUtils.variableIsAssigned(initialization, containingScope, false)) { + if (variableMayChange(containingScope, PsiUtil.skipParenthesizedExprDown(reference.getQualifierExpression()), initialization)) { return false; } @@ -158,10 +156,10 @@ public class UnnecessaryLocalVariableInspectionBase extends BaseInspection { return false; } - final boolean finalVariableIntroduction = + final boolean finalVariableIntroduction = !initialization.hasModifierProperty(PsiModifier.FINAL) && variable.hasModifierProperty(PsiModifier.FINAL) || PsiUtil.isLanguageLevel8OrHigher(initialization) && - !HighlightControlFlowUtil.isEffectivelyFinal(initialization, containingScope, null) && + !HighlightControlFlowUtil.isEffectivelyFinal(initialization, containingScope, null) && HighlightControlFlowUtil.isEffectivelyFinal(variable, containingScope, null); final PsiType variableType = variable.getType(); @@ -179,12 +177,12 @@ public class UnnecessaryLocalVariableInspectionBase extends BaseInspection { if (resolveHelper.resolveReferencedVariable(initializationName, refElement) != initialization) { return false; } - + if (!sameType) { final PsiElement parent = refElement.getParent(); if (parent instanceof PsiReferenceExpression) { final PsiElement resolve = ((PsiReferenceExpression)parent).resolve(); - if (resolve instanceof PsiMember && + if (resolve instanceof PsiMember && ((PsiMember)resolve).hasModifierProperty(PsiModifier.PRIVATE)) { return false; } @@ -195,6 +193,32 @@ public class UnnecessaryLocalVariableInspectionBase extends BaseInspection { return !TypeConversionUtil.boxingConversionApplicable(variableType, initializationType); } + private boolean isFinalChain(PsiReferenceExpression reference) { + while (true) { + PsiElement element = reference.resolve(); + if (!(element instanceof PsiField)) return true; + if (!((PsiField)element).hasModifierProperty(PsiModifier.FINAL)) return false; + PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(reference.getQualifierExpression()); + if (qualifier == null || qualifier instanceof PsiThisExpression) return true; + if (!(qualifier instanceof PsiReferenceExpression)) return false; + reference = (PsiReferenceExpression)qualifier; + } + } + + private boolean variableMayChange(PsiCodeBlock containingScope, PsiExpression qualifier, PsiVariable variable) { + while (variable != null) { + if (!variable.hasModifierProperty(PsiModifier.FINAL) && + VariableAccessUtils.variableIsAssigned(variable, containingScope, false)) { + return true; + } + if (!(qualifier instanceof PsiReferenceExpression)) break; + PsiReferenceExpression qualifierReference = (PsiReferenceExpression)qualifier; + qualifier = PsiUtil.skipParenthesizedExprDown(qualifierReference.getQualifierExpression()); + variable = ObjectUtils.tryCast(qualifierReference.resolve(), PsiVariable.class); + } + return false; + } + private boolean isImmediatelyReturned(PsiVariable variable) { final PsiCodeBlock containingScope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, true, PsiClass.class); if (containingScope == null) { diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/C.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/C.java index c23f8f4041aa..c86ac1c2dfbb 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/C.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/C.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -199,4 +199,29 @@ class UsingConstant { int yes = YES; yes++; } -} \ No newline at end of file +} + +class Test { + final int x = (int)(Math.random() * 10); + + int test() { + Test t = new Test(); + int xx = this.x; + t = null; + return xx; + } + + int test2() { + Test t = new Test(); + int xx = t.x; + t = null; + return xx; + } + + int test(Test[] arr) { + // copying of the field from array element is conservatively not considered safe to inline as array may change + int res = arr[0].x; + arr[0] = null; + return res; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/Tree.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/Tree.java new file mode 100644 index 000000000000..377d227a01c7 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/dataflow/unnecessary_local_variable/Tree.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.util.Iterator; +import java.util.NoSuchElementException; + +final class Tree { + + private final T elem; + private final Tree parent; + + private Tree(T elem, Tree parent) { + this.elem = elem; + this.parent = parent; + } + + /** Iterator from current tree element to it's root. */ + public Iterator toRoot() { + return new Iterator() { + Tree curr = Tree.this; + + @Override + public void remove() {} + + @Override + public boolean hasNext() { + return curr.parent != null; + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + final T res = curr.elem; // IDEA inspection: Local variable 'res' is redundant (IDEA-163786) + curr = curr.parent; + return res; + } + }; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionTest.java index b00103f8ab98..4e39393a440e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dataflow/UnnecessaryLocalVariableInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,4 +25,6 @@ public class UnnecessaryLocalVariableInspectionTest extends LightInspectionTestC } public void testC() { doTest(); } + + public void testTree() { doTest(); } } \ No newline at end of file