NullableStuffInspection: an option to not complain about generated implementation (IDEA-187625)

This commit is contained in:
peter
2018-04-27 16:48:31 +02:00
parent aed9e1a30b
commit 00222d2816
11 changed files with 166 additions and 7 deletions

View File

@@ -4,6 +4,7 @@ package com.intellij.codeInspection;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
import com.intellij.codeInspection.nullable.NullableStuffInspectionBase;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
@@ -83,9 +84,9 @@ public class AnnotateMethodFix implements LocalQuickFix {
for (PsiMethod psiMethod : methods) {
ReadAction.run(() -> {
if (psiMethod.isPhysical() &&
psiMethod.getManager().isInProject(psiMethod) &&
AnnotationUtil.isAnnotatingApplicable(psiMethod, myAnnotation) &&
!AnnotationUtil.isAnnotated(psiMethod, myAnnotation, CHECK_EXTERNAL | CHECK_TYPE)) {
!AnnotationUtil.isAnnotated(psiMethod, myAnnotation, CHECK_EXTERNAL | CHECK_TYPE) &&
!NullableStuffInspectionBase.shouldSkipOverriderAsGenerated(psiMethod)) {
toAnnotate.add(psiMethod);
}
});

View File

@@ -71,11 +71,13 @@ class AnnotateOverriddenMethodParameterFix implements LocalQuickFix {
PsiMethod[] methods = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
for (PsiMethod psiMethod : methods) {
if (NullableStuffInspectionBase.shouldSkipOverriderAsGenerated(psiMethod)) continue;
PsiParameter[] psiParameters = psiMethod.getParameterList().getParameters();
if (index >= psiParameters.length) continue;
PsiParameter psiParameter = psiParameters[index];
if (PsiManager.getInstance(project).isInProject(psiMethod) && AddAnnotationPsiFix.isAvailable(psiMethod, myAnnotation)) {
toAnnotate.add(psiParameter);
if (AddAnnotationPsiFix.isAvailable(psiMethod, myAnnotation)) {
toAnnotate.add(psiParameters[index]);
}
}

View File

@@ -14,9 +14,12 @@ import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.codeInspection.dataFlow.instructions.MethodCallInstruction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.GeneratedSourcesFilter;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
@@ -844,14 +847,13 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
hasAnnotatedParameter |= parameterAnnotated[i];
}
if (hasAnnotatedParameter || annotated.isDeclaredNotNull && !hasInheritableNotNull(method)) {
PsiManager manager = method.getManager();
final String defaultNotNull = nullableManager.getDefaultNotNull();
final boolean superMethodApplicable = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull);
PsiMethod[] overridings =
OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
boolean methodQuickFixSuggested = false;
for (PsiMethod overriding : overridings) {
if (!manager.isInProject(overriding)) continue;
if (shouldSkipOverriderAsGenerated(overriding)) continue;
if (!methodQuickFixSuggested
&& annotated.isDeclaredNotNull
@@ -908,6 +910,14 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
}
public static boolean shouldSkipOverriderAsGenerated(PsiMethod overriding) {
if (Registry.is("idea.report.nullity.missing.in.generated.overriders")) return false;
PsiFile file = overriding.getContainingFile();
VirtualFile virtualFile = file != null ? file.getVirtualFile() : null;
return virtualFile != null && GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(virtualFile, overriding.getProject());
}
private static boolean isNotNullNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean skipExternal) {
Project project = owner.getProject();
NullableNotNullManager manager = NullableNotNullManager.getInstance(project);

View File

@@ -0,0 +1,13 @@
import org.jetbrains.annotations.NotNull;
public interface MyTestClass {
@NotNull
String implementMe(@NotNull String arg);
}
public class MyRealTestClass implements MyTestClass {
@NotNull
String implementMe(String arg) {
}
}

View File

@@ -0,0 +1,12 @@
import org.jetbrains.annotations.NotNull;
public interface MyTestClass {
@NotNull
String implementMe(@NotNull String arg);
}
public class MyRealTestClass implements MyTestClass {
String implementMe(@NotNull String arg) {
}
}

View File

@@ -0,0 +1,12 @@
import org.jetbrains.annotations.NotNull;
public interface MyTestClass {
@NotNull<caret>
String implementMe(@NotNull String arg);
}
public class MyRealTestClass implements MyTestClass {
String implementMe(String arg) {
}
}

View File

@@ -0,0 +1,12 @@
import org.jetbrains.annotations.NotNull;
public interface MyTestClass {
@NotNull
String implementMe(@NotNull<caret> String arg);
}
public class MyRealTestClass implements MyTestClass {
String implementMe(String arg) {
}
}

View File

@@ -0,0 +1,8 @@
package foo;
import org.jetbrains.annotations.NotNull;
interface MyTestClass {
@NotNull
String implementMe();
}

View File

@@ -0,0 +1,65 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.nullable.NullableStuffInspection;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.GeneratedSourcesFilter;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class AnnotateMethodInGeneratedFilesTest extends LightCodeInsightFixtureTestCase {
private final GeneratedSourcesFilter myGeneratedSourcesFilter = new GeneratedSourcesFilter() {
@Override
public boolean isGeneratedSource(@NotNull VirtualFile file, @NotNull Project project) {
return file.getName().startsWith("Gen");
}
};
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath() +
"/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethodInGeneratedFiles";
}
@Override
public void setUp() throws Exception {
super.setUp();
Registry.get("idea.report.nullity.missing.in.generated.overriders").setValue(false, getTestRootDisposable());
myFixture.enableInspections(NullableStuffInspection.class);
Extensions.getRootArea().getExtensionPoint(GeneratedSourcesFilter.EP_NAME).registerExtension(myGeneratedSourcesFilter);
}
@Override
public void tearDown() throws Exception {
try {
Extensions.getRootArea().getExtensionPoint(GeneratedSourcesFilter.EP_NAME).unregisterExtension(myGeneratedSourcesFilter);
}
finally {
super.tearDown();
}
}
public void testAnnotateOverriddenMethod() {
doTest("Annotate overridden methods");
}
public void testAnnotateOverriddenParameters() {
doTest("Annotate overridden method parameters");
}
private void doTest(String quickFixName) {
PsiClass generated =
myFixture.addClass("public class GenMyTestClass implements MyTestClass {String implementMe(String arg) { return \"\"; } }");
String generatedTextBefore = generated.getText();
myFixture.configureByFile("before" + getTestName(false) + ".java");
myFixture.launchAction(myFixture.findSingleIntention(quickFixName));
myFixture.checkResultByFile("after" + getTestName(false) + ".java");
assertEquals(generatedTextBefore, generated.getText());
}
}

View File

@@ -19,8 +19,13 @@ package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.nullable.NullableStuffInspection;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.GeneratedSourcesFilter;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PsiTestUtil;
@@ -37,6 +42,13 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
};
private NullableStuffInspection myInspection = new NullableStuffInspection();
private final GeneratedSourcesFilter myGeneratedSourcesFilter = new GeneratedSourcesFilter() {
@Override
public boolean isGeneratedSource(@NotNull VirtualFile file, @NotNull Project project) {
return file.getName().startsWith("Gen");
}
};
@NotNull
@Override
protected LightProjectDescriptor getProjectDescriptor() {
@@ -57,11 +69,13 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
public void setUp() throws Exception {
super.setUp();
myInspection.REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = false;
Extensions.getRootArea().getExtensionPoint(GeneratedSourcesFilter.EP_NAME).registerExtension(myGeneratedSourcesFilter);
}
@Override
protected void tearDown() throws Exception {
myInspection = null;
Extensions.getRootArea().getExtensionPoint(GeneratedSourcesFilter.EP_NAME).unregisterExtension(myGeneratedSourcesFilter);
super.tearDown();
}
@@ -105,6 +119,13 @@ public class NullableStuffInspectionTest extends LightCodeInsightFixtureTestCase
doTest();
}
public void testOverriddenMethodsInGeneratedCode() {
Registry.get("idea.report.nullity.missing.in.generated.overriders").setValue(false, getTestRootDisposable());
myInspection.REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true;
myFixture.addClass("package foo; public class GenMyTestClass implements MyTestClass { String implementMe() {} }");
doTest();
}
public void testOverriddenViaMethodReference() { doTest(); }
public void testOverridingExternalNotNull() { doTest(); }

View File

@@ -674,6 +674,9 @@ ide.dfa.state.limit.description=Maximal allowed number of instruction states ana
idea.dfa.live.variables.analysis=true
idea.dfa.live.variables.analysis.description=Whether to flush dead variables when they're not needed when performing data flow analysis
idea.report.nullity.missing.in.generated.overriders=true
idea.report.nullity.missing.in.generated.overriders.description=Whether "@NotNull/@Nullable problems" inspection should complain about overriding methods or parameters missing @NotNull, which occur in generated code
ide.ignore.call.result.inspection.honor.inferred.pure=true
ide.ignore.call.result.inspection.honor.inferred.pure.description=Whether inferred @Contract(pure=true) annotations should be taken into account in ''Result of method call ignored'' inspection