From 67d404a6f3a27f8984717d2ff303fffa37b3e837 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 1 Dec 2025 13:19:14 +0100 Subject: [PATCH] IDEA-372145 [java-intentions] follow-up: use different nullity annotations in AnnotateOverriddenMethodParameterFix Also: simplify via delegation to ModCommand GitOrigin-RevId: 59f6cbdbfbee2995036b802b0a3add9f2360122f --- .../messages/JavaAnalysisBundle.properties | 3 +- .../AnnotateOverriddenMethodParameterFix.java | 61 ++++++++----------- .../nullable/NullableStuffInspectionBase.java | 2 +- .../afterOverriddenParameters.java | 2 +- .../beforeOverriddenParameters.java | 2 +- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties index 83fd64de4680..473b05c675ee 100644 --- a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties +++ b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties @@ -128,7 +128,8 @@ access.to.field.code.ref.code.outside.of.declared.guards.loc=Access to field #ref() outside of declared guards annotate.as.safevarargs=Annotate as '@SafeVarargs' annotate.overridden.methods.parameters.family.name=Annotate overriding method parameters -annotate.overridden.methods.parameters=Annotate overriding method parameters as ''@{0}'' +annotate.overridden.methods.parameters.nonnull=Annotate overriding method parameters as non-null +annotate.overridden.methods.parameters.nullable=Annotate overriding method parameters as nullable anonymous.ref.loc.can.be.replaced.with.0=Anonymous #ref can be replaced with {0} anonymous.ref.loc.can.be.replaced.with.lambda=Anonymous #ref can be replaced with lambda assigning.a.collection.of.nullable.elements=Assigning a collection of nullable elements into a collection of non-null elements diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/AnnotateOverriddenMethodParameterFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/AnnotateOverriddenMethodParameterFix.java index 04f9288fdb1b..076c333218e9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/AnnotateOverriddenMethodParameterFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/AnnotateOverriddenMethodParameterFix.java @@ -1,24 +1,26 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInspection.nullable; -import com.intellij.codeInsight.*; -import com.intellij.codeInsight.intention.AddAnnotationPsiFix; +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.Nullability; +import com.intellij.codeInsight.NullabilityAnnotationInfo; +import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.codeInsight.intention.AddAnnotationModCommandAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.java.analysis.JavaAnalysisBundle; +import com.intellij.modcommand.ActionContext; +import com.intellij.modcommand.ModCommand; +import com.intellij.modcommand.ModCommandAction; +import com.intellij.modcommand.ModCommandExecutor; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiInvalidElementAccessException; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiParameter; import com.intellij.psi.search.searches.OverridingMethodsSearch; -import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtilRt; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -26,17 +28,17 @@ import java.util.List; import java.util.function.Consumer; public class AnnotateOverriddenMethodParameterFix implements LocalQuickFix { - private final String myAnnotation; private final Nullability myTargetNullability; - AnnotateOverriddenMethodParameterFix(@NotNull Nullability targetNullability, String annotation) { - myAnnotation = annotation; + AnnotateOverriddenMethodParameterFix(@NotNull Nullability targetNullability) { myTargetNullability = targetNullability; } @Override public @NotNull String getName() { - return JavaAnalysisBundle.message("annotate.overridden.methods.parameters", ClassUtil.extractClassName(myAnnotation)); + return myTargetNullability == Nullability.NOT_NULL ? + JavaAnalysisBundle.message("annotate.overridden.methods.parameters.nonnull") : + JavaAnalysisBundle.message("annotate.overridden.methods.parameters.nullable"); } @Override @@ -49,38 +51,25 @@ public class AnnotateOverriddenMethodParameterFix implements LocalQuickFix { List toAnnotate = new ArrayList<>(); PsiParameter parameter = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiParameter.class, false); - if (parameter == null || !processParameterInheritorsUnderProgress(parameter, param -> { - if (AddAnnotationPsiFix.isAvailable(param, myAnnotation)) { - toAnnotate.add(param); - } - })) { + if (parameter == null || !processParameterInheritorsUnderProgress(parameter, toAnnotate::add)) { return; } FileModificationService.getInstance().preparePsiElementsForWrite(toAnnotate); - RuntimeException exception = null; - NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - String[] annotationsToRemove = - ArrayUtil.toStringArray(myTargetNullability == Nullability.NOT_NULL ? manager.getNullables() : manager.getNotNulls()); + ActionContext actionContext = ActionContext.from(descriptor); for (PsiParameter psiParam : toAnnotate) { assert psiParam != null : toAnnotate; - try { - if (AnnotationUtil.isAnnotatingApplicable(psiParam, myAnnotation)) { - NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(psiParam); - if (info != null && info.getNullability() == myTargetNullability && !info.isInferred()) continue; - AddAnnotationPsiFix fix = new AddAnnotationPsiFix(myAnnotation, psiParam, annotationsToRemove); - PsiFile containingFile = psiParam.getContainingFile(); - if (psiParam.isValid() && fix.isAvailable(project, containingFile, psiParam, psiParam)) { - fix.invoke(project, containingFile, psiParam, psiParam); - } + ModCommandExecutor.executeInteractively(actionContext, getFamilyName(), null, () -> { + NullabilityAnnotationInfo info = NullableNotNullManager.getInstance(project).findEffectiveNullabilityInfo(psiParam); + if (info != null && info.getNullability() == myTargetNullability && + info.getInheritedFrom() == null && !info.isInferred()) { + return ModCommand.nop(); } - } - catch (PsiInvalidElementAccessException|IncorrectOperationException e) { - exception = e; - } - if (exception != null) { - throw exception; - } + ModCommandAction action = myTargetNullability == Nullability.NOT_NULL + ? AddAnnotationModCommandAction.createAddNotNullFix(psiParam) + : AddAnnotationModCommandAction.createAddNullableFix(psiParam); + return action == null || action.getPresentation(actionContext) == null ? ModCommand.nop() : action.perform(actionContext); + }); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java index a9849c3cf496..b5b1a07da245 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java @@ -1249,7 +1249,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection if (psiElement == null) continue; } LocalQuickFix fix = isAnnotatingApplicable(parameter, defaultNotNull) - ? new AnnotateOverriddenMethodParameterFix(Nullability.NOT_NULL, defaultNotNull) + ? new AnnotateOverriddenMethodParameterFix(Nullability.NOT_NULL) : null; reportProblem(holder, psiElement, fix, "nullable.stuff.problems.overridden.method.parameters.are.not.annotated"); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenParameters.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenParameters.java index 2fa5d820b6f6..4803dda44961 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenParameters.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenParameters.java @@ -1,4 +1,4 @@ -// "Annotate overriding method parameters as '@NotNull'" "true" +// "Annotate overriding method parameters as non-null" "true" import org.jetbrains.annotations.NotNull; abstract class P2 { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenParameters.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenParameters.java index 2ffaae0e9e17..ef38d3629042 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenParameters.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenParameters.java @@ -1,4 +1,4 @@ -// "Annotate overriding method parameters as '@NotNull'" "true" +// "Annotate overriding method parameters as non-null" "true" import org.jetbrains.annotations.NotNull; abstract class P2 {