From 52a2de3b566de1dca0d13aad327e86fc4088f871 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 22 Dec 2014 13:44:38 +0100 Subject: [PATCH] don't infer annotations in anonymous classes unless they'll be useful --- .../dataFlow/ContractInference.java | 17 +----- .../dataFlow/InferenceFromSourceUtil.java | 55 +++++++++++++++++++ .../dataFlow/NullityInference.java | 2 +- .../dataFlow/PurityInference.java | 5 +- .../ContractInferenceFromSourceTest.groovy | 23 ++++++++ 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index 1fc3ea176503..7dcf36afb4ee 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -17,17 +17,12 @@ package com.intellij.codeInspection.dataFlow; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.dataFlow.MethodContract.ValueConstraint; -import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.RecursionManager; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.util.PsiUtilCore; +import com.intellij.psi.util.*; import com.intellij.util.Function; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; @@ -47,10 +42,10 @@ public class ContractInference { @NotNull public static List inferContracts(@NotNull final PsiMethod method) { - if (isLibraryCode(method)) { + if (!InferenceFromSourceUtil.shouldInferFromSource(method)) { return Collections.emptyList(); } - + return CachedValuesManager.getCachedValue(method, new CachedValueProvider>() { @Nullable @Override @@ -66,12 +61,6 @@ public class ContractInference { } }); } - - static boolean isLibraryCode(@NotNull PsiMethod method) { - if (method instanceof PsiCompiledElement) return true; - VirtualFile virtualFile = PsiUtilCore.getVirtualFile(method); - return virtualFile != null && FileIndexFacade.getInstance(method.getProject()).isInLibrarySource(virtualFile); - } } class ContractInferenceInterpreter { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java new file mode 100644 index 000000000000..408277627937 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/InferenceFromSourceUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2014 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.codeInspection.dataFlow; + +import com.intellij.openapi.roots.FileIndexFacade; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class InferenceFromSourceUtil { + static boolean shouldInferFromSource(@NotNull PsiMethod method) { + if (isLibraryCode(method) || + method.hasModifierProperty(PsiModifier.ABSTRACT) || + PsiUtil.canBeOverriden(method) || + method.getBody() == null) { + return false; + } + + if (method.hasModifierProperty(PsiModifier.STATIC)) return true; + + return !isUnusedInAnonymousClass(method); + } + + private static boolean isUnusedInAnonymousClass(@NotNull PsiMethod method) { + PsiClass containingClass = method.getContainingClass(); + return containingClass instanceof PsiAnonymousClass && + MethodReferencesSearch.search(method, new LocalSearchScope(containingClass), false).findFirst() == null; + } + + private static boolean isLibraryCode(@NotNull PsiMethod method) { + if (method instanceof PsiCompiledElement) return true; + VirtualFile virtualFile = PsiUtilCore.getVirtualFile(method); + return virtualFile != null && FileIndexFacade.getInstance(method.getProject()).isInLibrarySource(virtualFile); + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java index 5b79e8637678..ab02b5bf7648 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullityInference.java @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class NullityInference { public static Nullness inferNullity(final PsiMethod method) { - if (ContractInference.isLibraryCode(method)) { + if (!InferenceFromSourceUtil.shouldInferFromSource(method)) { return Nullness.UNKNOWN; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java index 6ceaeec72ae5..ee81e7c09fd3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/PurityInference.java @@ -34,7 +34,10 @@ import java.util.List; public class PurityInference { public static boolean inferPurity(@NotNull final PsiMethod method) { - if (ContractInference.isLibraryCode(method) || method.getReturnType() == PsiType.VOID || method.getBody() == null || method.isConstructor()) { + if (!InferenceFromSourceUtil.shouldInferFromSource(method) || + method.getReturnType() == PsiType.VOID || + method.getBody() == null || + method.isConstructor()) { return false; } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy index 1ee47eccb835..2d2aeb7c92e5 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy @@ -16,6 +16,8 @@ package com.intellij.codeInspection import com.intellij.codeInspection.dataFlow.ContractInference +import com.intellij.psi.PsiAnonymousClass +import com.intellij.psi.util.PsiTreeUtil import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase /** @@ -448,6 +450,27 @@ public static boolean isBlank(String s) { assert c == ['null -> true'] } + public void "test no inference for unused anonymous class methods where annotations won't be used anyway"() { + def method = PsiTreeUtil.findChildOfType(myFixture.addClass(""" +class Foo {{ + new Object() { + Object foo() { return null;} + }; +}}"""), PsiAnonymousClass).methods[0] + assert ContractInference.inferContracts(method).collect { it as String } == [] + } + + public void "test inference for used anonymous class methods"() { + def method = PsiTreeUtil.findChildOfType(myFixture.addClass(""" +class Foo {{ + new Object() { + Object foo() { return null;} + Object bar() { return foo();} + }; +}}"""), PsiAnonymousClass).methods[0] + assert ContractInference.inferContracts(method).collect { it as String } == [' -> null'] + } + private String inferContract(String method) { return assertOneElement(inferContracts(method)) }