diff --git a/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml b/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml index 275842ee7..6cf05e8c4 100644 --- a/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml +++ b/java-analysis-api/src/main/resources/LOCALIZE-LIB/en_US/consulo.java.analysis.JavaAnalysisLocalize.yaml @@ -30,6 +30,8 @@ change.visibility.level: text: Make {0} {1} comparision.between.object.and.primitive: text: Comparision between Object and primitive is illegal and is accepted in java 7 only +conflicting.nullability.annotations: + text: Conflicting nullability annotations contract.return.validator.incompatible.return.parameter.type: text: return type ''{0}'' must be convertible from parameter type ''{1}'' contract.return.validator.method.return.incompatible.with.method.containing.class: @@ -686,12 +688,46 @@ inspection.nullable.problems.annotated.field.setter.parameter.conflict: text: Setter parameter for @{0} field is annotated @{1} inspection.nullable.problems.annotated.field.setter.parameter.not.annotated: text: Setter parameter for @{0} field might be annotated @{0} itself +inspection.nullable.problems.applied.to.package: + text: Annotation on fully-qualified name must be placed before the last component +inspection.nullable.problems.at.class: + text: Nullability annotation is not applicable to classes +inspection.nullable.problems.at.constructor: + text: Nullability annotation is not applicable to constructors +inspection.nullable.problems.at.enum.constant: + text: Nullability annotation is not applicable to enum constants +inspection.nullable.problems.at.local.variable: + text: Nullability annotation is not applicable to local variables +inspection.nullable.problems.at.reference.list: + text: Nullability annotation is not applicable to extends/implements clause +inspection.nullable.problems.at.throws: + text: Nullability annotation is not applicable to 'throws' clause +inspection.nullable.problems.at.type.parameter: + text: Nullability annotation is not applicable to type parameters +inspection.nullable.problems.at.wildcard: + text: Nullability annotation is not applicable to wildcard type +inspection.nullable.problems.constructor.not.compatible.non.null.type.argument: + text: Constructor is not compatible with a non-null type argument inspection.nullable.problems.method.overrides.NotNull: text: Not annotated method overrides method annotated with @{0} +inspection.nullable.problems.nullable.instantiation.of.notnull: + text: Non-null type parameter ''{0}'' cannot be instantiated with @{1} type +inspection.nullable.problems.nullable.instantiation.of.notnull.container: + text: Non-null type parameter ''{0}'' cannot be instantiated under @{1} +inspection.nullable.problems.outer.type: + text: Outer type is inherently non-null inspection.nullable.problems.parameter.overrides.NotNull: text: Not annotated parameter overrides @{0} parameter inspection.nullable.problems.primitive.type.annotation: text: Primitive type members cannot be annotated +inspection.nullable.problems.receiver.annotation: + text: Receiver parameter is inherently non-null +inspection.nullable.problems.redundant.annotation.inherited.notnull: + text: 'Redundant nullability annotation: type parameter upper bound is already non-null' +inspection.nullable.problems.redundant.annotation.under.container: + text: Redundant nullability annotation in the scope of @{0} +inspection.nullable.problems.turn.off.redundant.annotation.under.container: + text: Don't report redundant nullability annotation in the scope of annotated container inspection.numeric.overflow.display.name: text: Numeric overflow inspection.objects.equals.can.be.simplified.display.name: @@ -1068,3 +1104,17 @@ vararg.method.call.with.50.poly.arguments: text: Vararg method call with 50+ poly arguments may cause compilation and analysis slowdown visible.for.testing.makes.little.sense.on.test.only.code: text: '@VisibleForTesting makes little sense on @TestOnly code' +complex.problem.with.nullability: + text: Incompatible type arguments due to nullability{0} +assigning.a.class.with.nullable.elements: + text: Assigning a class with nullable type arguments when a class with not-null type arguments is expected{0} +assigning.a.class.with.notnull.elements: + text: Assigning a class with not-null type arguments when a class with nullable type arguments is expected{0} +overriding.a.class.with.nullable.elements: + text: Overriding a class with nullable type arguments when a class with not-null type arguments is expected{0} +overriding.a.class.with.notnull.elements: + text: Overriding a class with not-null type arguments when a class with nullable type arguments is expected{0} +returning.a.class.with.nullable.arguments: + text: Returning a class with nullable type arguments when a class with not-null type arguments is expected{0} +returning.a.class.with.notnull.arguments: + text: Returning a class with not-null type arguments when a class with nullable type arguments is expected{0} diff --git a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/nullable/NullableStuffInspectionBase.java b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/nullable/NullableStuffInspectionBase.java index 7171dbbed..1efd8af73 100644 --- a/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java-analysis-impl/src/main/java/com/intellij/java/analysis/impl/codeInspection/nullable/NullableStuffInspectionBase.java @@ -1,7 +1,6 @@ // Copyright 2000-2019 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.analysis.impl.codeInspection.nullable; -import com.intellij.java.analysis.JavaAnalysisBundle; import com.intellij.java.analysis.codeInspection.AbstractBaseJavaLocalInspectionTool; import com.intellij.java.analysis.impl.codeInsight.intention.AddAnnotationFix; import com.intellij.java.analysis.impl.codeInsight.intention.AddAnnotationPsiFix; @@ -21,7 +20,9 @@ import com.intellij.java.language.psi.util.*; import com.intellij.java.language.util.JavaTypeNullabilityUtil; import com.siyeh.ig.psiutils.TypeUtils; +import consulo.annotation.access.RequiredReadAction; import consulo.application.util.registry.Registry; +import consulo.java.analysis.localize.JavaAnalysisLocalize; import consulo.language.editor.FileModificationService; import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.localize.InspectionLocalize; @@ -33,18 +34,18 @@ import consulo.logging.Logger; import consulo.project.Project; import consulo.project.content.GeneratedSourcesFilter; +import consulo.ui.annotation.RequiredUIAccess; import consulo.util.collection.ArrayUtil; import consulo.util.collection.ContainerUtil; import consulo.util.lang.StringUtil; import consulo.virtualFileSystem.VirtualFile; import one.util.streamex.StreamEx; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.PropertyKey; import org.jspecify.annotations.Nullable; import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; import static com.intellij.java.language.codeInsight.AnnotationUtil.*; @@ -56,10 +57,13 @@ public abstract class NullableStuffInspectionBase extends AbstractBaseJavaLocalI private static Logger LOG = Logger.getInstance(NullableStuffInspectionBase.class); @Override - public PsiElementVisitor buildVisitorImpl(final ProblemsHolder holder, - final boolean isOnTheFly, - LocalInspectionToolSession session, - NullableStuffInspectionState state) { + @RequiredReadAction + public PsiElementVisitor buildVisitorImpl( + final ProblemsHolder holder, + final boolean isOnTheFly, + LocalInspectionToolSession session, + NullableStuffInspectionState state + ) { PsiFile file = holder.getFile(); if (!PsiUtil.isAvailable(JavaFeature.ANNOTATIONS, file) || nullabilityAnnotationsNotAvailable(file)) { return PsiElementVisitor.EMPTY_VISITOR; @@ -70,6 +74,7 @@ public PsiElementVisitor buildVisitorImpl(final ProblemsHolder holder, private List notNulls = manager.getNotNulls(); @Override + @RequiredReadAction public void visitMethod(PsiMethod method) { checkNullableStuffForMethod(method, holder, state); } @@ -96,6 +101,7 @@ public void visitModule(PsiJavaModule module) { } @Override + @RequiredReadAction public void visitNewExpression(PsiNewExpression expression) { if (expression.isArrayCreation()) { return; @@ -136,33 +142,40 @@ public void visitNewExpression(PsiNewExpression expression) { } AddTypeAnnotationFix fix = null; - if (classType.getPsiContext() instanceof PsiJavaCodeReferenceElement typeRef && - typeRef.getParent() instanceof PsiTypeElement typeElement && typeElement.getType().equals(classType) && - typeElement.acceptsAnnotations()) { + if (classType.getPsiContext() instanceof PsiJavaCodeReferenceElement typeRef + && typeRef.getParent() instanceof PsiTypeElement typeElement + && typeElement.getType().equals(classType) + && typeElement.acceptsAnnotations()) { 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")) + holder.problem( + expression, + JavaAnalysisLocalize.inspectionNullableProblemsConstructorNotCompatibleNonNullTypeArgument() + ) .maybeFix(fix) .register(); } @Override + @RequiredReadAction public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { checkMethodReference(expression, holder, state); JavaResolveResult result = expression.advancedResolve(false); - PsiElement target = result.getElement(); - if (target instanceof PsiMethod) { - checkNestedGenericClasses(holder, expression, + if (result.getElement() instanceof PsiMethod method) { + checkNestedGenericClasses( + holder, + expression, LambdaUtil.getFunctionalInterfaceReturnType(expression), - result.getSubstitutor().substitute(((PsiMethod) target).getReturnType()), + result.getSubstitutor().substitute(method.getReturnType()), ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, - state); + state + ); } } @Override + @RequiredReadAction public void visitField(PsiField field) { PsiType type = field.getType(); Annotated annotated = check(field, holder, type); @@ -183,11 +196,13 @@ public void visitField(PsiField field) { PsiElement identifyingElement = field.getIdentifyingElement(); if (initializer != null && identifyingElement != null) { checkNestedGenericClasses(holder, identifyingElement, field.getType(), initializer.getType(), - ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state); + ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state + ); } } @Override + @RequiredReadAction public void visitMethodCallExpression(PsiMethodCallExpression call) { super.visitMethodCallExpression(call); PsiReferenceParameterList parameterList = call.getMethodExpression().getParameterList(); @@ -222,27 +237,41 @@ public void visitMethodCallExpression(PsiMethodCallExpression call) { PsiAnnotation anchor = explicit.annotation(); PsiJavaCodeReferenceElement ref = anchor.getNameReferenceElement(); if (ref != null) { - reportProblem(holder, anchor, "inspection.nullable.problems.nullable.instantiation.of.notnull", - typeParameter.getName(), ref.getReferenceName()); + reportProblem( + holder, + anchor, + JavaAnalysisLocalize.inspectionNullableProblemsNullableInstantiationOfNotnull( + typeParameter.getName(), + ref.getReferenceName() + ) + ); } } else if (source instanceof NullabilitySource.ContainerAnnotation container) { PsiElement anchor = parameterList.getTypeParameterElements()[i]; PsiJavaCodeReferenceElement ref = container.annotation().getNameReferenceElement(); if (ref != null) { - reportProblem(holder, anchor, "inspection.nullable.problems.nullable.instantiation.of.notnull.container", - typeParameter.getName(), ref.getReferenceName()); + reportProblem( + holder, + anchor, + JavaAnalysisLocalize.inspectionNullableProblemsNullableInstantiationOfNotnullContainer( + typeParameter.getName(), + ref.getReferenceName() + ) + ); } } } } @Override + @RequiredReadAction public void visitParameter(PsiParameter parameter) { check(parameter, holder, parameter.getType()); } @Override + @RequiredReadAction public void visitAnnotation(PsiAnnotation annotation) { NullabilityAnnotationWrapper wrapper = NullabilityAnnotationWrapper.from(annotation); if (wrapper == null) { @@ -252,16 +281,19 @@ public void visitAnnotation(PsiAnnotation annotation) { PsiType type = wrapper.type(); PsiModifierListOwner listOwner = wrapper.listOwner(); checkRedundantInContainerScope(wrapper, state); - if (type != null && - wrapper.nullability() == Nullability.NOT_NULL && - PsiUtil.resolveClassInClassTypeOnly(type) instanceof PsiTypeParameter) { + if (type != null + && wrapper.nullability() == Nullability.NOT_NULL + && PsiUtil.resolveClassInClassTypeOnly(type) instanceof PsiTypeParameter) { PsiType notAnnotated = type.annotate(TypeAnnotationProvider.EMPTY); TypeNullability notAnnotatedNullability = notAnnotated.getNullability(); - if (notAnnotatedNullability.nullability() == Nullability.NOT_NULL && - notAnnotatedNullability.source() instanceof NullabilitySource.ExtendsBound) { - reportProblem(holder, annotation, + if (notAnnotatedNullability.nullability() == Nullability.NOT_NULL + && notAnnotatedNullability.source() instanceof NullabilitySource.ExtendsBound) { + reportProblem( + holder, + annotation, new RemoveAnnotationQuickFix(annotation, null), - "inspection.nullable.problems.redundant.annotation.inherited.notnull"); + JavaAnalysisLocalize.inspectionNullableProblemsRedundantAnnotationInheritedNotnull() + ); } } if (type instanceof PsiPrimitiveType) { @@ -269,7 +301,13 @@ public void visitAnnotation(PsiAnnotation annotation) { if (targetType instanceof PsiArrayType && !targetType.hasAnnotations()) { additionalFix = new MoveAnnotationToArrayFix(); } - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.primitive.type.annotation", LocalQuickFix.notNullElements(additionalFix)); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsPrimitiveTypeAnnotation(), + LocalQuickFix.notNullElements(additionalFix) + ); } if (type instanceof PsiClassType classType) { PsiElement context = classType.getPsiContext(); @@ -279,56 +317,99 @@ public void visitAnnotation(PsiAnnotation annotation) { if (parent instanceof PsiJavaCodeReferenceElement) { if (outerCtx.resolve() instanceof PsiPackage) { ModCommandAction action = new MoveAnnotationOnStaticMemberQualifyingTypeFix(annotation); - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.applied.to.package", - LocalQuickFix.from(action)); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAppliedToPackage(), + LocalQuickFix.from(action) + ); } else { // If outer is qualifier of static member then don't report problem as it is already reported // as ANNOTATION_NOT_ALLOWED_STATIC which contains exactly the same fix "Move annotation". if (!PsiImplUtil.isTypeQualifierOfStaticMember(outerCtx)) { ModCommandAction action = new MoveAnnotationOnStaticMemberQualifyingTypeFix(annotation); - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.outer.type", - LocalQuickFix.from(action)); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsOuterType(), + LocalQuickFix.from(action) + ); } } } if (parent instanceof PsiReferenceList) { PsiElement firstChild = parent.getFirstChild(); if ((PsiUtil.isJavaToken(firstChild, JavaTokenType.EXTENDS_KEYWORD) || - PsiUtil.isJavaToken(firstChild, JavaTokenType.IMPLEMENTS_KEYWORD)) && - !(parent.getParent() instanceof PsiTypeParameter)) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.reference.list"); + PsiUtil.isJavaToken(firstChild, JavaTokenType.IMPLEMENTS_KEYWORD)) + && !(parent.getParent() instanceof PsiTypeParameter)) { + reportIncorrectLocation(holder, annotation, listOwner, JavaAnalysisLocalize.inspectionNullableProblemsAtReferenceList()); } if (PsiUtil.isJavaToken(firstChild, JavaTokenType.THROWS_KEYWORD)) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.throws"); + reportIncorrectLocation(holder, annotation, listOwner, JavaAnalysisLocalize.inspectionNullableProblemsAtThrows()); } } } } - if (type instanceof PsiArrayType && annotation.getParent() instanceof PsiTypeElement parent && - parent.getType().equals(type) && !manager.canAnnotateLocals(wrapper.qualifiedName())) { + if (type instanceof PsiArrayType + && annotation.getParent() instanceof PsiTypeElement parent + && parent.getType().equals(type) + && !manager.canAnnotateLocals(wrapper.qualifiedName())) { checkIllegalLocalAnnotation(annotation, parent.getParent(), state); } if (listOwner instanceof PsiMethod method && method.isConstructor()) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.constructor"); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAtConstructor() + ); } - if (listOwner instanceof PsiClass && AnnotationTargetUtil.findAnnotationTarget(annotation, PsiAnnotation.TargetType.TYPE) == null) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.class"); + if (listOwner instanceof PsiClass + && AnnotationTargetUtil.findAnnotationTarget(annotation, PsiAnnotation.TargetType.TYPE) == null) { + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAtClass() + ); } if (listOwner instanceof PsiEnumConstant) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.enum.constant"); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAtEnumConstant() + ); } if (!manager.canAnnotateLocals(wrapper.qualifiedName()) && !(targetType instanceof PsiArrayType)) { checkIllegalLocalAnnotation(annotation, listOwner, state); } if (type instanceof PsiWildcardType && manager.isTypeUseAnnotationLocationRestricted(wrapper.qualifiedName())) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.wildcard"); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAtWildcard() + ); } if (wrapper.owner() instanceof PsiTypeParameter && manager.isTypeUseAnnotationLocationRestricted(wrapper.qualifiedName())) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.at.type.parameter"); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsAtTypeParameter() + ); } if (listOwner instanceof PsiReceiverParameter && wrapper.nullability() != Nullability.NOT_NULL) { - reportIncorrectLocation(holder, annotation, listOwner, "inspection.nullable.problems.receiver.annotation"); + reportIncorrectLocation( + holder, + annotation, + listOwner, + JavaAnalysisLocalize.inspectionNullableProblemsReceiverAnnotation() + ); } checkOppositeAnnotationConflict(annotation, wrapper.nullability()); if (NOT_NULL.equals(wrapper.qualifiedName())) { @@ -336,7 +417,7 @@ public void visitAnnotation(PsiAnnotation annotation) { if (value instanceof PsiClassObjectAccessExpression classObjectAccessExpression) { PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(classObjectAccessExpression.getOperand().getType()); if (psiClass != null && !hasStringConstructor(psiClass)) { - reportProblem(holder, value, "custom.exception.class.should.have.a.constructor"); + reportProblem(holder, value, JavaAnalysisLocalize.customExceptionClassShouldHaveAConstructor()); } } } @@ -358,24 +439,37 @@ private void reportRedundantInContainerScope(PsiAnnotation annotation, Nullabili PsiJavaCodeReferenceElement containerName = containerInfo.getAnnotation().getNameReferenceElement(); if (containerName != null) { LocalQuickFix updateOptionFix = LocalQuickFix.from( - new UpdateInspectionOptionFix(NullableStuffInspectionBase.this, + new UpdateInspectionOptionFix( + NullableStuffInspectionBase.this, "REPORT_REDUNDANT_NULLABILITY_ANNOTATION_IN_THE_SCOPE_OF_ANNOTATED_CONTAINER", - JavaAnalysisBundle.message("inspection.nullable.problems.turn.off.redundant.annotation.under.container"), - false)); - reportProblem(holder, annotation, + JavaAnalysisLocalize.inspectionNullableProblemsTurnOffRedundantAnnotationUnderContainer(), + false + )); + reportProblem(holder, + annotation, LocalQuickFix.notNullElements(new RemoveAnnotationQuickFix(annotation, null), updateOptionFix), - "inspection.nullable.problems.redundant.annotation.under.container", containerName.getReferenceName()); + JavaAnalysisLocalize.inspectionNullableProblemsRedundantAnnotationUnderContainer(containerName.getReferenceName()) + ); } } - private void checkIllegalLocalAnnotation(PsiAnnotation annotation, @Nullable PsiElement owner, NullableStuffInspectionState state) { + private void checkIllegalLocalAnnotation( + PsiAnnotation annotation, + @Nullable PsiElement owner, + NullableStuffInspectionState state + ) { if (!state.REPORT_NULLABILITY_ANNOTATION_ON_LOCALS) { return; } if (owner instanceof PsiLocalVariable || - owner instanceof PsiParameter parameter && - parameter.getDeclarationScope() instanceof PsiCatchSection) { - reportIncorrectLocation(holder, annotation, (PsiVariable) owner, "inspection.nullable.problems.at.local.variable"); + owner instanceof PsiParameter parameter + && parameter.getDeclarationScope() instanceof PsiCatchSection) { + reportIncorrectLocation( + holder, + annotation, + (PsiVariable) owner, + JavaAnalysisLocalize.inspectionNullableProblemsAtLocalVariable() + ); } } @@ -388,7 +482,9 @@ private void checkOppositeAnnotationConflict(PsiAnnotation annotation, Nullabili ? tryCast(modifierList.getParent(), PsiModifierListOwner.class) : null; Predicate filter = anno -> - anno != annotation && manager.getAnnotationNullability(anno.getQualifiedName()).filter(n -> n != nullability).isPresent(); + anno != annotation && manager.getAnnotationNullability(anno.getQualifiedName()) + .filter(n -> n != nullability) + .isPresent(); PsiAnnotation oppositeAnno = ContainerUtil.find(owner.getAnnotations(), filter); if (oppositeAnno == null && listOwner != null) { NullabilityAnnotationInfo result = manager.findNullabilityAnnotationInfo( @@ -397,9 +493,15 @@ private void checkOppositeAnnotationConflict(PsiAnnotation annotation, Nullabili } if (oppositeAnno != null && Objects.equals(getRelatedType(annotation), getRelatedType(oppositeAnno))) { - reportProblem(holder, annotation, new RemoveAnnotationQuickFix(annotation, listOwner), - "inspection.nullable.problems.Nullable.NotNull.conflict", - getPresentableAnnoName(annotation), getPresentableAnnoName(oppositeAnno)); + reportProblem( + holder, + annotation, + new RemoveAnnotationQuickFix(annotation, listOwner), + JavaAnalysisLocalize.inspectionNullableProblemsNullableNotnullConflict( + getPresentableAnnoName(annotation), + getPresentableAnnoName(oppositeAnno) + ) + ); } } @@ -415,6 +517,7 @@ private static boolean hasStringConstructor(PsiClass aClass) { } @Override + @RequiredReadAction public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { super.visitReferenceElement(reference); @@ -422,24 +525,28 @@ public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { PsiElement list = reference.getParent(); PsiElement parent = list instanceof PsiReferenceList ? list.getParent() : null; - if (parent instanceof PsiClass psiClass && list == psiClass.getImplementsList() && - reference.resolve() instanceof PsiClass intf && intf.isInterface()) { - String error = checkIndirectInheritance(psiClass, intf); - if (error != null) { - holder.registerProblem(reference, error); + if (parent instanceof PsiClass psiClass + && list == psiClass.getImplementsList() + && reference.resolve() instanceof PsiClass intf + && intf.isInterface()) { + LocalizeValue error = checkIndirectInheritance(psiClass, intf); + if (error.isNotEmpty()) { + holder.newProblem(error) + .range(reference) + .create(); } } } + @RequiredReadAction private void checkNullableNotNullInstantiationConflict(PsiJavaCodeReferenceElement reference) { - PsiElement element = reference.resolve(); - if (element instanceof PsiClass) { - PsiTypeParameter[] typeParameters = ((PsiClass) element).getTypeParameters(); + if (reference.resolve() instanceof PsiClass psiClass) { + PsiTypeParameter[] typeParameters = psiClass.getTypeParameters(); PsiTypeElement[] typeArguments = getReferenceTypeArguments(reference); if (typeParameters.length > 0 && typeParameters.length == typeArguments.length && !(typeArguments[0].getType() instanceof PsiDiamondType)) { for (int i = 0; i < typeParameters.length; i++) { PsiTypeElement typeArgument = typeArguments[i]; - Project project = element.getProject(); + Project project = psiClass.getProject(); PsiType type = typeArgument.getType(); if (TypeNullability.ofTypeParameter(typeParameters[i]).nullability() != Nullability.NOT_NULL) { continue; @@ -449,24 +556,30 @@ private void checkNullableNotNullInstantiationConflict(PsiJavaCodeReferenceEleme if (typeNullability != Nullability.NOT_NULL && !(typeNullability == Nullability.UNKNOWN && type instanceof PsiWildcardType wildcardType && !wildcardType.isExtends())) { String annotationToAdd = manager.getDefaultAnnotation(Nullability.NOT_NULL, reference); - PsiClass annotationClass = JavaPsiFacade.getInstance(project).findClass(annotationToAdd, element.getResolveScope()); + PsiClass annotationClass = + JavaPsiFacade.getInstance(project).findClass(annotationToAdd, psiClass.getResolveScope()); List fixes = new ArrayList<>(); if (annotationClass != null && AnnotationTargetUtil.findAnnotationTarget(annotationClass, PsiAnnotation.TargetType.TYPE_USE) != null) { - fixes.add(LocalQuickFix.from(new AddTypeAnnotationFix(typeArgument, annotationToAdd, manager.getNullables()))); + fixes.add(LocalQuickFix.from(new AddTypeAnnotationFix( + typeArgument, + annotationToAdd, + manager.getNullables() + ))); } fixes.add(LocalQuickFix.from(createAnnotateAsNullMarkedFix(typeArgument, manager.getNullables()))); ProblemHighlightType level = - nullability == TypeNullability.UNKNOWN && !state.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE ? - ProblemHighlightType.INFORMATION : - ProblemHighlightType.GENERIC_ERROR_OR_WARNING; + nullability == TypeNullability.UNKNOWN && !state.REPORT_NOT_ANNOTATED_INSTANTIATION_NOT_NULL_TYPE + ? ProblemHighlightType.INFORMATION + : ProblemHighlightType.GENERIC_ERROR_OR_WARNING; if (!isOnTheFly && level == ProblemHighlightType.INFORMATION) { continue; } - holder.registerProblem(typeArgument, - JavaAnalysisBundle.message("non.null.type.argument.is.expected"), - level, - fixes.toArray(LocalQuickFix.EMPTY_ARRAY)); + holder.newProblem(JavaAnalysisLocalize.nonNullTypeArgumentIsExpected()) + .range(typeArgument) + .withFixes(fixes) + .highlightType(level) + .create(); } } } @@ -487,7 +600,8 @@ public void visitAssignmentExpression(PsiAssignmentExpression expression) { checkNestedGenericClasses(holder, expression.getOperationSign(), expression.getLExpression().getType(), rExpression.getType(), - ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state); + ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state + ); } @Override @@ -501,7 +615,8 @@ public void visitLocalVariable(PsiLocalVariable variable) { return; } checkNestedGenericClasses(holder, identifier, variable.getType(), initializer.getType(), - ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state); + ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state + ); } @Override @@ -512,7 +627,8 @@ public void visitReturnStatement(PsiReturnStatement statement) { } checkNestedGenericClasses(holder, returnValue, PsiTypesUtil.getMethodReturnType(statement), returnValue.getType(), - ConflictNestedTypeProblem.RETURN_NESTED_TYPE_PROBLEM, state); + ConflictNestedTypeProblem.RETURN_NESTED_TYPE_PROBLEM, state + ); } @Override @@ -520,7 +636,8 @@ public void visitLambdaExpression(PsiLambdaExpression lambda) { super.visitLambdaExpression(lambda); PsiElement body = lambda.getBody(); if (body instanceof PsiExpression psiExpression) { - checkNestedGenericClasses(holder, + checkNestedGenericClasses( + holder, body, LambdaUtil.getFunctionalInterfaceReturnType(lambda), psiExpression.getType(), @@ -548,40 +665,49 @@ public void visitCallExpression(PsiCallExpression callExpression) { (i < parameters.length - 1 || !MethodCallInstruction.isVarArgCall(method, substitutor, arguments, parameters))) { PsiType expectedType = substitutor.substitute(parameters[i].getType()); checkNestedGenericClasses(holder, argument, expectedType, argument.getType(), - ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state); + ConflictNestedTypeProblem.ASSIGNMENT_NESTED_TYPE_PROBLEM, state + ); } } } }; } - private boolean checkNestedGenericClasses(ProblemsHolder holder, - PsiElement errorElement, - @Nullable PsiType expectedType, - @Nullable PsiType actualType, - ConflictNestedTypeProblem problem, - NullableStuffInspectionState state) { + private boolean checkNestedGenericClasses( + ProblemsHolder holder, + PsiElement errorElement, + @Nullable PsiType expectedType, + @Nullable PsiType actualType, + ConflictNestedTypeProblem problem, + NullableStuffInspectionState state + ) { if (expectedType == null || actualType == null) { return false; } - JavaTypeNullabilityUtil.NullabilityConflictContext - context = JavaTypeNullabilityUtil.getNullabilityConflictInAssignment(expectedType, actualType, - state.REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS); + JavaTypeNullabilityUtil.NullabilityConflictContext context = JavaTypeNullabilityUtil.getNullabilityConflictInAssignment( + expectedType, + actualType, + state.REPORT_NOT_NULL_TO_NULLABLE_CONFLICTS_IN_ASSIGNMENTS + ); JavaTypeNullabilityUtil.NullabilityConflict conflict = context.nullabilityConflict(); - String messageKey = switch (conflict) { - case UNKNOWN -> null; - case NOT_NULL_TO_NULL -> problem.notNullToNullProblem(); - case NULL_TO_NOT_NULL -> problem.nullToNotNullProblem(); - case COMPLEX -> problem.complexProblem(); - }; - if (messageKey == null) { + if (conflict == JavaTypeNullabilityUtil.NullabilityConflict.UNKNOWN) { return false; } - reportProblem(holder, errorElement, LocalQuickFix.EMPTY_ARRAY, - messageKey, new Object[]{""}, - messageKey, new Object[]{NullableStuffInspectionUtil.getNullabilityConflictPresentation(context)}); + Function messageKey = switch (conflict) { + case NOT_NULL_TO_NULL -> (Function) problem::notNullToNullProblem; + case NULL_TO_NOT_NULL -> (Function) problem::nullToNotNullProblem; + default -> (Function) problem::complexProblem; + }; + + reportProblem( + holder, + errorElement, + LocalQuickFix.EMPTY_ARRAY, + messageKey.apply(""), + messageKey.apply(NullableStuffInspectionUtil.getNullabilityConflictPresentation(context)) + ); return true; } @@ -593,30 +719,42 @@ private void checkConflictingContainerAnnotations(ProblemsHolder holder, @Nullab List conflictingAnnotations = manager.getConflictingContainerAnnotations(list); if (!conflictingAnnotations.isEmpty()) { for (PsiAnnotation annotation : conflictingAnnotations) { - reportProblem(holder, annotation, "conflicting.nullability.annotations"); + reportProblem(holder, annotation, JavaAnalysisLocalize.conflictingNullabilityAnnotations()); } } } - private void reportProblem(ProblemsHolder holder, PsiElement anchor, String messageKey, Object... args) { - reportProblem(holder, anchor, LocalQuickFix.EMPTY_ARRAY, messageKey, args); + private void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalizeValue message) { + reportProblem(holder, anchor, LocalQuickFix.EMPTY_ARRAY, message); } - private void reportProblem(ProblemsHolder holder, PsiElement anchor, @Nullable LocalQuickFix fix, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String messageKey, Object... args) { - reportProblem(holder, anchor, fix == null ? LocalQuickFix.EMPTY_ARRAY : new LocalQuickFix[]{fix}, messageKey, args); + private void reportProblem( + ProblemsHolder holder, + PsiElement anchor, + @Nullable LocalQuickFix fix, + LocalizeValue message + ) { + reportProblem(holder, anchor, fix == null ? LocalQuickFix.EMPTY_ARRAY : new LocalQuickFix[]{fix}, message); } - protected void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalQuickFix [] fixes, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String messageKey, Object... args) { - reportProblem(holder, anchor, fixes, messageKey, args, messageKey, args); + protected void reportProblem( + ProblemsHolder holder, + PsiElement anchor, + LocalQuickFix[] fixes, + LocalizeValue message + ) { + reportProblem(holder, anchor, fixes, message, message); } - protected void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalQuickFix [] fixes, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String descriptionKey, Object [] descriptionArgs, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String tooltipKey, Object [] tooltipArgs) { - ProblemsHolder.ProblemBuilder builder = holder.problem(anchor, JavaAnalysisBundle.message(descriptionKey, descriptionArgs)) - .tooltip(JavaAnalysisBundle.message(tooltipKey, tooltipArgs)) + protected void reportProblem( + ProblemsHolder holder, + PsiElement anchor, + LocalQuickFix[] fixes, + LocalizeValue description, + LocalizeValue tooltip + ) { + ProblemsHolder.ProblemBuilder builder = holder.problem(anchor, description) + .tooltip(tooltip) .highlight(ProblemHighlightType.GENERIC_ERROR_OR_WARNING); for (LocalQuickFix quickFix : fixes) { builder.fix(quickFix); @@ -624,7 +762,8 @@ protected void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalQuic builder.register(); } - private @Nullable String checkIndirectInheritance(PsiClass psiClass, PsiClass intf, NullableStuffInspectionState state) { + @RequiredReadAction + private LocalizeValue checkIndirectInheritance(PsiClass psiClass, PsiClass intf, NullableStuffInspectionState state) { PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(intf, psiClass, PsiSubstitutor.EMPTY); for (PsiMethod intfMethod : intf.getAllMethods()) { PsiClass intfMethodClass = intfMethod.getContainingClass(); @@ -632,28 +771,38 @@ protected void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalQuic JavaOverridingMethodsSearcher.findOverridingMethod(psiClass, intfMethod, intfMethodClass); PsiClass overridingMethodClass = overridingMethod == null ? null : overridingMethod.getContainingClass(); if (overridingMethodClass != null && overridingMethodClass != psiClass) { - String error = checkIndirectInheritance(intfMethod, intfMethodClass, overridingMethod, overridingMethodClass, substitutor, state); - if (error != null) { + LocalizeValue error = + checkIndirectInheritance(intfMethod, intfMethodClass, overridingMethod, overridingMethodClass, substitutor, state); + if (error.isNotEmpty()) { return error; } } } - return null; + return LocalizeValue.empty(); } - private @Nullable String checkIndirectInheritance(PsiMethod intfMethod, - PsiClass intfMethodClass, - PsiMethod overridingMethod, - PsiClass overridingMethodClass, - PsiSubstitutor substitutor, - NullableStuffInspectionState state) { + @RequiredReadAction + private LocalizeValue checkIndirectInheritance( + PsiMethod intfMethod, + PsiClass intfMethodClass, + PsiMethod overridingMethod, + PsiClass overridingMethodClass, + PsiSubstitutor substitutor, + NullableStuffInspectionState state + ) { if (isNullableOverridingNotNull(Annotated.from(overridingMethod), intfMethod, state)) { - return JavaAnalysisBundle.message("inspection.message.nullable.method.implements.non.null.method", - overridingMethod.getName(), overridingMethodClass.getName(), intfMethodClass.getName()); + return JavaAnalysisLocalize.inspectionMessageNullableMethodImplementsNonNullMethod( + overridingMethod.getName(), + overridingMethodClass.getName(), + intfMethodClass.getName() + ); } if (isNonAnnotatedOverridingNotNull(overridingMethod, intfMethod, state)) { - return JavaAnalysisBundle.message("inspection.message.non.annotated.method.implements.non.null.method", - overridingMethod.getName(), overridingMethodClass.getName(), intfMethodClass.getName()); + return JavaAnalysisLocalize.inspectionMessageNonAnnotatedMethodImplementsNonNullMethod( + overridingMethod.getName(), + overridingMethodClass.getName(), + intfMethodClass.getName() + ); } PsiParameter[] overridingParameters = overridingMethod.getParameterList().getParameters(); @@ -664,23 +813,36 @@ protected void reportProblem(ProblemsHolder holder, PsiElement anchor, LocalQuic PsiParameter parameter = overridingParameters[i]; List supers = Collections.singletonList(superParameters[i]); if (findNullableSuperForNotNullParameter(parameter, supers, substitutor, state) != null) { - return JavaAnalysisBundle.message("inspection.message.non.null.parameter.should.not.override.nullable.parameter", - parameter.getName(), overridingMethod.getName(), overridingMethodClass.getName(), intfMethodClass.getName()); + return JavaAnalysisLocalize.inspectionMessageNonNullParameterShouldNotOverrideNullableParameter( + parameter.getName(), + overridingMethod.getName(), + overridingMethodClass.getName(), + intfMethodClass.getName() + ); } - if (findNotNullSuperForNonAnnotatedParameter(manager, parameter, super, states) != null) { - return JavaAnalysisBundle.message("inspection.message.non.annotated.parameter.should.not.override.non.null.parameter", - parameter.getName(), overridingMethod.getName(), overridingMethodClass.getName(), intfMethodClass.getName()); + if (findNotNullSuperForNonAnnotatedParameter(manager, parameter, supers, state) != null) { + return JavaAnalysisLocalize.inspectionMessageNonAnnotatedParameterShouldNotOverrideNonNullParameter( + parameter.getName(), + overridingMethod.getName(), + overridingMethodClass.getName(), + intfMethodClass.getName() + ); } if (isNotNullParameterOverridingNonAnnotated(manager, parameter, supers, substitutor, state)) { - return JavaAnalysisBundle.message("inspection.message.non.null.parameter.should.not.override.non.annotated.parameter", - parameter.getName(), overridingMethod.getName(), overridingMethodClass.getName(), intfMethodClass.getName()); + return JavaAnalysisLocalize.inspectionMessageNonNullParameterShouldNotOverrideNonAnnotatedParameter( + parameter.getName(), + overridingMethod.getName(), + overridingMethodClass.getName(), + intfMethodClass.getName() + ); } } } - return null; + return LocalizeValue.empty(); } + @RequiredReadAction private void checkMethodReference(PsiMethodReferenceExpression expression, ProblemsHolder holder, NullableStuffInspectionState state) { PsiMethod superMethod = LambdaUtil.getFunctionalInterfaceMethod(expression); PsiMethod targetMethod = tryCast(expression.resolve(), PsiMethod.class); @@ -691,8 +853,14 @@ private void checkMethodReference(PsiMethodReferenceExpression expression, Probl PsiElement refName = expression.getReferenceNameElement(); assert refName != null; if (isNullableOverridingNotNull(check(targetMethod, holder, expression.getType()), superMethod, state)) { - reportProblem(holder, refName, "inspection.nullable.problems.Nullable.method.overrides.NotNull", - getPresentableAnnoName(targetMethod), getPresentableAnnoName(superMethod)); + reportProblem( + holder, + refName, + JavaAnalysisLocalize.inspectionNullableProblemsNullableMethodOverridesNotnull( + getPresentableAnnoName(targetMethod), + getPresentableAnnoName(superMethod) + ) + ); } } @@ -704,35 +872,52 @@ private static boolean nullabilityAnnotationsNotAvailable(PsiFile file) { Project project = file.getProject(); GlobalSearchScope scope = GlobalSearchScope.allScope(project); JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - return ContainerUtil.find(NullableNotNullManager.getInstance(project).getNullables(), s -> facade.findClass(s, scope) != null) == null; + return ContainerUtil.find( + NullableNotNullManager.getInstance(project).getNullables(), + s -> facade.findClass(s, scope) != null + ) == null; } - private void checkAccessors(PsiField field, - Annotated annotated, - Project project, - NullableNotNullManager manager, - String anno, - List annoToRemove, - ProblemsHolder holder, - NullableStuffInspectionState state) { + @RequiredReadAction + private void checkAccessors( + PsiField field, + Annotated annotated, + Project project, + NullableNotNullManager manager, + String anno, + List annoToRemove, + ProblemsHolder holder, + NullableStuffInspectionState state + ) { String propName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(field.getName(), VariableKind.FIELD); - boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); + boolean isStatic = field.isStatic(); PsiMethod getter = PropertyUtilBase.findPropertyGetter(field.getContainingClass(), propName, isStatic, false); PsiIdentifier nameIdentifier = getter == null ? null : getter.getNameIdentifier(); if (nameIdentifier != null && nameIdentifier.isPhysical()) { if (PropertyUtil.getFieldOfGetter(getter) == field) { - AddAnnotationModCommandAction getterAnnoFix = new AddAnnotationModCommandAction(anno, getter, ArrayUtil.toStringArray(annoToRemove)); - if (satte.REPORT_NOT_ANNOTATED_GETTER) { + AddAnnotationModCommandAction getterAnnoFix = + new AddAnnotationModCommandAction(anno, getter, ArrayUtil.toStringArray(annoToRemove)); + if (state.REPORT_NOT_ANNOTATED_GETTER) { if (!hasNullability(manager, getter) && !TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) { - reportProblem(holder, nameIdentifier, getterAnnoFix, "inspection.nullable.problems.annotated.field.getter.not.annotated", - StringUtil.getShortName(anno)); + reportProblem( + holder, + nameIdentifier, + getterAnnoFix, + JavaAnalysisLocalize.inspectionNullableProblemsAnnotatedFieldGetterNotAnnotated(StringUtil.getShortName(anno)) + ); } } if (annotated.isDeclaredNotNull && isNullableNotInferred(getter, false) || annotated.isDeclaredNullable && isNotNullNotInferred(getter, false, false)) { - reportProblem(holder, nameIdentifier, getterAnnoFix, - "inspection.nullable.problems.annotated.field.getter.conflict", getPresentableAnnoName(field), - getPresentableAnnoName(getter)); + reportProblem( + holder, + nameIdentifier, + getterAnnoFix, + JavaAnalysisLocalize.inspectionNullableProblemsAnnotatedFieldGetterConflict( + getPresentableAnnoName(field), + getPresentableAnnoName(getter) + ) + ); } } } @@ -745,40 +930,56 @@ annotated.isDeclaredNullable && isNotNullNotInferred(getter, false, false)) { PsiParameter parameter = parameters[0]; LOG.assertTrue(parameter != null, setter.getText()); AddAnnotationFix addAnnoFix = createAddAnnotationFix(anno, annoToRemove, parameter); - if (state.REPORT_NOT_ANNOTATED_GETTER && !hasNullability(manager, parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { + if (state.REPORT_NOT_ANNOTATED_GETTER && !hasNullability(manager, parameter) + && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { PsiIdentifier parameterName = parameter.getNameIdentifier(); assertValidElement(setter, parameter, parameterName); - reportProblem(holder, parameterName, addAnnoFix, - "inspection.nullable.problems.annotated.field.setter.parameter.not.annotated", getPresentableAnnoName(field)); + reportProblem( + holder, + parameterName, + addAnnoFix, + JavaAnalysisLocalize.inspectionNullableProblemsAnnotatedFieldSetterParameterNotAnnotated(getPresentableAnnoName(field)) + ); } - if (PropertyUtil.isSimpleSetter(setter)) { - if (annotated.isDeclaredNotNull && isNullableNotInferred(parameter, false)) { - PsiIdentifier parameterName = parameter.getNameIdentifier(); - assertValidElement(setter, parameter, parameterName); - reportProblem(holder, parameterName, addAnnoFix, - "inspection.nullable.problems.annotated.field.setter.parameter.conflict", - getPresentableAnnoName(field), getPresentableAnnoName(parameter)); - } + if (PropertyUtil.isSimpleSetter(setter) + && annotated.isDeclaredNotNull + && isNullableNotInferred(parameter, false)) { + PsiIdentifier parameterName = parameter.getNameIdentifier(); + assertValidElement(setter, parameter, parameterName); + reportProblem( + holder, + parameterName, + addAnnoFix, + JavaAnalysisLocalize.inspectionNullableProblemsAnnotatedFieldSetterParameterConflict( + getPresentableAnnoName(field), + getPresentableAnnoName(parameter) + ) + ); } } } + @RequiredReadAction private static AddAnnotationFix createAddAnnotationFix(String anno, List annoToRemove, PsiParameter parameter) { return new AddAnnotationFix(anno, parameter, annoToRemove.toArray(ArrayUtil.EMPTY_STRING_ARRAY)); } @Contract("_,_,null -> fail") + @RequiredReadAction private static void assertValidElement(PsiMethod setter, PsiParameter parameter, PsiIdentifier nameIdentifier1) { LOG.assertTrue(nameIdentifier1 != null && nameIdentifier1.isPhysical(), setter.getText()); LOG.assertTrue(parameter.isPhysical(), setter.getText()); } - private void checkConstructorParameters(PsiField field, - Annotated annotated, - String anno, - List annoToRemove, - ProblemsHolder holder, - NullableStuffInspectionState state) { + @RequiredReadAction + private void checkConstructorParameters( + PsiField field, + Annotated annotated, + String anno, + List annoToRemove, + ProblemsHolder holder, + NullableStuffInspectionState state + ) { List initializers = DfaPsiUtil.findAllConstructorInitializers(field); if (initializers.isEmpty()) { return; @@ -788,45 +989,56 @@ private void checkConstructorParameters(PsiField field, reportConstructorParameterFromField(field, anno, annoToRemove, holder, initializers); } - if (field.hasModifierProperty(PsiModifier.FINAL)) { + if (field.isFinal()) { checkFinalFieldInitializedNotNull(field, annotated, holder, initializers); } } - private void checkFinalFieldInitializedNotNull(PsiField field, Annotated annotated, - ProblemsHolder holder, List initializers) { + @RequiredReadAction + private void checkFinalFieldInitializedNotNull( + PsiField field, + Annotated annotated, + ProblemsHolder holder, + List initializers + ) { List notNullParams = new ArrayList<>(); for (PsiExpression rhs : initializers) { - if (rhs instanceof PsiReferenceExpression ref) { - PsiElement target = ref.resolve(); - if (isConstructorParameter(target) && target.isPhysical()) { - PsiParameter parameter = (PsiParameter) target; - if (annotated.isDeclaredNullable && isNotNullNotInferred(parameter, false, false)) { - notNullParams.add(parameter); - } - } + if (rhs instanceof PsiReferenceExpression ref + && ref.resolve() instanceof PsiParameter parameter + && isConstructorParameter(parameter) + && parameter.isPhysical() + && annotated.isDeclaredNullable + && isNotNullNotInferred(parameter, false, false)) { + notNullParams.add(parameter); } } if (notNullParams.size() != initializers.size()) { - // it's not the case that the field is and @Nullable and always initialized via parameters, - // so there might be other initializers that could justify it being nullable, + // it's not the case that the field is and @Nullable and always initialized via parameters, + // so there might be other initializers that could justify it being nullable, // so don't highlight field and constructor parameter annotation inconsistency return; } PsiIdentifier nameIdentifier = field.getNameIdentifier(); if (nameIdentifier.isPhysical()) { - reportProblem(holder, nameIdentifier, AddAnnotationModCommandAction.createAddNotNullFix(field), - "0.field.is.always.initialized.not.null", getPresentableAnnoName(field)); + reportProblem( + holder, + nameIdentifier, + AddAnnotationModCommandAction.createAddNotNullFix(field), + JavaAnalysisLocalize.zeroFieldIsAlwaysInitializedNotNull(getPresentableAnnoName(field)) + ); } } - private void reportConstructorParameterFromField(PsiField field, - String anno, - List annoToRemove, - ProblemsHolder holder, - List initializers) { + @RequiredReadAction + private void reportConstructorParameterFromField( + PsiField field, + String anno, + List annoToRemove, + ProblemsHolder holder, + List initializers + ) { Map> ctorToInitializers = StreamEx.of(initializers) .mapToEntry(e -> PsiTreeUtil.getParentOfType(e, PsiMethod.class), e -> e) .nonNullKeys() @@ -835,9 +1047,11 @@ private void reportConstructorParameterFromField(PsiField field, ctorToInitializers.forEach((ctor, exprs) -> { List parameters = StreamEx.of(exprs) - .map(e -> PsiUtil.skipParenthesizedExprDown(e) instanceof PsiReferenceExpression ref - ? tryCast(ref.resolve(), PsiParameter.class) - : null) + .map( + e -> PsiUtil.skipParenthesizedExprDown(e) instanceof PsiReferenceExpression ref + ? tryCast(ref.resolve(), PsiParameter.class) + : null + ) .distinct() .limit(2) .toList(); @@ -850,16 +1064,22 @@ private void reportConstructorParameterFromField(PsiField field, && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) { PsiIdentifier nameIdentifier = parameter.getNameIdentifier(); if (nameIdentifier != null && nameIdentifier.isPhysical()) { - reportProblem(holder, nameIdentifier, createAddAnnotationFix(anno, annoToRemove, parameter), - "inspection.nullable.problems.annotated.field.constructor.parameter.not.annotated", - StringUtil.getShortName(anno)); + reportProblem( + holder, + nameIdentifier, + createAddAnnotationFix(anno, annoToRemove, parameter), + JavaAnalysisLocalize.inspectionNullableProblemsAnnotatedFieldConstructorParameterNotAnnotated( + StringUtil.getShortName(anno) + ) + ); } } }); } private static boolean isConstructorParameter(@Nullable PsiElement parameter) { - return parameter instanceof PsiParameter && psiElement(PsiParameterList.class).withParent(psiMethod().constructor(true)).accepts(parameter.getParent()); + return parameter instanceof PsiParameter + && psiElement(PsiParameterList.class).withParent(psiMethod().constructor(true)).accepts(parameter.getParent()); } private static String getPresentableAnnoName(PsiModifierListOwner owner) { @@ -900,56 +1120,71 @@ private Annotated(@Nullable PsiAnnotation notNull, @Nullable PsiAnnotation nulla static Annotated from(PsiModifierListOwner owner) { NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject()); - NullabilityAnnotationInfo notNullInfo = manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NOT_NULL)); - NullabilityAnnotationInfo nullableInfo = manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NULLABLE)); - PsiAnnotation notNullAnno = notNullInfo == null || (notNullInfo.isContainer() && nullableInfo != null && !nullableInfo.isContainer()) - ? null : notNullInfo.getAnnotation(); - PsiAnnotation nullableAnno = nullableInfo == null || (nullableInfo.isContainer() && notNullInfo != null && !notNullInfo.isContainer()) - ? null : nullableInfo.getAnnotation(); + NullabilityAnnotationInfo notNullInfo = + manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NOT_NULL)); + NullabilityAnnotationInfo nullableInfo = + manager.findNullabilityAnnotationInfo(owner, Collections.singleton(Nullability.NULLABLE)); + PsiAnnotation notNullAnno = + notNullInfo == null || (notNullInfo.isContainer() && nullableInfo != null && !nullableInfo.isContainer()) + ? null : notNullInfo.getAnnotation(); + PsiAnnotation nullableAnno = + nullableInfo == null || (nullableInfo.isContainer() && notNullInfo != null && !notNullInfo.isContainer()) + ? null : nullableInfo.getAnnotation(); return new Annotated(notNullAnno, nullableAnno); } } + @RequiredReadAction private Annotated check(PsiModifierListOwner owner, ProblemsHolder holder, PsiType type) { Annotated annotated = Annotated.from(owner); PsiAnnotation annotation = annotated.notNull == null ? annotated.nullable : annotated.notNull; if (annotation != null && !annotation.isPhysical() && type instanceof PsiPrimitiveType) { - reportIncorrectLocation(holder, annotation, owner, "inspection.nullable.problems.primitive.type.annotation"); + reportIncorrectLocation(holder, annotation, owner, JavaAnalysisLocalize.inspectionNullableProblemsPrimitiveTypeAnnotation()); } if (owner instanceof PsiParameter parameter) { Nullability expectedNullability = DfaPsiUtil.inferParameterNullability(parameter); if (annotated.notNull != null && expectedNullability == Nullability.NULLABLE) { - reportParameterNullabilityMismatch(parameter, annotated.notNull, holder, "parameter.can.be.null"); + reportParameterNullabilityMismatch(parameter, annotated.notNull, holder, JavaAnalysisLocalize.parameterCanBeNull()); } else if (annotated.nullable != null && expectedNullability == Nullability.NOT_NULL) { - reportParameterNullabilityMismatch(parameter, annotated.nullable, holder, "parameter.is.always.not.null"); + reportParameterNullabilityMismatch(parameter, annotated.nullable, holder, JavaAnalysisLocalize.parameterIsAlwaysNotNull()); } } return annotated; } - private void reportParameterNullabilityMismatch(PsiParameter owner, - PsiAnnotation annotation, - ProblemsHolder holder, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String messageKey) { + @RequiredReadAction + private void reportParameterNullabilityMismatch( + PsiParameter owner, + PsiAnnotation annotation, + ProblemsHolder holder, + LocalizeValue message + ) { if (annotation.isPhysical() && !PsiTreeUtil.isAncestor(owner, annotation, true)) { return; } PsiElement anchor = annotation.isPhysical() ? annotation : owner.getNameIdentifier(); if (anchor != null && !anchor.getTextRange().isEmpty()) { - reportProblem(holder, anchor, new RemoveAnnotationQuickFix(annotation, owner), messageKey); + reportProblem(holder, anchor, new RemoveAnnotationQuickFix(annotation, owner), message); } } - private void reportIncorrectLocation(ProblemsHolder holder, PsiAnnotation annotation, - @Nullable PsiModifierListOwner listOwner, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String messageKey, - LocalQuickFix ... additionalFixes) { + private void reportIncorrectLocation( + ProblemsHolder holder, + PsiAnnotation annotation, + @Nullable PsiModifierListOwner listOwner, + LocalizeValue message, + LocalQuickFix... additionalFixes + ) { RemoveAnnotationQuickFix removeFix = new RemoveAnnotationQuickFix(annotation, listOwner, true); MoveAnnotationToBoundFix moveToBoundFix = MoveAnnotationToBoundFix.create(annotation); LocalQuickFix[] fixes = moveToBoundFix == null ? new LocalQuickFix[]{removeFix} : new LocalQuickFix[]{moveToBoundFix, removeFix}; - reportProblem(holder, !annotation.isPhysical() && listOwner != null ? listOwner.getNavigationElement() : annotation, - ArrayUtil.mergeArrays(additionalFixes, fixes), messageKey); + reportProblem( + holder, + !annotation.isPhysical() && listOwner != null ? listOwner.getNavigationElement() : annotation, + ArrayUtil.mergeArrays(additionalFixes, fixes), + message + ); } @Override @@ -962,13 +1197,14 @@ public String getShortName() { return "NullableProblems"; } - private void checkNullableStuffForMethod(PsiMethod method, - ProblemsHolder holder, - NullableStuffInspectionState state) { + @RequiredReadAction + private void checkNullableStuffForMethod(PsiMethod method, ProblemsHolder holder, NullableStuffInspectionState state) { Annotated annotated = check(method, holder, method.getReturnType()); List superMethods = ContainerUtil.map( - method.findSuperMethodSignaturesIncludingStatic(true), MethodSignatureBackedByPsiMethod::getMethod); + method.findSuperMethodSignaturesIncludingStatic(true), + MethodSignatureBackedByPsiMethod::getMethod + ); NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(holder.getProject()); @@ -978,11 +1214,13 @@ private void checkNullableStuffForMethod(PsiMethod method, checkConflictingContainerAnnotations(holder, method.getModifierList()); } - private void checkSupers(PsiMethod method, - ProblemsHolder holder, - Annotated annotated, - List superMethods, - NullableStuffInspectionState state) { + private void checkSupers( + PsiMethod method, + ProblemsHolder holder, + Annotated annotated, + List superMethods, + NullableStuffInspectionState state + ) { PsiIdentifier identifier = method.getNameIdentifier(); if (identifier == null) { return; @@ -990,22 +1228,37 @@ private void checkSupers(PsiMethod method, for (PsiMethod superMethod : superMethods) { if (isNullableOverridingNotNull(annotated, superMethod, state)) { PsiAnnotation annotation = findAnnotation(method, getNullityManager(method).getNullables(), true); - reportProblem(holder, annotation != null ? annotation : identifier, - "inspection.nullable.problems.Nullable.method.overrides.NotNull", - getPresentableAnnoName(method), getPresentableAnnoName(superMethod)); + reportProblem( + holder, + annotation != null ? annotation : identifier, + JavaAnalysisLocalize.inspectionNullableProblemsNullableMethodOverridesNotnull( + getPresentableAnnoName(method), + getPresentableAnnoName(superMethod) + ) + ); break; } if (isNonAnnotatedOverridingNotNull(method, superMethod, state)) { - reportProblem(holder, identifier, createFixForNonAnnotatedOverridesNotNull(method), - "inspection.nullable.problems.method.overrides.NotNull", getPresentableAnnoName(superMethod)); + reportProblem( + holder, + identifier, + createFixForNonAnnotatedOverridesNotNull(method), + JavaAnalysisLocalize.inspectionNullableProblemsMethodOverridesNotnull(getPresentableAnnoName(superMethod)) + ); break; } PsiTypeElement returnTypeElement = method.getReturnTypeElement(); - if (returnTypeElement != null && - checkNestedGenericClasses(holder, returnTypeElement, superMethod.getReturnType(), method.getReturnType(), - ConflictNestedTypeProblem.OVERRIDING_NESTED_TYPE_PROBLEM, state)) { + if (returnTypeElement != null + && checkNestedGenericClasses( + holder, + returnTypeElement, + superMethod.getReturnType(), + method.getReturnType(), + ConflictNestedTypeProblem.OVERRIDING_NESTED_TYPE_PROBLEM, + state + )) { break; } } @@ -1023,19 +1276,21 @@ private static NullableNotNullManager getNullityManager(PsiMethod method) { } private boolean isNullableOverridingNotNull(Annotated methodInfo, PsiMethod superMethod, NullableStuffInspectionState state) { - return state.REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE && methodInfo.isDeclaredNullable && isNotNullNotInferred(superMethod, true, false); + return state.REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE + && methodInfo.isDeclaredNullable + && isNotNullNotInferred(superMethod, true, false); } private boolean isNonAnnotatedOverridingNotNull(PsiMethod method, PsiMethod superMethod, NullableStuffInspectionState state) { - if (state.REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && - !(method.getReturnType() instanceof PsiPrimitiveType) && - !method.isConstructor()) { + if (state.REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL + && !(method.getReturnType() instanceof PsiPrimitiveType) + && !method.isConstructor()) { NullableNotNullManager manager = getNullityManager(method); NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(method); if ((info == null || info.isInferred() || - (info.getInheritedFrom() != null && manager.findContainerAnnotation(method) == null)) && - isNotNullNotInferred(superMethod, true, state.IGNORE_EXTERNAL_SUPER_NOTNULL) && - !hasInheritableNotNull(superMethod)) { + (info.getInheritedFrom() != null && manager.findContainerAnnotation(method) == null)) + && isNotNullNotInferred(superMethod, true, state.IGNORE_EXTERNAL_SUPER_NOTNULL) + && !hasInheritableNotNull(superMethod)) { return true; } } @@ -1046,11 +1301,13 @@ private static boolean hasInheritableNotNull(PsiModifierListOwner owner) { return isAnnotated(owner, "javax.annotation.constraints.NotNull", CHECK_HIERARCHY | CHECK_TYPE); } - private void checkParameters(PsiMethod method, - ProblemsHolder holder, - List superMethods, - NullableNotNullManager nullableManager, - NullableStuffInspectionState state) { + private void checkParameters( + PsiMethod method, + ProblemsHolder holder, + List superMethods, + NullableNotNullManager nullableManager, + NullableStuffInspectionState state + ) { PsiParameter[] parameters = method.getParameterList().getParameters(); for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; @@ -1077,11 +1334,13 @@ private static List getSuperParameters(List s return superParameters; } - private void checkSuperParameterAnnotations(ProblemsHolder holder, - NullableNotNullManager nullableManager, - PsiParameter parameter, - List superParameters, - NullableStuffInspectionState state) { + private void checkSuperParameterAnnotations( + ProblemsHolder holder, + NullableNotNullManager nullableManager, + PsiParameter parameter, + List superParameters, + NullableStuffInspectionState state + ) { PsiIdentifier nameIdentifier = parameter.getNameIdentifier(); if (nameIdentifier == null) { return; @@ -1089,17 +1348,26 @@ private void checkSuperParameterAnnotations(ProblemsHolder holder, PsiParameter nullableSuper = findNullableSuperForNotNullParameter(parameter, superParameters, PsiSubstitutor.EMPTY, state); if (nullableSuper != null) { PsiAnnotation annotation = findAnnotation(parameter, nullableManager.getNotNulls(), true); - reportProblem(holder, annotation != null ? annotation : nameIdentifier, - "inspection.nullable.problems.NotNull.parameter.overrides.Nullable", - getPresentableAnnoName(parameter), getPresentableAnnoName(nullableSuper)); + reportProblem( + holder, + annotation != null ? annotation : nameIdentifier, + JavaAnalysisLocalize.inspectionNullableProblemsNotnullParameterOverridesNullable( + getPresentableAnnoName(parameter), + getPresentableAnnoName(nullableSuper) + ) + ); } PsiParameter notNullSuper = findNotNullSuperForNonAnnotatedParameter(nullableManager, parameter, superParameters, state); if (notNullSuper != null) { LocalQuickFix fix = isAnnotatingApplicable(parameter, nullableManager.getDefaultAnnotation(Nullability.NOT_NULL, parameter)) ? LocalQuickFix.from(AddAnnotationModCommandAction.createAddNotNullFix(parameter)) : null; - reportProblem(holder, nameIdentifier, fix, - "inspection.nullable.problems.parameter.overrides.NotNull", getPresentableAnnoName(notNullSuper)); + reportProblem( + holder, + nameIdentifier, + fix, + JavaAnalysisLocalize.inspectionNullableProblemsParameterOverridesNotnull(getPresentableAnnoName(notNullSuper)) + ); } if (isNotNullParameterOverridingNonAnnotated(nullableManager, parameter, superParameters, PsiSubstitutor.EMPTY, state)) { NullabilityAnnotationInfo info = nullableManager.findEffectiveNullabilityInfo(parameter); @@ -1107,35 +1375,51 @@ private void checkSuperParameterAnnotations(ProblemsHolder holder, PsiAnnotation notNullAnnotation = info.getAnnotation(); boolean physical = PsiTreeUtil.isAncestor(parameter, notNullAnnotation, true); LocalQuickFix fix = physical ? new RemoveAnnotationQuickFix(notNullAnnotation, parameter) : null; - reportProblem(holder, physical ? notNullAnnotation : nameIdentifier, fix, - "inspection.nullable.problems.NotNull.parameter.overrides.not.annotated", getPresentableAnnoName(parameter)); + reportProblem( + holder, + physical ? notNullAnnotation : nameIdentifier, + fix, + JavaAnalysisLocalize.inspectionNullableProblemsNotnullParameterOverridesNotAnnotated(getPresentableAnnoName(parameter)) + ); } PsiTypeElement typeElement = parameter.getTypeElement(); if (typeElement != null) { for (PsiParameter superParameter : superParameters) { - if (checkNestedGenericClasses(holder, typeElement, parameter.getType(), superParameter.getType(), - ConflictNestedTypeProblem.OVERRIDING_NESTED_TYPE_PROBLEM, state)) { + if (checkNestedGenericClasses( + holder, + typeElement, + parameter.getType(), + superParameter.getType(), + ConflictNestedTypeProblem.OVERRIDING_NESTED_TYPE_PROBLEM, + state + )) { break; } } } } - private @Nullable PsiParameter findNotNullSuperForNonAnnotatedParameter(NullableNotNullManager nullableManager, - PsiParameter parameter, - List superParameters, - NullableStuffInspectionState state) { + private @Nullable PsiParameter findNotNullSuperForNonAnnotatedParameter( + NullableNotNullManager nullableManager, + PsiParameter parameter, + List superParameters, + NullableStuffInspectionState state + ) { return state.REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && !hasNullability(nullableManager, parameter) - ? ContainerUtil.find(superParameters, - sp -> isNotNullNotInferred(sp, false, state.IGNORE_EXTERNAL_SUPER_NOTNULL) && !hasInheritableNotNull(sp)) + ? ContainerUtil.find( + superParameters, + sp -> isNotNullNotInferred(sp, false, state.IGNORE_EXTERNAL_SUPER_NOTNULL) && !hasInheritableNotNull(sp) + ) : null; } - private @Nullable PsiParameter findNullableSuperForNotNullParameter(PsiParameter parameter, - List superParameters, - PsiSubstitutor superSubstitutor, - NullableStuffInspectionState state) { + private @Nullable PsiParameter findNullableSuperForNotNullParameter( + PsiParameter parameter, + List superParameters, + PsiSubstitutor superSubstitutor, + NullableStuffInspectionState state + ) { if (!state.REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE || !isNotNullNotInferred(parameter, false, false)) { return null; } @@ -1156,26 +1440,37 @@ private void checkSuperParameterAnnotations(ProblemsHolder holder, return null; } - private boolean isNotNullParameterOverridingNonAnnotated(NullableNotNullManager nullableManager, - PsiParameter parameter, - List superParameters, - PsiSubstitutor substitutor, - NullableStuffInspectionState state) { + private boolean isNotNullParameterOverridingNonAnnotated( + NullableNotNullManager nullableManager, + PsiParameter parameter, + List superParameters, + PsiSubstitutor substitutor, + NullableStuffInspectionState state + ) { if (!state.REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED) { return false; } NullabilityAnnotationInfo info = nullableManager.findEffectiveNullabilityInfo(parameter); - return info != null && info.getNullability() == Nullability.NOT_NULL && info.getInheritedFrom() == null && !info.isInferred() && - ContainerUtil.exists(superParameters, sp -> isSuperNotAnnotated(nullableManager, parameter, sp, substitutor)); + return info != null + && info.getNullability() == Nullability.NOT_NULL + && info.getInheritedFrom() == null + && !info.isInferred() + && ContainerUtil.exists(superParameters, sp -> isSuperNotAnnotated(nullableManager, parameter, sp, substitutor)); } - private static boolean isSuperNotAnnotated(NullableNotNullManager nullableManager, PsiParameter parameter, - PsiParameter superParameter, PsiSubstitutor superSubstitutor) { + private static boolean isSuperNotAnnotated( + NullableNotNullManager nullableManager, + PsiParameter parameter, + PsiParameter superParameter, + PsiSubstitutor superSubstitutor + ) { if (hasNullability(nullableManager, superParameter)) { return false; } - if (ContainerUtil.exists(getSuperAnnotationOwners(superParameter), - superSuperParameter -> hasNullability(nullableManager, superSuperParameter))) { + if (ContainerUtil.exists( + getSuperAnnotationOwners(superParameter), + superSuperParameter -> hasNullability(nullableManager, superSuperParameter) + )) { return false; } PsiType type = superParameter.getType(); @@ -1195,12 +1490,14 @@ private static boolean isSuperNotAnnotated(NullableNotNullManager nullableManage return true; } - private void checkNullLiteralArgumentOfNotNullParameterUsages(PsiMethod method, - ProblemsHolder holder, - NullableNotNullManager nullableManager, - int parameterIdx, - PsiParameter parameter, - NullableStuffInspectionState state) { + private void checkNullLiteralArgumentOfNotNullParameterUsages( + PsiMethod method, + ProblemsHolder holder, + NullableNotNullManager nullableManager, + int parameterIdx, + PsiParameter parameter, + NullableStuffInspectionState state + ) { if (!state.REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER || !holder.isOnTheFly()) { return; } @@ -1229,16 +1526,24 @@ private void checkNullLiteralArgumentOfNotNullParameterUsages(PsiMethod method, return; } - reportProblem(holder, elementToHighlight, createNavigateToNullParameterUsagesFix(parameter), - "inspection.nullable.problems.NotNull.parameter.receives.null.literal", - StringUtil.getShortName(Objects.requireNonNull(info.getAnnotation().getQualifiedName()))); + reportProblem( + holder, + elementToHighlight, + createNavigateToNullParameterUsagesFix(parameter), + JavaAnalysisLocalize.inspectionNullableProblemsNotnullParameterReceivesNullLiteral( + StringUtil.getShortName(Objects.requireNonNull(info.getAnnotation().getQualifiedName())) + ) + ); } - private void checkOverriders(PsiMethod method, - ProblemsHolder holder, - Annotated annotated, - NullableNotNullManager nullableManager, - NullableStuffInspectionState state) { + @RequiredReadAction + private void checkOverriders( + PsiMethod method, + ProblemsHolder holder, + Annotated annotated, + NullableNotNullManager nullableManager, + NullableStuffInspectionState state + ) { PsiParameter[] parameters = method.getParameterList().getParameters(); if (state.REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS) { boolean[] checkParameter = new boolean[parameters.length]; @@ -1246,12 +1551,13 @@ private void checkOverriders(PsiMethod method, boolean hasAnnotatedParameter = false; for (int i = 0; i < parameters.length; i++) { PsiParameter parameter = parameters[i]; - checkParameter[i] = isNotNullNotInferred(parameter, false, false) && - !hasInheritableNotNull(parameter) && - !(parameter.getType() instanceof PsiPrimitiveType); + checkParameter[i] = isNotNullNotInferred(parameter, false, false) + && !hasInheritableNotNull(parameter) + && !(parameter.getType() instanceof PsiPrimitiveType); hasAnnotatedParameter |= checkParameter[i]; } - boolean checkReturnType = annotated.isDeclaredNotNull && !hasInheritableNotNull(method) && !(method.getReturnType() instanceof PsiPrimitiveType); + boolean checkReturnType = + annotated.isDeclaredNotNull && !hasInheritableNotNull(method) && !(method.getReturnType() instanceof PsiPrimitiveType); if (hasAnnotatedParameter || checkReturnType) { PsiMethod[] overridings = OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY); @@ -1284,7 +1590,12 @@ private void checkOverriders(PsiMethod method, continue; } } - reportProblem(holder, psiElement, fix, "nullable.stuff.problems.overridden.methods.are.not.annotated"); + reportProblem( + holder, + psiElement, + fix, + JavaAnalysisLocalize.nullableStuffProblemsOverriddenMethodsAreNotAnnotated() + ); methodQuickFixSuggested = true; } } @@ -1299,7 +1610,8 @@ private void checkOverriders(PsiMethod method, !isNotNullNotInferred(parameter, false, false) && !isNullableNotInferred(parameter, false) && AddAnnotationPsiFix.isAvailable(parameter, defaultNotNull)) { - PsiIdentifier identifier = parameters[i].getNameIdentifier(); //be sure that corresponding tree element available + PsiIdentifier identifier = + parameters[i].getNameIdentifier(); //be sure that corresponding tree element available NullabilityAnnotationInfo info = nullableManager.findOwnNullabilityInfo(parameters[i]); PsiElement psiElement = info == null ? null : info.getAnnotation(); if (psiElement == null || !psiElement.isPhysical()) { @@ -1311,8 +1623,12 @@ private void checkOverriders(PsiMethod method, LocalQuickFix fix = isAnnotatingApplicable(parameter, defaultNotNull) ? new AnnotateOverriddenMethodParameterFix(Nullability.NOT_NULL) : null; - reportProblem(holder, psiElement, fix, - "nullable.stuff.problems.overridden.method.parameters.are.not.annotated"); + reportProblem( + holder, + psiElement, + fix, + JavaAnalysisLocalize.nullableStuffProblemsOverriddenMethodParametersAreNotAnnotated() + ); parameterQuickFixSuggested[i] = true; } } @@ -1322,6 +1638,7 @@ private void checkOverriders(PsiMethod method, } } + @RequiredReadAction public static boolean shouldSkipOverriderAsGenerated(PsiMethod overriding) { if (Registry.is("idea.report.nullity.missing.in.generated.overriders")) { return false; @@ -1352,23 +1669,25 @@ public static boolean isNullableNotInferred(PsiModifierListOwner owner, boolean Project project = owner.getProject(); NullableNotNullManager manager = NullableNotNullManager.getInstance(project); NullabilityAnnotationInfo info = manager.findEffectiveNullabilityInfo(owner); - return info != null && !info.isInferred() && info.getNullability() == Nullability.NULLABLE && - (checkBases || info.getInheritedFrom() == null); + return info != null + && !info.isInferred() + && info.getNullability() == Nullability.NULLABLE + && (checkBases || info.getInheritedFrom() == null); } private static class MyAnnotateMethodFix implements LocalQuickFix { private Nullability myNullability; private String[] myAnnotationsToRemove; - MyAnnotateMethodFix(Nullability nullability, String ... annotationsToRemove) { + MyAnnotateMethodFix(Nullability nullability, String... annotationsToRemove) { myNullability = nullability; myAnnotationsToRemove = annotationsToRemove.length == 0 ? ArrayUtil.EMPTY_STRING_ARRAY : annotationsToRemove; } - @Override - public String getFamilyName() { - return JavaAnalysisBundle.message("inspection.annotate.overridden.method.quickfix.family.name"); - } +// @Override +// public String getFamilyName() { +// return JavaAnalysisLocalize.inspectionAnnotateOverriddenMethodQuickfixFamilyName().get(); +// } @Override public boolean startInWriteAction() { @@ -1376,6 +1695,7 @@ public boolean startInWriteAction() { } @Override + @RequiredUIAccess public void applyFix(Project project, ProblemDescriptor descriptor) { PsiElement psiElement = descriptor.getPsiElement(); @@ -1386,17 +1706,20 @@ public void applyFix(Project project, ProblemDescriptor descriptor) { List toAnnotate = new ArrayList<>(); NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - if (!AnnotateOverriddenMethodParameterFix.processModifiableInheritorsUnderProgress(method, (Consumer) psiMethod -> { - 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); + if (!AnnotateOverriddenMethodParameterFix.processModifiableInheritorsUnderProgress( + method, + (Consumer) psiMethod -> { + 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); + } } - })) { + )) { return; } @@ -1410,49 +1733,45 @@ public void applyFix(Project project, ProblemDescriptor descriptor) { } @Override - public String getName() { - return JavaAnalysisBundle.message("inspection.annotate.overridden.method.nullable.quickfix.name"); + public LocalizeValue getName() { + return JavaAnalysisLocalize.inspectionAnnotateOverriddenMethodNullableQuickfixName(/*Short name of annotation here*/); } } - private enum ConflictNestedTypeProblem { - RETURN_NESTED_TYPE_PROBLEM("returning.a.class.with.notnull.arguments", "returning.a.class.with.nullable.arguments"), - ASSIGNMENT_NESTED_TYPE_PROBLEM("assigning.a.class.with.notnull.elements", "assigning.a.class.with.nullable.elements"), - OVERRIDING_NESTED_TYPE_PROBLEM("overriding.a.class.with.notnull.elements", "overriding.a.class.with.nullable.elements"), - ; - - @NotNull - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) - private String notNullToNullProblemMessage; - - @NotNull - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) - private String nullToNotNullProblemMessage; - @NotNull - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) - private String complexProblem; - - ConflictNestedTypeProblem(@PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String notNullToNullProblemMessage, - @PropertyKey(resourceBundle = JavaAnalysisBundle.BUNDLE) String nullToNotNullProblemMessage) { + RETURN_NESTED_TYPE_PROBLEM( + JavaAnalysisLocalize::returningAClassWithNotnullArguments, + JavaAnalysisLocalize::returningAClassWithNullableArguments + ), + ASSIGNMENT_NESTED_TYPE_PROBLEM( + JavaAnalysisLocalize::assigningAClassWithNotnullElements, + JavaAnalysisLocalize::assigningAClassWithNullableElements + ), + OVERRIDING_NESTED_TYPE_PROBLEM( + JavaAnalysisLocalize::overridingAClassWithNotnullElements, + JavaAnalysisLocalize::overridingAClassWithNullableElements + ); + + private Function notNullToNullProblemMessage; + private Function nullToNotNullProblemMessage; + private Function myComplexProblem; + + ConflictNestedTypeProblem(Function notNullToNullProblemMessage, Function nullToNotNullProblemMessage) { this.notNullToNullProblemMessage = notNullToNullProblemMessage; this.nullToNotNullProblemMessage = nullToNotNullProblemMessage; - this.complexProblem = "complex.problem.with.nullability"; + this.myComplexProblem = JavaAnalysisLocalize::complexProblemWithNullability; } - @NotNull - String notNullToNullProblem() { - return notNullToNullProblemMessage; + LocalizeValue notNullToNullProblem(Object param) { + return notNullToNullProblemMessage.apply(param); } - @NotNull - String nullToNotNullProblem() { - return nullToNotNullProblemMessage; + LocalizeValue nullToNotNullProblem(Object param) { + return nullToNotNullProblemMessage.apply(param); } - @NotNull - String complexProblem() { - return complexProblem; + LocalizeValue complexProblem(Object param) { + return myComplexProblem.apply(param); } } } \ No newline at end of file