diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java
index f078182b5de4..a44496b25551 100644
--- a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java
+++ b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java
@@ -514,16 +514,14 @@ public final class CaptureConfigurable implements SearchableConfigurable, NoScro
mySettings = DebuggerProjectSettings.getInstance(myProject);
myAsyncSchedulePanel = new AnnotationsPanel(project,
JavaDebuggerBundle.message("settings.async.schedule"),
- "",
getAsyncAnnotations(mySettings, true),
- Collections.singletonList(getAnnotationName(true)),
- Collections.emptySet(), false, false);
+ Collections.singletonList(getAnnotationName(true))
+ );
myAsyncExecutePanel = new AnnotationsPanel(project,
JavaDebuggerBundle.message("settings.async.execute"),
- "",
getAsyncAnnotations(mySettings, false),
- Collections.singletonList(getAnnotationName(false)),
- Collections.emptySet(), false, false);
+ Collections.singletonList(getAnnotationName(false))
+ );
init();
setTitle(JavaDebuggerBundle.message("settings.async.annotations.configuration"));
}
diff --git a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties
index b03920ca9acc..91c0d5dc90c1 100644
--- a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties
+++ b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties
@@ -157,7 +157,7 @@ inspection.annotate.overridden.method.and.self.quickfix.family.name=Annotate ove
inspection.annotate.overridden.method.and.self.quickfix.name=Annotate overriding methods and self with ''@{0}''
inspection.annotate.overridden.method.quickfix.family.name=Annotate overriding methods
inspection.annotate.overridden.method.quickfix.name=Annotate overriding methods with ''@{0}''
-inspection.annotate.overridden.method.nullable.quickfix.name=Annotate overriding methods as ''@{0}''
+inspection.annotate.overridden.method.nullable.quickfix.name=Add missing nullability annotation to overriding methods
inspection.c.style.array.declarations.option=Ignore C-style declarations in variables
inspection.can.be.final.accept.quickfix=Make final
inspection.can.be.final.option1=Report methods
@@ -310,7 +310,6 @@ inspection.visibility.package.private.top.level.classes=Suggest package-private
inspection.visibility.private.inner.members=Suggest 'private' for inner class members when referenced from outer class only
instance.member.guarded.by.static.0.loc=Instance member guarded by static "{0}" #loc
instance.member.guarded.by.static.ref.loc=Instance member guarded by static #ref #loc
-make.0.default.annotation=Make "{0}" default annotation
make.default.the.last.case.family.name=Make 'default' the last case
make.final.and.annotate.as.safevarargs=Make final and annotate as '@SafeVarargs'
method.reference.mapped.to.comparator=Method reference mapped to 'Comparator' interface does not fulfill the 'Comparator' contract
@@ -565,7 +564,6 @@ mutation.signature.problem.invalid.token=Invalid token: {0}; supported are ''thi
required.type=Required type
provided.type=Provided
type.mismatch.reason=reason: {0}
-inspection.message.code.generation.different.nullability.annotation.will.be.used=The generated code will use ''@{1}'' instead of ''@{0}''
change.visibility.level=Make {0} {1}
inspection.redundant.tostring.option.notnull.qualifier=Report only when qualifier is known to be not-null
highlighting.try.blocks=Highlighting try blocks\u2026
diff --git a/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml b/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml
index 153ac86d44dd..44c2aee521fa 100644
--- a/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml
+++ b/java/java-analysis-impl/resources/META-INF/JavaAnalysisPlugin.xml
@@ -337,14 +337,14 @@
description="Assume any implementation of methods like Object.toString() or Iterable.iterator() to be pure during bytecode inference. This assumption might lead to false-positives in some inspections, though it's believed to uncover more bugs."/>
+
-
-
-
-
+
+
+
fixes = createNPEFixes(expression, top, reporter.isOnTheFly(), alwaysNull);
fixes.add(LocalQuickFix.from(AddAnnotationModCommandAction.createAddNullableFix(target)));
reporter.registerProblem(expression, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
@@ -806,16 +804,18 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
}
reporter.registerProblem(expr, text, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
- else if (AnnotationUtil.isAnnotatingApplicable(anchor)) {
- final String defaultNullable = manager.getDefaultNullable();
- final String presentableNullable = StringUtil.getShortName(defaultNullable);
- final String text = exactlyNull
- ? JavaAnalysisBundle.message("dataflow.message.return.null.from.notnullable", presentableNullable)
- : JavaAnalysisBundle.message("dataflow.message.return.nullable.from.notnullable", presentableNullable);
- PsiMethod surroundingMethod = PsiTreeUtil.getParentOfType(anchor, PsiMethod.class, true, PsiLambdaExpression.class);
- final LocalQuickFix fix = surroundingMethod == null ? null :
- LocalQuickFix.from(AddAnnotationModCommandAction.createAddNullableFix(surroundingMethod));
- reporter.registerProblem(expr, text, LocalQuickFix.notNullElements(fix));
+ else {
+ final String defaultNullable = manager.getDefaultAnnotation(Nullability.NULLABLE, anchor);
+ if (AnnotationUtil.isAnnotatingApplicable(anchor, defaultNullable)) {
+ final String presentableNullable = StringUtil.getShortName(defaultNullable);
+ final String text = exactlyNull
+ ? JavaAnalysisBundle.message("dataflow.message.return.null.from.notnullable", presentableNullable)
+ : JavaAnalysisBundle.message("dataflow.message.return.nullable.from.notnullable", presentableNullable);
+ PsiMethod surroundingMethod = PsiTreeUtil.getParentOfType(anchor, PsiMethod.class, true, PsiLambdaExpression.class);
+ final LocalQuickFix fix = surroundingMethod == null ? null :
+ LocalQuickFix.from(AddAnnotationModCommandAction.createAddNullableFix(surroundingMethod));
+ reporter.registerProblem(expr, text, LocalQuickFix.notNullElements(fix));
+ }
}
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/ChangeNullableDefaultsFix.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/ChangeNullableDefaultsFix.java
deleted file mode 100644
index b3e99d699491..000000000000
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/ChangeNullableDefaultsFix.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2000-2024 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.codeInspection.ProblemDescriptor;
-import com.intellij.java.analysis.JavaAnalysisBundle;
-import com.intellij.modcommand.ModCommand;
-import com.intellij.modcommand.ModCommandQuickFix;
-import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiAnnotation;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import static com.intellij.modcommand.ModCommand.*;
-
-class ChangeNullableDefaultsFix extends ModCommandQuickFix {
- private final String myNotNullName;
- private final String myNullableName;
-
- ChangeNullableDefaultsFix(@Nullable PsiAnnotation notNull, @Nullable PsiAnnotation nullable) {
- myNotNullName = notNull != null ? notNull.getQualifiedName() : null;
- myNullableName = nullable != null ? nullable.getQualifiedName() : null;
- }
-
- ChangeNullableDefaultsFix(String notNull, String nullable) {
- myNotNullName = notNull;
- myNullableName = nullable;
- }
-
- @Override
- public @NotNull String getFamilyName() {
- return JavaAnalysisBundle.message("make.0.default.annotation", myNotNullName != null ? myNotNullName : myNullableName);
- }
-
- @Override
- public @NotNull ModCommand perform(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
- ModCommand command = nop();
- if (myNotNullName != null) {
- command = command.andThen(
- updateOptionList(descriptor.getPsiElement(), "NullableNotNullManager.myNotNulls", list -> {
- if (!list.contains(myNotNullName)) {
- list.add(myNotNullName);
- }
- })).andThen(updateOption(descriptor.getPsiElement(), "NullableNotNullManager.myDefaultNotNull", myNotNullName));
- }
- else {
- command = command.andThen(
- updateOptionList(descriptor.getPsiElement(), "NullableNotNullManager.myNullables", list -> {
- if (!list.contains(myNullableName)) {
- list.add(myNullableName);
- }
- })).andThen(updateOption(descriptor.getPsiElement(), "NullableNotNullManager.myDefaultNullable", myNullableName));
- }
- return command;
- }
-}
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 145e182e7272..5123c1214544 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
@@ -33,7 +33,6 @@ import com.intellij.psi.impl.search.JavaOverridingMethodsSearcher;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.*;
-import com.intellij.psi.util.ClassUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.ContainerUtil;
@@ -153,7 +152,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
if (classType.getPsiContext() instanceof PsiJavaCodeReferenceElement typeRef &&
typeRef.getParent() instanceof PsiTypeElement typeElement && typeElement.getType().equals(classType) &&
typeElement.acceptsAnnotations()) {
- fix = new AddTypeAnnotationFix(typeElement, manager.getDefaultNullable(), notNulls);
+ fix = new AddTypeAnnotationFix(typeElement, manager.getDefaultAnnotation(Nullability.NULLABLE, expression), notNulls);
}
holder.problem(expression,
JavaAnalysisBundle.message("inspection.nullable.problems.constructor.not.compatible.non.null.type.argument"))
@@ -183,11 +182,10 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
Project project = holder.getProject();
if (annotated.isDeclaredNotNull ^ annotated.isDeclaredNullable) {
- final String anno = annotated.isDeclaredNotNull ? manager.getDefaultNotNull() : manager.getDefaultNullable();
+ final String anno =
+ manager.getDefaultAnnotation(annotated.isDeclaredNotNull ? Nullability.NOT_NULL : Nullability.NULLABLE, field);
final List annoToRemove = annotated.isDeclaredNotNull ? nullables : notNulls;
- if (!checkNonStandardAnnotations(field, annotated, anno, holder)) return;
-
checkAccessors(field, annotated, project, manager, anno, annoToRemove, holder);
checkConstructorParameters(field, annotated, manager, anno, annoToRemove, holder);
@@ -413,7 +411,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
Nullability typeNullability = DfaPsiUtil.getTypeNullability(type);
if (typeNullability != Nullability.NOT_NULL &&
!(typeNullability == Nullability.UNKNOWN && type instanceof PsiWildcardType wildcardType && !wildcardType.isExtends())) {
- String annotationToAdd = manager.getDefaultNotNull();
+ String annotationToAdd = manager.getDefaultAnnotation(Nullability.NOT_NULL, reference);
PsiClass annotationClass = JavaPsiFacade.getInstance(project).findClass(annotationToAdd, element.getResolveScope());
AddTypeAnnotationFix fix = null;
if (annotationClass != null &&
@@ -624,23 +622,6 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
return ContainerUtil.find(NullableNotNullManager.getInstance(project).getNullables(), s -> facade.findClass(s, scope) != null) == null;
}
- private static boolean checkNonStandardAnnotations(PsiField field,
- Annotated annotated,
- String anno, @NotNull ProblemsHolder holder) {
- if (!isAnnotatingApplicable(field, anno)) {
- PsiAnnotation annotation = requireNonNull(annotated.isDeclaredNullable ? annotated.nullable : annotated.notNull);
- String message = JavaAnalysisBundle.message("inspection.message.code.generation.different.nullability.annotation.will.be.used",
- annotation.getQualifiedName(), anno);
- final PsiJavaCodeReferenceElement annotationNameReferenceElement = annotation.getNameReferenceElement();
- holder.registerProblem(annotationNameReferenceElement != null && annotationNameReferenceElement.isPhysical() ? annotationNameReferenceElement : field.getNameIdentifier(),
- message,
- ProblemHighlightType.WEAK_WARNING,
- new ChangeNullableDefaultsFix(annotated.notNull, annotated.nullable));
- return false;
- }
- return true;
- }
-
private void checkAccessors(PsiField field,
Annotated annotated,
Project project,
@@ -882,7 +863,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
if (isNonAnnotatedOverridingNotNull(method, superMethod)) {
- reportProblem(holder, identifier, createFixForNonAnnotatedOverridesNotNull(method, superMethod),
+ reportProblem(holder, identifier, createFixForNonAnnotatedOverridesNotNull(method),
"inspection.nullable.problems.method.overrides.NotNull", getPresentableAnnoName(superMethod));
break;
}
@@ -900,12 +881,11 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
return NullableNotNullManager.getInstance(method.getProject());
}
- private static @Nullable LocalQuickFix createFixForNonAnnotatedOverridesNotNull(PsiMethod method,
- PsiMethod superMethod) {
+ private static @Nullable LocalQuickFix createFixForNonAnnotatedOverridesNotNull(PsiMethod method) {
NullableNotNullManager nullableManager = getNullityManager(method);
return isAnnotatingApplicable(method, nullableManager.getDefaultNotNull())
? LocalQuickFix.from(AddAnnotationModCommandAction.createAddNotNullFix(method))
- : createChangeDefaultNotNullFix(nullableManager, superMethod);
+ : null;
}
private boolean isNullableOverridingNotNull(Annotated methodInfo, PsiMethod superMethod) {
@@ -975,9 +955,9 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
PsiParameter notNullSuper = findNotNullSuperForNonAnnotatedParameter(nullableManager, parameter, superParameters);
if (notNullSuper != null) {
- LocalQuickFix fix = isAnnotatingApplicable(parameter, nullableManager.getDefaultNotNull())
+ LocalQuickFix fix = isAnnotatingApplicable(parameter, nullableManager.getDefaultAnnotation(Nullability.NOT_NULL, parameter))
? LocalQuickFix.from(AddAnnotationModCommandAction.createAddNotNullFix(parameter))
- : createChangeDefaultNotNullFix(nullableManager, notNullSuper);
+ : null;
reportProblem(holder, nameIdentifier, fix,
"inspection.nullable.problems.parameter.overrides.NotNull", getPresentableAnnoName(notNullSuper));
}
@@ -1095,14 +1075,13 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
boolean checkReturnType = annotated.isDeclaredNotNull && !hasInheritableNotNull(method) && !(method.getReturnType() instanceof PsiPrimitiveType);
if (hasAnnotatedParameter || checkReturnType) {
- final String defaultNotNull = nullableManager.getDefaultNotNull();
- final boolean superMethodApplicable = isAnnotatingApplicable(method, defaultNotNull);
PsiMethod[] overridings =
OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
boolean methodQuickFixSuggested = false;
for (PsiMethod overriding : overridings) {
if (shouldSkipOverriderAsGenerated(overriding)) continue;
+ final String defaultNotNull = nullableManager.getDefaultAnnotation(Nullability.NOT_NULL, overriding);
if (!methodQuickFixSuggested
&& checkReturnType
&& !isNotNullNotInferred(overriding, false, false)
@@ -1115,8 +1094,8 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
final String[] annotationsToRemove = ArrayUtilRt.toStringArray(nullableManager.getNullables());
LocalQuickFix fix = isAnnotatingApplicable(overriding, defaultNotNull)
- ? new MyAnnotateMethodFix(defaultNotNull, annotationsToRemove)
- : superMethodApplicable ? null : createChangeDefaultNotNullFix(nullableManager, method);
+ ? new MyAnnotateMethodFix(Nullability.NOT_NULL, annotationsToRemove)
+ : null;
PsiElement psiElement = annotation;
if (!annotation.isPhysical() || !PsiTreeUtil.isAncestor(method, annotation, true)) {
@@ -1145,7 +1124,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
}
LocalQuickFix fix = isAnnotatingApplicable(parameter, defaultNotNull)
? new AnnotateOverriddenMethodParameterFix(Nullability.NOT_NULL, defaultNotNull)
- : createChangeDefaultNotNullFix(nullableManager, parameters[i]);
+ : null;
reportProblem(holder, psiElement, fix,
"nullable.stuff.problems.overridden.method.parameters.are.not.annotated");
parameterQuickFixSuggested[i] = true;
@@ -1183,28 +1162,12 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
(checkBases || info.getInheritedFrom() == null);
}
- private static LocalQuickFix createChangeDefaultNotNullFix(NullableNotNullManager nullableManager, PsiModifierListOwner modifierListOwner) {
- final PsiAnnotation annotation = findAnnotation(modifierListOwner, nullableManager.getNotNulls());
- if (annotation != null) {
- final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
- if (referenceElement != null) {
- JavaResolveResult resolveResult = referenceElement.advancedResolve(false);
- if (resolveResult.getElement() != null &&
- resolveResult.isValidResult() &&
- !nullableManager.getDefaultNotNull().equals(annotation.getQualifiedName())) {
- return new ChangeNullableDefaultsFix(annotation.getQualifiedName(), null);
- }
- }
- }
- return null;
- }
-
private static class MyAnnotateMethodFix implements LocalQuickFix {
- protected final String myAnnotation;
+ private final @NotNull Nullability myNullability;
private final String[] myAnnotationsToRemove;
- MyAnnotateMethodFix(@NotNull String fqn, String @NotNull ... annotationsToRemove) {
- myAnnotation = fqn;
+ MyAnnotateMethodFix(@NotNull Nullability nullability, String @NotNull ... annotationsToRemove) {
+ myNullability = nullability;
myAnnotationsToRemove = annotationsToRemove.length == 0 ? ArrayUtilRt.EMPTY_STRING_ARRAY : annotationsToRemove;
}
@@ -1225,10 +1188,14 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
PsiMethod method = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
if (method == null) return;
final List toAnnotate = new ArrayList<>();
+ NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
if (!AnnotateOverriddenMethodParameterFix.processModifiableInheritorsUnderProgress(method, (Consumer super PsiMethod>)psiMethod -> {
- if (isAnnotatingApplicable(psiMethod, myAnnotation) &&
- !isAnnotated(psiMethod, myAnnotation, CHECK_EXTERNAL | CHECK_TYPE)) {
+ NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(psiMethod);
+ if (info != null && info.getNullability() == myNullability && !info.isInferred() && info.getInheritedFrom() == null) return;
+ String annotation = manager.getDefaultAnnotation(myNullability, psiMethod);
+ if (isAnnotatingApplicable(psiMethod, annotation) &&
+ !isAnnotated(psiMethod, annotation, CHECK_EXTERNAL | CHECK_TYPE)) {
toAnnotate.add(psiMethod);
}
})) {
@@ -1237,7 +1204,8 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
FileModificationService.getInstance().preparePsiElementsForWrite(toAnnotate);
for (PsiMethod psiMethod : toAnnotate) {
- AddAnnotationPsiFix fix = new AddAnnotationPsiFix(myAnnotation, psiMethod, myAnnotationsToRemove);
+ String annotation = manager.getDefaultAnnotation(myNullability, psiMethod);
+ AddAnnotationPsiFix fix = new AddAnnotationPsiFix(annotation, psiMethod, myAnnotationsToRemove);
fix.invoke(psiMethod.getProject(), psiMethod.getContainingFile(), psiMethod, psiMethod);
}
UndoUtil.markPsiFileForUndo(method.getContainingFile());
@@ -1250,8 +1218,7 @@ public class NullableStuffInspectionBase extends AbstractBaseJavaLocalInspection
@Override
public @NotNull String getName() {
- return JavaAnalysisBundle.message("inspection.annotate.overridden.method.nullable.quickfix.name",
- ClassUtil.extractClassName(myAnnotation));
+ return JavaAnalysisBundle.message("inspection.annotate.overridden.method.nullable.quickfix.name");
}
}
}
diff --git a/java/java-analysis-impl/src/com/siyeh/ig/assignment/AssignmentToNullInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/assignment/AssignmentToNullInspection.java
index 7378b13ce89d..194971342187 100644
--- a/java/java-analysis-impl/src/com/siyeh/ig/assignment/AssignmentToNullInspection.java
+++ b/java/java-analysis-impl/src/com/siyeh/ig/assignment/AssignmentToNullInspection.java
@@ -15,6 +15,7 @@
*/
package com.siyeh.ig.assignment;
+import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.intention.AddAnnotationModCommandAction;
import com.intellij.codeInspection.LocalQuickFix;
@@ -58,7 +59,7 @@ public final class AssignmentToNullInspection extends BaseInspection {
return null;
}
final NullableNotNullManager manager = NullableNotNullManager.getInstance(target.getProject());
- String annotation = manager.getDefaultNullable();
+ String annotation = manager.getDefaultAnnotation(Nullability.NULLABLE, variable);
if (JavaPsiFacade.getInstance(variable.getProject()).findClass(annotation, variable.getResolveScope()) == null) {
return null;
}
diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java
index 5abae78932e9..12e872aa99c7 100644
--- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java
+++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java
@@ -591,11 +591,11 @@ public class ExtractMethodDialog extends RefactoringDialog implements AbstractEx
protected String getSignature() {
final @NonNls StringBuilder buffer = new StringBuilder();
- if (myGenerateAnnotations != null && myGenerateAnnotations.isSelected()) {
+ if (myGenerateAnnotations != null && myGenerateAnnotations.isSelected() && myNullability != null) {
final NullableNotNullManager nullManager = NullableNotNullManager.getInstance(myProject);
buffer.append("@");
buffer.append(
- StringUtil.getShortName(myNullability == Nullability.NULLABLE ? nullManager.getDefaultNullable() : nullManager.getDefaultNotNull()));
+ StringUtil.getShortName(nullManager.getDefaultAnnotation(myNullability, myTargetClass)));
buffer.append("\n");
}
final int declarationOffset = buffer.length();
diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java
index 844f05c20c06..588930a1bcdc 100644
--- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java
+++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java
@@ -711,8 +711,9 @@ public class ExtractMethodProcessor implements MatchProvider {
private Nullability initNullability() {
if (!PsiUtil.isAvailable(JavaFeature.ANNOTATIONS, myElements[0]) || PsiUtil.resolveClassInType(myReturnType) == null) return null;
final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject);
+ String defaultAnnotation = manager.getDefaultAnnotation(Nullability.NULLABLE, myElements[0]);
final PsiClass nullableAnnotationClass = JavaPsiFacade.getInstance(myProject)
- .findClass(manager.getDefaultNullable(), myElements[0].getResolveScope());
+ .findClass(defaultAnnotation, myElements[0].getResolveScope());
if (nullableAnnotationClass != null) {
final PsiElement elementInCopy = myTargetClass.getContainingFile().copy().findElementAt(myTargetClass.getTextOffset());
final PsiClass classCopy = PsiTreeUtil.getParentOfType(elementInCopy, PsiClass.class);
@@ -1076,9 +1077,9 @@ public class ExtractMethodProcessor implements MatchProvider {
NullableNotNullManager nullManager = NullableNotNullManager.getInstance(myProject);
switch (myNullability) {
case NOT_NULL ->
- updateAnnotations(newMethod, nullManager.getNullables(), nullManager.getDefaultNotNull(), nullManager.getNotNulls());
+ updateAnnotations(newMethod, nullManager.getNullables(), nullManager.getDefaultAnnotation(myNullability, myTargetClass), nullManager.getNotNulls());
case NULLABLE ->
- updateAnnotations(newMethod, nullManager.getNotNulls(), nullManager.getDefaultNullable(), nullManager.getNullables());
+ updateAnnotations(newMethod, nullManager.getNotNulls(), nullManager.getDefaultAnnotation(myNullability, myTargetClass), nullManager.getNullables());
default -> { }
}
}
@@ -1691,7 +1692,7 @@ public class ExtractMethodProcessor implements MatchProvider {
boolean isNotNull = nullability == Nullability.NOT_NULL;
final List toKeep = isNotNull ? notNullAnnotations : nullableAnnotations;
final List toRemove = isNotNull ? nullableAnnotations : notNullAnnotations;
- final String toAdd = isNotNull ? nullabilityManager.getDefaultNotNull() : nullabilityManager.getDefaultNullable();
+ final String toAdd = nullabilityManager.getDefaultAnnotation(nullability, parm);
updateAnnotations(parm, toRemove, toAdd, toKeep);
}
}
diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt
index 0905af3cea5f..f250e25c494c 100644
--- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt
+++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethod/newImpl/ExtractMethodHelper.kt
@@ -55,8 +55,9 @@ object ExtractMethodHelper {
fun isNullabilityAvailable(extractOptions: ExtractOptions): Boolean {
val project = extractOptions.project
- val scope = extractOptions.elements.first().resolveScope
- val defaultNullable = NullableNotNullManager.getInstance(project).defaultNullable
+ val element = extractOptions.elements.first()
+ val scope = element.resolveScope
+ val defaultNullable = NullableNotNullManager.getInstance(project).getDefaultAnnotation(Nullability.NULLABLE, element)
val annotationClass = JavaPsiFacade.getInstance(project).findClass(defaultNullable, scope)
return annotationClass != null
}
@@ -119,12 +120,9 @@ object ExtractMethodHelper {
fun addNullabilityAnnotation(typeElement: PsiTypeElement?, nullability: Nullability) {
if (typeElement == null) return
+ if (nullability == Nullability.UNKNOWN) return
val nullabilityManager = NullableNotNullManager.getInstance(typeElement.project)
- val annotation = when (nullability) {
- Nullability.NOT_NULL -> nullabilityManager.defaultNotNull
- Nullability.NULLABLE -> nullabilityManager.defaultNullable
- else -> return
- }
+ val annotation = nullabilityManager.getDefaultAnnotation(nullability, typeElement)
val annotationElement = AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(annotation, PsiNameValuePair.EMPTY_ARRAY, typeElement)
if (annotationElement != null) {
JavaCodeStyleManager.getInstance(typeElement.project).shortenClassReferences(annotationElement)
diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java
index 142b91423708..3f2002b0f3fc 100644
--- a/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java
+++ b/java/java-impl-refactorings/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java
@@ -3,6 +3,7 @@
package com.intellij.refactoring.extractMethodObject;
import com.intellij.application.options.CodeStyle;
+import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.generation.GenerateMembersUtil;
import com.intellij.java.refactoring.JavaRefactoringBundle;
@@ -630,7 +631,7 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor {
final PsiModifierList modifierList = field.getModifierList();
LOG.assertTrue(modifierList != null);
if (NullableNotNullManager.isNullable(parameter)) {
- final String annotationName = NullableNotNullManager.getInstance(myProject).getDefaultNullable();
+ final String annotationName = NullableNotNullManager.getInstance(myProject).getDefaultAnnotation(Nullability.NULLABLE, field);
modifierList.addAfter(myElementFactory.createAnnotationFromText("@" + annotationName, field), null);
}
modifierList.setModifierProperty(PsiModifier.FINAL, isFinal);
diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl-refactorings/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java
index 3e21caeb2ec2..521b2b33dbf6 100644
--- a/java/java-impl-refactorings/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java
+++ b/java/java-impl-refactorings/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java
@@ -33,6 +33,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
+import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
@@ -561,6 +562,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase {
else {
final PsiFile copy = PsiFileFactory.getInstance(project)
.createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false);
+ ((PsiFileImpl)copy).setOriginalFile(file);
final TextRange range = new TextRange(elements[0].getTextRange().getStartOffset(),
elements[elements.length - 1].getTextRange().getEndOffset());
final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, range.getStartOffset(), range.getEndOffset());
diff --git a/java/java-impl/src/com/intellij/codeInsight/MakeInferredAnnotationExplicit.java b/java/java-impl/src/com/intellij/codeInsight/MakeInferredAnnotationExplicit.java
index b7e4209af9e0..2b5cb5980a99 100644
--- a/java/java-impl/src/com/intellij/codeInsight/MakeInferredAnnotationExplicit.java
+++ b/java/java-impl/src/com/intellij/codeInsight/MakeInferredAnnotationExplicit.java
@@ -57,7 +57,7 @@ public final class MakeInferredAnnotationExplicit extends BaseIntentionAction {
List annotations = getAnnotationsToAdd(owner);
if (!annotations.isEmpty()) {
String presentation = StreamEx.of(annotations)
- .map(MakeInferredAnnotationExplicit::getAnnotationPresentation)
+ .map(annotation -> getAnnotationPresentation(file, annotation))
.joining(" ");
setText(CommonQuickFixBundle.message("fix.insert.x", presentation));
return true;
@@ -76,8 +76,8 @@ public final class MakeInferredAnnotationExplicit extends BaseIntentionAction {
return ContainerUtil.filter(annotations, anno -> !isJetBrainsAnnotation(anno));
}
- private static @NotNull String getAnnotationPresentation(PsiAnnotation annotation) {
- final PsiJavaCodeReferenceElement nameRef = correctAnnotation(annotation).getNameReferenceElement();
+ private static @NotNull String getAnnotationPresentation(@NotNull PsiFile file, PsiAnnotation annotation) {
+ final PsiJavaCodeReferenceElement nameRef = correctAnnotation(file, annotation).getNameReferenceElement();
final String name = nameRef != null ? nameRef.getReferenceName() : annotation.getQualifiedName();
return "@" + name + annotation.getParameterList().getText();
}
@@ -160,11 +160,12 @@ public final class MakeInferredAnnotationExplicit extends BaseIntentionAction {
}
private @Unmodifiable @NotNull List getAnnotationsToAdd(@NotNull PsiModifierListOwner owner) {
+ PsiFile file = owner.getContainingFile();
List allAnnotations = StreamEx.of(InferredAnnotationsManager.getInstance(owner.getProject()).findInferredAnnotations(owner))
.remove(DefaultInferredAnnotationProvider::isExperimentalInferredAnnotation)
- .map(MakeInferredAnnotationExplicit::correctAnnotation)
+ .map(annotation -> correctAnnotation(file, annotation))
.toList();
- return filterAnnotations(owner.getContainingFile(), allAnnotations);
+ return filterAnnotations(file, allAnnotations);
}
private static void doMakeAnnotationExplicit(@NotNull Project project, @NotNull PsiModifierListOwner owner, @NotNull List annotations) {
@@ -179,15 +180,15 @@ public final class MakeInferredAnnotationExplicit extends BaseIntentionAction {
}
}
- private static @NotNull PsiAnnotation correctAnnotation(@NotNull PsiAnnotation annotation) {
+ private static @NotNull PsiAnnotation correctAnnotation(@NotNull PsiFile file, @NotNull PsiAnnotation annotation) {
Project project = annotation.getProject();
NullableNotNullManager nnnm = NullableNotNullManager.getInstance(project);
PsiAnnotation corrected = null;
if (annotation.hasQualifiedName(AnnotationUtil.NULLABLE)) {
- corrected = createAnnotation(project, nnnm.getDefaultNullable());
+ corrected = createAnnotation(project, nnnm.getDefaultAnnotation(Nullability.NULLABLE, file));
}
else if (annotation.hasQualifiedName(AnnotationUtil.NOT_NULL)) {
- corrected = createAnnotation(project, nnnm.getDefaultNotNull());
+ corrected = createAnnotation(project, nnnm.getDefaultAnnotation(Nullability.NOT_NULL, file));
}
return corrected != null ? corrected : annotation;
}
diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java
index f97b948c95a1..5f8900c512ab 100644
--- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java
+++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java
@@ -11,12 +11,15 @@ import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.JavaBundle;
import com.intellij.java.codeserver.core.JavaPsiModuleUtil;
import com.intellij.java.library.JavaLibraryModificationTracker;
+import com.intellij.java.library.JavaLibraryUtil;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.extensions.ExtensionPointListener;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
@@ -27,6 +30,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex;
+import com.intellij.psi.impl.source.DummyHolder;
import com.intellij.psi.search.DelegatingGlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValueProvider.Result;
@@ -61,6 +65,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
private List myDefaultAll;
public String myDefaultNullable = NULLABLE;
public String myDefaultNotNull = NOT_NULL;
+ public boolean myOrdered = false;
public final JDOMExternalizableStringList myNullables = new JDOMExternalizableStringList();
public final JDOMExternalizableStringList myNotNulls = new JDOMExternalizableStringList();
private List myInstrumentedNotNulls = List.of(NOT_NULL);
@@ -85,11 +90,11 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
private void updateDefaults() {
myAnnotationSupports = AnnotationPackageSupport.EP_NAME.getExtensionList();
myDefaultNullables = StreamEx.of(myAnnotationSupports)
- .cross(s -> s.getNullabilityAnnotations(Nullability.NULLABLE).stream()).invert().toMap();
+ .cross(s -> s.getNullabilityAnnotations(Nullability.NULLABLE).stream()).invert().toCustomMap(LinkedHashMap::new);
myDefaultNotNulls = StreamEx.of(myAnnotationSupports)
- .cross(s -> s.getNullabilityAnnotations(Nullability.NOT_NULL).stream()).invert().toMap();
+ .cross(s -> s.getNullabilityAnnotations(Nullability.NOT_NULL).stream()).invert().toCustomMap(LinkedHashMap::new);
myDefaultUnknowns = StreamEx.of(myAnnotationSupports)
- .cross(s -> s.getNullabilityAnnotations(Nullability.UNKNOWN).stream()).invert().toMap();
+ .cross(s -> s.getNullabilityAnnotations(Nullability.UNKNOWN).stream()).invert().toCustomMap(LinkedHashMap::new);
myDefaultAll = StreamEx.of(myDefaultNullables, myDefaultNotNulls, myDefaultUnknowns).toFlatList(Map::keySet);
normalizeDefaults();
}
@@ -108,6 +113,39 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
normalizeDefaults();
}
+ @Override
+ public @NotNull String getDefaultAnnotation(@NotNull Nullability nullability, @NotNull PsiElement context) {
+ Collection annotations = switch(nullability) {
+ case NOT_NULL -> myNotNulls;
+ case NULLABLE -> myNullables;
+ case UNKNOWN -> myDefaultUnknowns.keySet();
+ };
+ PsiFile containingFile = context.getContainingFile();
+ if (containingFile instanceof DummyHolder) {
+ PsiElement element = containingFile.getContext();
+ if (element != null) {
+ containingFile = element.getContainingFile();
+ }
+ }
+ PsiFile file = containingFile.getOriginalFile();
+ Module module = ModuleUtilCore.findModuleForFile(file);
+ if (module == null) return getDefaultAnnotation(nullability);
+ for (String annotation : annotations) {
+ if (JavaLibraryUtil.hasLibraryClass(module, annotation)) {
+ return annotation;
+ }
+ }
+ return getDefaultAnnotation(nullability);
+ }
+
+ private @NotNull String getDefaultAnnotation(@NotNull Nullability nullability) {
+ return switch (nullability) {
+ case NOT_NULL -> getDefaultNotNull();
+ case NULLABLE -> getDefaultNullable();
+ case UNKNOWN -> AnnotationUtil.UNKNOWN_NULLABILITY;
+ };
+ }
+
@Override
public @NotNull String getDefaultNullable() {
return myDefaultNullable;
@@ -227,15 +265,16 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
private boolean hasDefaultValues() {
return NOT_NULL.equals(myDefaultNotNull) &&
NULLABLE.equals(myDefaultNullable) &&
- new HashSet<>(myNullables).equals(myDefaultNullables.keySet()) &&
- new HashSet<>(myNotNulls).equals(myDefaultNotNulls.keySet());
+ myNullables.equals(new ArrayList<>(myDefaultNullables.keySet())) &&
+ myNotNulls.equals(new ArrayList<>(myDefaultNotNulls.keySet()));
}
-
+
@Override
public void loadState(@NotNull Element state) {
try {
DefaultJDOMExternalizer.readExternal(this, state);
normalizeDefaults();
+ migrateSettings();
}
catch (InvalidDataException e) {
LOG.error(e);
@@ -250,6 +289,31 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement
}
}
+ /**
+ * Convert old settings when the order of annotations didn't matter:
+ * 1. Put the default annotation on top, unless it's JetBrains default annotation
+ * 2. Next, put all the custom annotations
+ * 3. Next, put all annotations from {@link AnnotationPackageSupport} extensions in the order they are provided
+ */
+ private void migrateSettings() {
+ if (!myOrdered) {
+ myOrdered = true;
+ // Set the default order and put default to the front
+ myNullables.removeAll(myDefaultNullables.keySet());
+ myNullables.addAll(myDefaultNullables.keySet());
+ if (!myDefaultNullable.equals(NULLABLE)) {
+ myNullables.remove(myDefaultNullable);
+ myNullables.add(0, myDefaultNullable);
+ }
+ myNotNulls.removeAll(myDefaultNotNulls.keySet());
+ myNotNulls.addAll(myDefaultNotNulls.keySet());
+ if (!myDefaultNotNull.equals(NOT_NULL)) {
+ myNotNulls.remove(myDefaultNotNull);
+ myNotNulls.add(0, myDefaultNotNull);
+ }
+ }
+ }
+
private void normalizeDefaults() {
myNotNulls.removeAll(myDefaultNullables.keySet());
myNullables.removeAll(myDefaultNotNulls.keySet());
diff --git a/java/java-impl/src/com/intellij/codeInsight/annoPackages/AnnotationPackageSupport.java b/java/java-impl/src/com/intellij/codeInsight/annoPackages/AnnotationPackageSupport.java
index 60d75c1464b1..951c7d42a29b 100644
--- a/java/java-impl/src/com/intellij/codeInsight/annoPackages/AnnotationPackageSupport.java
+++ b/java/java-impl/src/com/intellij/codeInsight/annoPackages/AnnotationPackageSupport.java
@@ -3,15 +3,19 @@ package com.intellij.codeInsight.annoPackages;
import com.intellij.codeInsight.ContextNullabilityInfo;
import com.intellij.codeInsight.Nullability;
+import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
- * Support for custom annotation packages
+ * Support for custom annotation packages.
+ * The registration order of extension matters, as the annotations returned from the first {@link #getNullabilityAnnotations(Nullability)}
+ * will be preferred by default.
*/
public interface AnnotationPackageSupport {
ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.lang.jvm.annotationPackageSupport");
@@ -32,7 +36,9 @@ public interface AnnotationPackageSupport {
/**
* @param nullability desired nullability
- * @return list of explicit annotations which denote given nullability (and may denote additional semantics)
+ * @return list of explicit annotations which denote given nullability (and may denote additional semantics).
+ * The annotation returned first will be preferred by default
+ * in {@link NullableNotNullManager#getDefaultAnnotation(Nullability, PsiElement)}.
*/
default @NotNull List getNullabilityAnnotations(@NotNull Nullability nullability) {
return Collections.emptyList();
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java
index 8169958e1192..481189cdb955 100644
--- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java
+++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java
@@ -3,6 +3,7 @@ package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.ExpectedTypesProvider;
+import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
@@ -109,7 +110,7 @@ class JavaClassNameInsertHandler implements InsertHandler toRemove) {
+ new AddAnnotationPsiFix(fqn, element, ArrayUtil.toStringArray(toRemove)).applyFix();
}
public int getCount() {
return myNotNullSet.size() + myNullableSet.size();
}
- public static boolean apply(@NotNull Project project, @NotNull NullableNotNullManager manager, UsageInfo info) {
+ public static boolean apply(@NotNull NullableNotNullManager manager, UsageInfo info) {
if (info instanceof NullableUsageInfo) {
return annotateNullable(manager, (PsiModifierListOwner)info.getElement());
}
diff --git a/java/java-impl/src/com/siyeh/ig/bugs/ReturnNullInspection.java b/java/java-impl/src/com/siyeh/ig/bugs/ReturnNullInspection.java
index 843c431f6cb6..f47025c3ab5a 100644
--- a/java/java-impl/src/com/siyeh/ig/bugs/ReturnNullInspection.java
+++ b/java/java-impl/src/com/siyeh/ig/bugs/ReturnNullInspection.java
@@ -1,7 +1,6 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.bugs;
-import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInsight.NullabilityAnnotationInfo;
import com.intellij.codeInsight.NullableNotNullManager;
@@ -20,7 +19,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.controlFlow.DefUseUtil;
import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.util.ArrayUtilRt;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
@@ -79,10 +77,6 @@ public final class ReturnNullInspection extends BaseInspection {
@Override
protected @Nullable LocalQuickFix buildFix(Object... infos) {
final PsiElement elt = (PsiElement)infos[0];
- if (!AnnotationUtil.isAnnotatingApplicable(elt)) {
- return null;
- }
-
final PsiMethod method = PsiTreeUtil.getParentOfType(elt, PsiMethod.class, false, PsiLambdaExpression.class);
if (method == null) return null;
final PsiType type = method.getReturnType();
@@ -91,9 +85,7 @@ public final class ReturnNullInspection extends BaseInspection {
return new ReplaceWithEmptyOptionalFix(((PsiClassType)type).rawType().getCanonicalText());
}
- final NullableNotNullManager manager = NullableNotNullManager.getInstance(elt.getProject());
- return LocalQuickFix.from(new AddAnnotationModCommandAction(manager.getDefaultNullable(), method,
- ArrayUtilRt.toStringArray(manager.getNotNulls())));
+ return LocalQuickFix.from(AddAnnotationModCommandAction.createAddNullableFix(method));
}
@Override
diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java
index 83463bbdae35..df85d25aabf3 100644
--- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java
+++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java
@@ -382,10 +382,6 @@ public class AnnotationUtil {
return false;
}
- public static boolean isAnnotatingApplicable(@NotNull PsiElement elt) {
- return isAnnotatingApplicable(elt, NullableNotNullManager.getInstance(elt.getProject()).getDefaultNullable());
- }
-
public static boolean isAnnotatingApplicable(@NotNull PsiElement elt, @NotNull String annotationFQN) {
final Project project = elt.getProject();
return PsiUtil.isAvailable(JavaFeature.ANNOTATIONS, elt) &&
diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
index c1c286891640..b7b75a1f60b9 100644
--- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
+++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java
@@ -57,12 +57,45 @@ public abstract class NullableNotNullManager {
public abstract void setNullables(String @NotNull ... annotations);
+ /**
+ * @param nullability wanted nullability
+ * @param context PSI context
+ * @return the best suitable annotation to insert in a specified context
+ */
+ public abstract @NotNull String getDefaultAnnotation(@NotNull Nullability nullability, @NotNull PsiElement context);
+
+ /**
+ * @return default nullable annotation,
+ * used for external and inferred annotations, or as a fallback when no other annotation is available
+ * @see #getDefaultAnnotation(Nullability, PsiElement)
+ */
public abstract @NotNull String getDefaultNullable();
+ /**
+ * Sets the default nullable annotation, used for external and inferred annotations,
+ * or as a fallback when no other annotation is available.
+ *
+ * @param defaultNullable new default nullable annotation
+ * @see #getDefaultNullable()
+ */
public abstract void setDefaultNullable(@NotNull String defaultNullable);
+ /**
+ * @return default not-null annotation,
+ * used for external and inferred annotations, or as a fallback when no other annotation is available
+ * @see #getDefaultAnnotation(Nullability, PsiElement)
+ */
public abstract @NotNull String getDefaultNotNull();
+ /**
+ * Sets the default not-null annotation, used for external and inferred annotations,
+ * or as a fallback when no other annotation is available.
+ *
+ * @param defaultNotNull new default nullable annotation
+ * @see #getDefaultNotNull()
+ */
+ public abstract void setDefaultNotNull(@NotNull String defaultNotNull);
+
public void copyNotNullAnnotation(@NotNull PsiModifierListOwner original, @NotNull PsiModifierListOwner generated) {
NullabilityAnnotationInfo info = findOwnNullabilityInfo(original);
if (info == null || info.getNullability() != Nullability.NOT_NULL) return;
@@ -110,8 +143,6 @@ public abstract class NullableNotNullManager {
return null;
}
- public abstract void setDefaultNotNull(@NotNull String defaultNotNull);
-
/**
* Returns own nullability annotation info for given element. Returned annotation is not inherited and
* not container annotation for class/package. Still it could be inferred or external.
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenWithInferred.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenWithInferred.java
index 99869df0bb52..6bd3f49d8599 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenWithInferred.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/afterOverriddenWithInferred.java
@@ -1,4 +1,4 @@
-// "Annotate overriding methods as '@NotNull'" "true"
+// "Add missing nullability annotation to overriding methods" "true"
import org.jetbrains.annotations.NotNull;
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenWithInferred.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenWithInferred.java
index 15d60a531b0b..0b9d188a1004 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenWithInferred.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod/beforeOverriddenWithInferred.java
@@ -1,4 +1,4 @@
-// "Annotate overriding methods as '@NotNull'" "true"
+// "Add missing nullability annotation to overriding methods" "true"
import org.jetbrains.annotations.NotNull;
diff --git a/java/java-tests/testData/refactoring/extractMethod/InterfaceMethodVisibility_after.java b/java/java-tests/testData/refactoring/extractMethod/InterfaceMethodVisibility_after.java
index 96473d5f021e..86902293cfe5 100644
--- a/java/java-tests/testData/refactoring/extractMethod/InterfaceMethodVisibility_after.java
+++ b/java/java-tests/testData/refactoring/extractMethod/InterfaceMethodVisibility_after.java
@@ -1,9 +1,12 @@
+import org.jetbrains.annotations.NotNull;
+
interface TweetParser {
static String getTweetMessageFrom(String fullTweet) {
String fieldName = "\"text\":\"";
return newMethod(fullTweet, fieldName);
}
+ @NotNull
static String newMethod(String fullTweet, String fieldName) {
int indexOfField = fullTweet.indexOf(fieldName) + fieldName.length();
int indexOfEndOfField = fullTweet.indexOf("\"", indexOfField);
diff --git a/java/java-tests/testData/refactoring/extractMethodNew/InterfaceMethodVisibility_after.java b/java/java-tests/testData/refactoring/extractMethodNew/InterfaceMethodVisibility_after.java
index 96473d5f021e..86902293cfe5 100644
--- a/java/java-tests/testData/refactoring/extractMethodNew/InterfaceMethodVisibility_after.java
+++ b/java/java-tests/testData/refactoring/extractMethodNew/InterfaceMethodVisibility_after.java
@@ -1,9 +1,12 @@
+import org.jetbrains.annotations.NotNull;
+
interface TweetParser {
static String getTweetMessageFrom(String fullTweet) {
String fieldName = "\"text\":\"";
return newMethod(fullTweet, fieldName);
}
+ @NotNull
static String newMethod(String fullTweet, String fieldName) {
int indexOfField = fullTweet.indexOf(fieldName) + fieldName.length();
int indexOfEndOfField = fullTweet.indexOf("\"", indexOfField);
diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java
index 2069c3b69272..cced6c439b01 100644
--- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java
@@ -38,7 +38,7 @@ public class AnnotateMethodInGeneratedFilesTest extends LightJavaCodeInsightFixt
}
public void testAnnotateOverriddenMethod() {
- doTest("Annotate overriding methods");
+ doTest("Add missing nullability annotation");
}
public void testAnnotateOverriddenParameters() {
diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodTest.java
index dc92c8bcc2e2..ded9534ece55 100644
--- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodTest.java
@@ -7,10 +7,8 @@ import com.intellij.codeInsight.annoPackages.AnnotationPackageSupport;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.nullable.NullableStuffInspection;
-import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.extensions.LoadingOrder;
import com.intellij.pom.java.LanguageLevel;
-import com.intellij.testFramework.ServiceContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -25,7 +23,6 @@ public class AnnotateMethodTest extends LightQuickFixParameterizedTestCase {
protected void setUp() throws Exception {
super.setUp();
if (getTestName(false).contains("TypeUse")) {
- NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject());
AnnotationPackageSupport mySupport = new AnnotationPackageSupport() {
@Override
public @NotNull List getNullabilityAnnotations(@NotNull Nullability nullability) {
@@ -40,17 +37,10 @@ public class AnnotateMethodTest extends LightQuickFixParameterizedTestCase {
public boolean isTypeUseAnnotationLocationRestricted() {
return true;
}
- };
- ServiceContainerUtil.registerExtension(ApplicationManager.getApplication(), AnnotationPackageSupport.EP_NAME, mySupport, getTestRootDisposable());
- String prevNullable = nnnManager.getDefaultNullable();
- String prevNotNull = nnnManager.getDefaultNotNull();
- nnnManager.setDefaultNotNull("typeUse.NotNull");
- nnnManager.setDefaultNullable("typeUse.Nullable");
- Disposer.register(getTestRootDisposable(), () -> {
- nnnManager.setDefaultNotNull(prevNotNull);
- nnnManager.setDefaultNullable(prevNullable);
- });
+ };
+ AnnotationPackageSupport.EP_NAME.getPoint().registerExtension(mySupport, LoadingOrder.FIRST, getTestRootDisposable());
}
+ NullableNotNullManager.getInstance(getProject()).setNotNulls(); // to normalize defaults
}
@Override
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.java
index 758dde125a54..785ec7a55f4e 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/MakeInferredAnnotationExplicitTest.java
@@ -1,6 +1,5 @@
package com.intellij.java.codeInsight;
-import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
@@ -50,7 +49,6 @@ public class MakeInferredAnnotationExplicitTest extends LightJavaCodeInsightFixt
public void test_custom_notNull() {
myFixture.addClass("package foo; public @interface MyNotNull {}");
NullableNotNullManager.getInstance(getProject()).setNotNulls("foo.MyNotNull");
- NullableNotNullManager.getInstance(getProject()).setDefaultNotNull("foo.MyNotNull");
myFixture.configureByText("a.java", """
class Foo {
@@ -75,7 +73,6 @@ public class MakeInferredAnnotationExplicitTest extends LightJavaCodeInsightFixt
public void test_type_use() {
myFixture.addClass("package foo; import java.lang.annotation.*;@Target(ElementType.TYPE_USE)public @interface MyNotNull {}");
NullableNotNullManager.getInstance(getProject()).setNotNulls("foo.MyNotNull");
- NullableNotNullManager.getInstance(getProject()).setDefaultNotNull("foo.MyNotNull");
myFixture.configureByText("a.java", """
class Foo {
static void foo(String[] array) {
@@ -96,7 +93,6 @@ public class MakeInferredAnnotationExplicitTest extends LightJavaCodeInsightFixt
public void test_type_use_qualified_type() {
myFixture.addClass("package foo; import java.lang.annotation.*;@Target(ElementType.TYPE_USE)public @interface MyNotNull {}");
NullableNotNullManager.getInstance(getProject()).setNotNulls("foo.MyNotNull");
- NullableNotNullManager.getInstance(getProject()).setDefaultNotNull("foo.MyNotNull");
myFixture.configureByText("a.java", """
import org.jetbrains.annotations.Contract;
import foo.MyNotNull;
@@ -125,7 +121,6 @@ public class MakeInferredAnnotationExplicitTest extends LightJavaCodeInsightFixt
protected void tearDown() throws Exception {
try {
NullableNotNullManager.getInstance(getProject()).setNotNulls(ArrayUtil.EMPTY_STRING_ARRAY);
- NullableNotNullManager.getInstance(getProject()).setDefaultNotNull(AnnotationUtil.NOT_NULL);
}
catch (Throwable e) {
addSuppressedException(e);
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/OverrideImplement15Test.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/OverrideImplement15Test.java
index 2fb1da3f8069..0d07049ed52d 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInsight/OverrideImplement15Test.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/OverrideImplement15Test.java
@@ -64,24 +64,16 @@ public class OverrideImplement15Test extends LightJavaCodeInsightTestCase {
private void doCustomNotNullAnnotations() {
NullableNotNullManager nullableNotNullManager = NullableNotNullManager.getInstance(getProject());
- String defaultNotNull = nullableNotNullManager.getDefaultNotNull();
List notNulls = nullableNotNullManager.getNotNulls();
- String defaultNullable = nullableNotNullManager.getDefaultNullable();
List nullables = nullableNotNullManager.getNullables();
try {
- nullableNotNullManager.setNotNulls(ArrayUtil.append(ArrayUtil.toStringArray(notNulls),"p.NN"));
- nullableNotNullManager.setDefaultNotNull("p.NN");
-
- nullableNotNullManager.setNullables(ArrayUtil.append(ArrayUtil.toStringArray(nullables),"p.N"));
- nullableNotNullManager.setDefaultNullable("p.N");
+ nullableNotNullManager.setNotNulls(ArrayUtil.prepend("p.NN",ArrayUtil.toStringArray(notNulls)));
+ nullableNotNullManager.setNullables(ArrayUtil.prepend("p.N",ArrayUtil.toStringArray(nullables)));
doTest(true, true);
}
finally {
- nullableNotNullManager.setDefaultNotNull(defaultNotNull);
nullableNotNullManager.setNotNulls(ArrayUtil.toStringArray(notNulls));
-
- nullableNotNullManager.setDefaultNullable(defaultNullable);
nullableNotNullManager.setNullables(ArrayUtil.toStringArray(nullables));
}
}
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java
index b022e0f4ab6d..036fab92629b 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspection8Test.java
@@ -2,11 +2,9 @@
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
-import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInspection.dataFlow.ConstantValueInspection;
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
-import com.intellij.openapi.util.Disposer;
import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull;
@@ -306,24 +304,10 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
}
public void testArrayTypeParameterInference() {
setupTypeUseAnnotations("typeUse", myFixture);
- NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject());
- nnnManager.setDefaultNotNull("typeUse.NotNull");
- nnnManager.setDefaultNullable("typeUse.Nullable");
- Disposer.register(getTestRootDisposable(), () -> {
- nnnManager.setDefaultNotNull(AnnotationUtil.NOT_NULL);
- nnnManager.setDefaultNullable(AnnotationUtil.NULLABLE);
- });
doTest();
}
public void testArrayTypeParameterInferenceAmbiguous() {
setupAmbiguousAnnotations("ambiguous", myFixture);
- NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject());
- nnnManager.setDefaultNotNull("ambiguous.NotNull");
- nnnManager.setDefaultNullable("ambiguous.Nullable");
- Disposer.register(getTestRootDisposable(), () -> {
- nnnManager.setDefaultNotNull(AnnotationUtil.NOT_NULL);
- nnnManager.setDefaultNullable(AnnotationUtil.NULLABLE);
- });
doTest();
}
public void testGuavaFunction() {
diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java
index 0c2c821c2e7d..054449429f88 100644
--- a/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/NullableStuffInspectionTest.java
@@ -384,15 +384,7 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
public void testQuickFixOnTypeArgument() {
setupTypeUseAnnotations("typeUse", myFixture);
- NullableNotNullManager manager = NullableNotNullManager.getInstance(getProject());
- String oldDefault = manager.getDefaultNotNull();
- try {
- manager.setDefaultNotNull("typeUse.NotNull");
- doTestWithFix("Annotate as '@NotNull'");
- }
- finally {
- manager.setDefaultNotNull(oldDefault);
- }
+ doTestWithFix("Annotate as '@NotNull'");
}
public void testRemoveAnnotationWithImportQuickFix() {
@@ -401,15 +393,7 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
public void testQuickFixOnTypeArgumentNullable() {
setupTypeUseAnnotations("typeUse", myFixture);
- NullableNotNullManager manager = NullableNotNullManager.getInstance(getProject());
- String oldDefault = manager.getDefaultNotNull();
- try {
- manager.setDefaultNotNull("typeUse.NotNull");
- doTestWithFix("Annotate as '@NotNull'");
- }
- finally {
- manager.setDefaultNotNull(oldDefault);
- }
+ doTestWithFix("Annotate as '@NotNull'");
}
public void testCheckerDefaultTypeUseRecursiveGeneric() {
@@ -463,10 +447,6 @@ public class NullableStuffInspectionTest extends LightJavaCodeInsightFixtureTest
}
public void testIncompatibleConstructors() {
- final NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject());
- String nullable = nnnManager.getDefaultNullable();
- nnnManager.setDefaultNullable("org.jspecify.annotations.Nullable");
- Disposer.register(myFixture.getTestRootDisposable(), () -> nnnManager.setDefaultNullable(nullable));
addJSpecifyNullMarked(myFixture);
setupTypeUseAnnotations("org.jspecify.annotations", myFixture);
doTest();
diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties
index c51b94bf4f89..b0a3bcc1d183 100644
--- a/java/openapi/resources/messages/JavaBundle.properties
+++ b/java/openapi/resources/messages/JavaBundle.properties
@@ -1082,7 +1082,9 @@ node.call.hierarchy.unknown.jsp=unknown jsp
node.hierarchy.java.lang.object=All classes are derived from java.lang.Object
notification.navigation.to.overriding.classes=Navigation to overriding classes is not possible during index update
notification.navigation.to.overriding.methods=Navigation to overriding methods is not possible during index update
+annotations.panel.title={0} annotations:
nullable.notnull.annotations.panel.title={0} annotations:
+nullable.notnull.annotations.panel.description=The first from this list available in the project will be suggested as a quick-fix
nullable.notnull.annotations.runtime.instrumentation.tooltip=Add runtime assertions for notnull-annotated methods and parameters
nullable.notnull.configuration.dialog.title=Nullable/NotNull Configuration
nullable.notnull.annotation.used.label=Annotation used for code generation:
diff --git a/java/openapi/src/com/intellij/codeInsight/AnnotationsPanel.java b/java/openapi/src/com/intellij/codeInsight/AnnotationsPanel.java
index 5eeeb11bf542..3e2bf7a8c8dd 100644
--- a/java/openapi/src/com/intellij/codeInsight/AnnotationsPanel.java
+++ b/java/openapi/src/com/intellij/codeInsight/AnnotationsPanel.java
@@ -1,14 +1,10 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight;
-import com.intellij.core.JavaPsiBundle;
import com.intellij.ide.util.ClassFilter;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeClassChooserFactory;
import com.intellij.java.JavaBundle;
-import com.intellij.openapi.application.ModalityState;
-import com.intellij.openapi.application.ReadAction;
-import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.NlsSafe;
@@ -18,23 +14,20 @@ import com.intellij.ui.*;
import com.intellij.ui.dsl.builder.DslComponentProperty;
import com.intellij.ui.dsl.builder.VerticalComponentGap;
import com.intellij.ui.table.JBTable;
-import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.ui.JBDimension;
import com.intellij.util.ui.UI;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
-import javax.swing.table.*;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableRowSorter;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
public class AnnotationsPanel {
private final Project myProject;
@@ -47,32 +40,16 @@ public class AnnotationsPanel {
public AnnotationsPanel(Project project,
@NonNls String name,
- String defaultAnnotation,
List annotations,
- List defaultAnnotations,
- Set checkedAnnotations,
- boolean showInstrumentationOptions,
- boolean showDefaultActions) {
- this(project, new SimpleAnnotationPanelModel(name, defaultAnnotation, annotations, defaultAnnotations, checkedAnnotations), showInstrumentationOptions, showDefaultActions);
- }
-
- public AnnotationsPanel(Project project,
- @NotNull AnnotationPanelModel model,
- boolean showInstrumentationOptions,
- boolean showDefaultActions) {
+ List defaultAnnotations) {
myProject = project;
- myDefaultAnnotations = new HashSet<>(model.getDefaultAnnotations());
+ myDefaultAnnotations = new HashSet<>(defaultAnnotations);
- List annotations = model.getAnnotations();
myCombo = new ComboBox<>(annotations.stream().sorted().toArray(String[]::new));
- String defaultAnnotation = model.getDefaultAnnotation();
- if (!annotations.contains(defaultAnnotation)) {
- addAnnotationToCombo(defaultAnnotation);
+ if (!annotations.contains("")) {
+ addAnnotationToCombo("");
}
- if (model.hasAdvancedAnnotations()) {
- loadAdvancedAnnotations(model);
- }
- myCombo.setSelectedItem(defaultAnnotation);
+ myCombo.setSelectedItem("");
myTableModel = new DefaultTableModel() {
@Override
@@ -80,9 +57,9 @@ public class AnnotationsPanel {
return column == 1;
}
};
- myTableModel.setColumnCount(showInstrumentationOptions ? 2 : 1);
+ myTableModel.setColumnCount(1);
for (String annotation : annotations) {
- addRow(annotation, model.getCheckedAnnotations().contains(annotation));
+ addRow(annotation);
}
DefaultTableColumnModel columnModel = new DefaultTableColumnModel();
@@ -107,41 +84,11 @@ public class AnnotationsPanel {
mySorter = new TableRowSorter<>(myTableModel);
mySorter.setSortKeys(List.of(new RowSorter.SortKey(0, SortOrder.ASCENDING)));
myTable.setRowSorter(mySorter);
- if (!showInstrumentationOptions) myTable.setTableHeader(null);
+ myTable.setTableHeader(null);
mySorter.sort();
- if (showInstrumentationOptions) {
- columnModel.getColumn(0).setHeaderValue(JavaPsiBundle.message("node.annotation.tooltip"));
-
- TableColumn checkColumn = new TableColumn(1, 100, new BooleanTableCellRenderer(), new BooleanTableCellEditor());
- columnModel.addColumn(checkColumn);
- checkColumn.setHeaderValue(" Instrument ");
-
- TableCellRenderer defaultRenderer = myTable.getTableHeader().getDefaultRenderer();
-
- TableCellRenderer headerRenderer = new TableCellRenderer() {
- @Override
- public Component getTableCellRendererComponent(JTable table,
- Object value,
- boolean isSelected,
- boolean hasFocus,
- int row,
- int column) {
- Component component = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
- if (component instanceof JComponent) {
- ((JComponent)component)
- .setToolTipText(column == 1 ? JavaBundle.message("nullable.notnull.annotations.runtime.instrumentation.tooltip") : null);
- }
- return component;
- }
- };
- myTable.getTableHeader().setDefaultRenderer(headerRenderer);
- checkColumn.setHeaderRenderer(headerRenderer);
- checkColumn.sizeWidthToFit();
- }
-
final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTable).disableUpDownActions()
- .setAddAction(b -> chooseAnnotation(model.getName()))
+ .setAddAction(b -> chooseAnnotation(name))
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
@@ -167,7 +114,7 @@ public class AnnotationsPanel {
final var tablePanel = UI.PanelFactory
.panel(toolbarDecorator.createPanel())
- .withLabel(JavaBundle.message("nullable.notnull.annotations.panel.title", model.getName()))
+ .withLabel(JavaBundle.message("annotations.panel.title", name))
.moveLabelOnTop()
.resizeY(true)
.createPanel();
@@ -178,15 +125,6 @@ public class AnnotationsPanel {
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.weightx = 1;
- if (showDefaultActions) {
- myComponent.add(new JLabel(JavaBundle.message("nullable.notnull.annotation.used.label")), constraints);
- constraints.fill = GridBagConstraints.HORIZONTAL;
- constraints.insets.bottom = 3;
- constraints.gridy = 1;
- myComponent.add(myCombo, constraints);
- constraints.insets.bottom = 0;
- constraints.gridy = 2;
- }
constraints.fill = GridBagConstraints.BOTH;
constraints.weighty = 1;
myComponent.add(tablePanel, constraints);
@@ -196,42 +134,8 @@ public class AnnotationsPanel {
return true;
}
- private void loadAdvancedAnnotations(@NotNull AnnotationPanelModel model) {
- // No project-specific annotations are possible for default project
- if (myProject.isDefault()) return;
- String loading = JavaBundle.message("loading.additional.annotations");
- myCombo.addItem(loading);
- DumbService.getInstance(myProject).runWhenSmart(() -> {
- ReadAction.nonBlocking(model::getAdvancedAnnotations)
- .finishOnUiThread(ModalityState.any(), advancedAnnotations -> {
- myCombo.removeItem(loading);
- int count = myCombo.getItemCount();
- Object selectedItem = myCombo.getSelectedItem();
- List newItems = Stream.concat(
- IntStream.range(0, count).mapToObj(myCombo::getItemAt),
- advancedAnnotations.stream()).sorted().distinct().toList();
- myCombo.removeAllItems();
- newItems.forEach(myCombo::addItem);
- myCombo.setSelectedItem(selectedItem);
- }).submit(AppExecutorUtil.getAppExecutorService());
- });
- myCombo.addActionListener(new ActionListener() {
- Object previous = myCombo.getSelectedItem();
-
- @Override
- public void actionPerformed(ActionEvent e) {
- Object item = myCombo.getSelectedItem();
- if (item == loading) {
- myCombo.setSelectedItem(previous);
- } else {
- previous = item;
- }
- }
- });
- }
-
- private void addRow(String annotation, boolean checked) {
- myTableModel.addRow(new Object[]{annotation, checked});
+ private void addRow(String annotation) {
+ myTableModel.addRow(new Object[]{annotation});
}
private Integer selectAnnotation(String annotation) {
@@ -264,7 +168,7 @@ public class AnnotationsPanel {
}
final String qualifiedName = selected.getQualifiedName();
if (selectAnnotation(qualifiedName) == null) {
- addRow(qualifiedName, false);
+ addRow(qualifiedName);
addAnnotationToCombo(qualifiedName);
mySorter.sort();
Object added = selectAnnotation(qualifiedName);
@@ -285,10 +189,6 @@ public class AnnotationsPanel {
return myComponent;
}
- String getDefaultAnnotation() {
- return myCombo.getItem();
- }
-
public String[] getAnnotations() {
int size = myTable.getRowCount();
String[] result = new String[size];
@@ -298,16 +198,6 @@ public class AnnotationsPanel {
return result;
}
- List getCheckedAnnotations() {
- List result = new ArrayList<>();
- for (int i = 0; i < myTable.getRowCount(); i++) {
- if (Boolean.TRUE.equals(myTable.getValueAt(i, 1))) {
- result.add((String)myTable.getValueAt(i, 0));
- }
- }
- return result;
- }
-
/** Reset table to contain only annotations from the list. */
public void resetAnnotations(List annotations) {
final Set set = new HashSet<>(annotations);
@@ -321,57 +211,7 @@ public class AnnotationsPanel {
}
}
for (String annotation : set) {
- addRow(annotation, false);
- }
- }
-
- private static class SimpleAnnotationPanelModel implements AnnotationPanelModel {
- private final @NonNls String myName;
- private final String myDefaultAnnotation;
- private final List myAnnotations;
- private final List myDefaultAnnotations;
- private final Set myCheckedAnnotations;
-
- private SimpleAnnotationPanelModel(@NonNls String name,
- String defaultAnnotation,
- List annotations,
- List defaultAnnotations,
- Set checkedAnnotations) {
- myName = name;
- myDefaultAnnotation = defaultAnnotation;
- myAnnotations = annotations;
- myDefaultAnnotations = defaultAnnotations;
- myCheckedAnnotations = checkedAnnotations;
- }
-
- @Override
- public @NotNull String getName() {
- return myName;
- }
-
- @Override
- public @NotNull String getDefaultAnnotation() {
- return myDefaultAnnotation;
- }
-
- @Override
- public @NotNull List getAnnotations() {
- return myAnnotations;
- }
-
- @Override
- public @NotNull List getAdvancedAnnotations() {
- return myAnnotations;
- }
-
- @Override
- public @NotNull List getDefaultAnnotations() {
- return myDefaultAnnotations;
- }
-
- @Override
- public @NotNull Set getCheckedAnnotations() {
- return myCheckedAnnotations;
+ addRow(annotation);
}
}
}
\ No newline at end of file
diff --git a/java/openapi/src/com/intellij/codeInsight/NullableAnnotationsPanel.java b/java/openapi/src/com/intellij/codeInsight/NullableAnnotationsPanel.java
new file mode 100644
index 000000000000..75a7d67baa0a
--- /dev/null
+++ b/java/openapi/src/com/intellij/codeInsight/NullableAnnotationsPanel.java
@@ -0,0 +1,319 @@
+// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.codeInsight;
+
+import com.intellij.core.JavaPsiBundle;
+import com.intellij.ide.util.ClassFilter;
+import com.intellij.ide.util.TreeClassChooser;
+import com.intellij.ide.util.TreeClassChooserFactory;
+import com.intellij.java.JavaBundle;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.application.ReadAction;
+import com.intellij.openapi.project.DumbService;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.openapi.util.NlsSafe;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.ui.*;
+import com.intellij.ui.dsl.builder.DslComponentProperty;
+import com.intellij.ui.dsl.builder.VerticalComponentGap;
+import com.intellij.ui.table.JBTable;
+import com.intellij.util.concurrency.AppExecutorUtil;
+import com.intellij.util.ui.JBDimension;
+import com.intellij.util.ui.UI;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public class NullableAnnotationsPanel {
+ private final Project myProject;
+ private final Set myDefaultAnnotations;
+ private final JBTable myTable;
+ private final JPanel myComponent;
+ private final ComboBox myCombo;
+ protected final DefaultTableModel myTableModel;
+
+ public NullableAnnotationsPanel(Project project,
+ @NotNull AnnotationPanelModel model,
+ boolean showInstrumentationOptions) {
+ myProject = project;
+ myDefaultAnnotations = new HashSet<>(model.getDefaultAnnotations());
+
+ List annotations = model.getAnnotations();
+ myCombo = new ComboBox<>(annotations.stream().sorted().toArray(String[]::new));
+ String defaultAnnotation = model.getDefaultAnnotation();
+ if (!annotations.contains(defaultAnnotation)) {
+ addAnnotationToCombo(defaultAnnotation);
+ }
+ if (model.hasAdvancedAnnotations()) {
+ loadAdvancedAnnotations(model);
+ }
+ myCombo.setSelectedItem(defaultAnnotation);
+
+ myTableModel = new DefaultTableModel() {
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return column == 1;
+ }
+ };
+ myTableModel.setColumnCount(showInstrumentationOptions ? 2 : 1);
+ for (String annotation : annotations) {
+ addRow(annotation, model.getCheckedAnnotations().contains(annotation));
+ }
+
+ DefaultTableColumnModel columnModel = new DefaultTableColumnModel();
+ columnModel.addColumn(new TableColumn(0, 100, new ColoredTableCellRenderer() {
+ @Override
+ public void acquireState(JTable table, boolean isSelected, boolean hasFocus, int row, int column) {
+ super.acquireState(table, isSelected, false, row, column);
+ }
+
+ @Override
+ protected void customizeCellRenderer(@NotNull JTable table,
+ Object value,
+ boolean selected,
+ boolean hasFocus,
+ int row,
+ int column) {
+ append((String)value, SimpleTextAttributes.REGULAR_ATTRIBUTES);
+ }
+ }, null));
+
+ myTable = new JBTable(myTableModel, columnModel);
+ if (!showInstrumentationOptions) myTable.setTableHeader(null);
+
+ if (showInstrumentationOptions) {
+ columnModel.getColumn(0).setHeaderValue(JavaPsiBundle.message("node.annotation.tooltip"));
+
+ TableColumn checkColumn = new TableColumn(1, 100, new BooleanTableCellRenderer(), new BooleanTableCellEditor());
+ columnModel.addColumn(checkColumn);
+ checkColumn.setHeaderValue(" Instrument ");
+
+ TableCellRenderer headerRenderer = createHeaderRenderer();
+ myTable.getTableHeader().setDefaultRenderer(headerRenderer);
+ checkColumn.setHeaderRenderer(headerRenderer);
+ checkColumn.sizeWidthToFit();
+ }
+
+ final ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(myTable)
+ .setMoveUpAction(new AnActionButtonRunnable() {
+ @Override
+ public void run(AnActionButton button) {
+ int selectedRow = myTable.getSelectedRow();
+ if (selectedRow < 1) return;
+ @SuppressWarnings("unchecked")
+ List