From f1ad5bc4a688fa967794211e3d78c3eb3aaf947a Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 28 Jun 2016 18:33:22 +0300 Subject: [PATCH] unused inspection: treat fields as unused if they are implicitly write but not implicitly read (IDEA-118095) --- .../UnusedDeclarationInspectionBase.java | 15 +++--- .../reference/RefFieldImpl.java | 3 +- .../deadCode/mockedField/expected.xml | 2 + .../deadCode/mockedField/src/Test.java | 11 ++++ .../codeInspection/UnusedDeclarationTest.java | 4 ++ .../codeInspection/reference/RefUtil.java | 16 ++++++ plugins/junit/src/META-INF/plugin.xml | 1 + .../junit2/inspection/JUnitEntryPoint.java | 4 -- .../JUnitImplicitUsageProvider.java | 53 +++++++++++++++++++ 9 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 java/java-tests/testData/inspection/deadCode/mockedField/expected.xml create mode 100644 java/java-tests/testData/inspection/deadCode/mockedField/src/Test.java create mode 100644 plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitImplicitUsageProvider.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java index 590a5aa53a2e..36c8ea3485f6 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java @@ -18,7 +18,6 @@ package com.intellij.codeInspection.deadCode; import com.intellij.ToolExtensionPoints; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.GroupNames; -import com.intellij.codeInsight.daemon.ImplicitUsageProvider; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtilBase; import com.intellij.codeInspection.*; @@ -37,7 +36,6 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.vfs.VirtualFile; @@ -372,7 +370,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { public boolean isEntryPoint(@NotNull RefElement owner) { final PsiElement element = owner.getElement(); - if (RefUtil.isImplicitUsage(element)) return true; + if (isImplicitUsage(element)) return true; if (element instanceof PsiModifierListOwner) { final EntryPointsManager entryPointsManager = EntryPointsManager.getInstance(element.getProject()); if (entryPointsManager.isEntryPoint(element)) { @@ -389,6 +387,11 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { return false; } + private static boolean isImplicitUsage(PsiElement element) { + return element instanceof PsiField ? RefUtil.isImplicitRead(element) + : RefUtil.isImplicitUsage(element); + } + public boolean isEntryPoint(@NotNull PsiElement element) { final Project project = element.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); @@ -417,11 +420,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { return true; } } - final ImplicitUsageProvider[] implicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); - for (ImplicitUsageProvider provider : implicitUsageProviders) { - if (provider.isImplicitUsage(element)) return true; - } - return false; + return isImplicitUsage(element); } public boolean isGlobalEnabledInEditor() { 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 fa9c17c35ef3..a70f700bad2b 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 @@ -21,7 +21,6 @@ import com.intellij.psi.*; import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -122,7 +121,7 @@ public class RefFieldImpl extends RefJavaElementImpl implements RefField { refUtil.addReferences(psiField, this, psiField); } - if (psiField.getInitializer() != null || psiField instanceof PsiEnumConstant) { + if (psiField.getInitializer() != null || psiField instanceof PsiEnumConstant || RefUtil.isWriteImplicitly(psiField)) { if (!checkFlag(USED_FOR_WRITING_MASK)) { setFlag(true, ASSIGNED_ONLY_IN_INITIALIZER_MASK); setFlag(true, USED_FOR_WRITING_MASK); diff --git a/java/java-tests/testData/inspection/deadCode/mockedField/expected.xml b/java/java-tests/testData/inspection/deadCode/mockedField/expected.xml new file mode 100644 index 000000000000..4704d91e891d --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/mockedField/expected.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/deadCode/mockedField/src/Test.java b/java/java-tests/testData/inspection/deadCode/mockedField/src/Test.java new file mode 100644 index 000000000000..ac7e1bbc3884 --- /dev/null +++ b/java/java-tests/testData/inspection/deadCode/mockedField/src/Test.java @@ -0,0 +1,11 @@ +public class MyMTest { + @org.mockito.Mock + private String myFoo; + + { + System.out.println(myFoo); + } + + @org.junit.Test + public void testName() throws Exception {} +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationTest.java index 4adfd853b947..36a110a64240 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationTest.java @@ -164,6 +164,10 @@ public class UnusedDeclarationTest extends InspectionTestCase { doTest(); } + public void testMockedField() { + doTest(); + } + public void testConstructorCalls() { doTest(); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefUtil.java b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefUtil.java index 1790f700f771..017836dddb94 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/reference/RefUtil.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/reference/RefUtil.java @@ -41,6 +41,14 @@ public class RefUtil { return false; } + public static boolean isImplicitRead(PsiElement element) { + final ImplicitUsageProvider[] implicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); + for (ImplicitUsageProvider provider : implicitUsageProviders) { + if (provider.isImplicitRead(element)) return true; + } + return false; + } + public static boolean contains(RefEntity element, @NotNull Collection entities){ for (RefEntity refEntity : entities) { if (Comparing.equal(refEntity, element)) { @@ -49,4 +57,12 @@ public class RefUtil { } return false; } + + public static boolean isWriteImplicitly(PsiElement element) { + final ImplicitUsageProvider[] implicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); + for (ImplicitUsageProvider provider : implicitUsageProviders) { + if (provider.isImplicitWrite(element)) return true; + } + return false; + } } diff --git a/plugins/junit/src/META-INF/plugin.xml b/plugins/junit/src/META-INF/plugin.xml index 949eb0849641..a7fa2901f522 100644 --- a/plugins/junit/src/META-INF/plugin.xml +++ b/plugins/junit/src/META-INF/plugin.xml @@ -62,6 +62,7 @@ + diff --git a/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitEntryPoint.java b/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitEntryPoint.java index 95d9521119b7..1d22952dd7d2 100644 --- a/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitEntryPoint.java +++ b/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitEntryPoint.java @@ -104,10 +104,6 @@ public class JUnitEntryPoint extends EntryPoint { @Override public String[] getIgnoreAnnotations() { return new String[]{"org.junit.Rule", - "org.mockito.Mock", - "org.mockito.Spy", - "org.mockito.Captor", - "org.mockito.InjectMocks", "org.junit.ClassRule", "org.junit.experimental.theories.DataPoint"}; } diff --git a/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitImplicitUsageProvider.java b/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitImplicitUsageProvider.java new file mode 100644 index 000000000000..acb1ee747726 --- /dev/null +++ b/plugins/junit/src/com/intellij/execution/junit2/inspection/JUnitImplicitUsageProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2016 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. + */ +package com.intellij.execution.junit2.inspection; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.daemon.ImplicitUsageProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiParameter; + +import java.util.Arrays; +import java.util.List; + +public class JUnitImplicitUsageProvider implements ImplicitUsageProvider { + private static final String MOCK = "org.mockito.Mock"; + private static final List INJECTED_FIELD_ANNOTATIONS = Arrays.asList( + MOCK, + "org.mockito.Spy", + "org.mockito.Captor", + "org.mockito.InjectMocks"); + + + @Override + public boolean isImplicitUsage(PsiElement element) { + return isImplicitWrite(element); + } + + @Override + public boolean isImplicitRead(PsiElement element) { + return false; + } + + @Override + public boolean isImplicitWrite(PsiElement element) { + if (element instanceof PsiParameter) { + return AnnotationUtil.isAnnotated((PsiParameter)element, MOCK, false); + } + return element instanceof PsiField && AnnotationUtil.isAnnotated((PsiField) element, INJECTED_FIELD_ANNOTATIONS); + } +}