2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInspection.nullable;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.codeInsight.NullableNotNullManager;
20 import com.intellij.codeInsight.daemon.GroupNames;
21 import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
22 import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
23 import com.intellij.codeInsight.intention.impl.AddNotNullAnnotationFix;
24 import com.intellij.codeInspection.*;
25 import com.intellij.codeInspection.dataFlow.DfaPsiUtil;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.util.WriteExternalException;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.psi.*;
31 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
32 import com.intellij.psi.codeStyle.VariableKind;
33 import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.searches.OverridingMethodsSearch;
36 import com.intellij.psi.util.*;
37 import com.intellij.util.ArrayUtil;
38 import com.intellij.util.containers.ContainerUtil;
39 import org.jdom.Element;
40 import org.jetbrains.annotations.NotNull;
43 import java.util.ArrayList;
44 import java.util.List;
47 public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionTool {
48 // deprecated fields remain to minimize changes to users inspection profiles (which are often located in version control).
49 @Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLABLE_METHOD_OVERRIDES_NOTNULL = true;
50 @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL = true;
51 @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE = true;
52 @Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_PARAMETER_OVERRIDES_NOTNULL = true;
53 @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_GETTER = true;
54 @SuppressWarnings({"WeakerAccess"}) public boolean IGNORE_EXTERNAL_SUPER_NOTNULL;
55 @SuppressWarnings({"WeakerAccess"}) public boolean REQUIRE_NOTNULL_FIELDS_INITIALIZED = true;
56 @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED;
57 @Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NOT_ANNOTATED_SETTER_PARAMETER = true;
58 @Deprecated @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS = true; // remains for test
59 @SuppressWarnings({"WeakerAccess"}) public boolean REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD = true;
60 public boolean REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER = true;
62 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.nullable.NullableStuffInspectionBase");
65 public void writeSettings(@NotNull Element node) throws WriteExternalException {
66 super.writeSettings(node);
67 for (Element child : new ArrayList<>(node.getChildren())) {
68 String name = child.getAttributeValue("name");
69 String value = child.getAttributeValue("value");
70 if ("IGNORE_EXTERNAL_SUPER_NOTNULL".equals(name) && "false".equals(value) ||
71 "REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED".equals(name) && "false".equals(value) ||
72 "REQUIRE_NOTNULL_FIELDS_INITIALIZED".equals(name) && "true".equals(value) ||
73 "REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER".equals(name) && "true".equals(value)) {
74 node.removeContent(child);
81 public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
82 final PsiFile file = holder.getFile();
83 if (!PsiUtil.isLanguageLevel5OrHigher(file) || nullabilityAnnotationsNotAvailable(file)) {
84 return new PsiElementVisitor() { };
86 return new JavaElementVisitor() {
88 public void visitMethod(PsiMethod method) {
89 checkNullableStuffForMethod(method, holder, isOnTheFly);
93 public void visitField(PsiField field) {
94 final PsiType type = field.getType();
95 final Annotated annotated = check(field, holder, type);
96 if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
99 Project project = holder.getProject();
100 final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
101 if (annotated.isDeclaredNotNull ^ annotated.isDeclaredNullable) {
102 final String anno = annotated.isDeclaredNotNull ? manager.getDefaultNotNull() : manager.getDefaultNullable();
103 final List<String> annoToRemove = annotated.isDeclaredNotNull ? manager.getNullables() : manager.getNotNulls();
105 if (!checkNonStandardAnnotations(field, annotated, manager, anno, holder)) return;
107 checkAccessors(field, annotated, project, manager, anno, annoToRemove, holder);
109 checkConstructorParameters(field, annotated, manager, anno, annoToRemove, holder);
112 if (REQUIRE_NOTNULL_FIELDS_INITIALIZED && !annotated.isDeclaredNullable) {
113 checkNotNullFieldsInitialized(field, manager, holder);
118 public void visitParameter(PsiParameter parameter) {
119 check(parameter, holder, parameter.getType());
123 public void visitAnnotation(PsiAnnotation annotation) {
124 if (!AnnotationUtil.NOT_NULL.equals(annotation.getQualifiedName())) return;
126 PsiAnnotationMemberValue value = annotation.findDeclaredAttributeValue("exception");
127 if (value instanceof PsiClassObjectAccessExpression) {
128 PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(((PsiClassObjectAccessExpression)value).getOperand().getType());
129 if (psiClass != null && !hasStringConstructor(psiClass)) {
130 //noinspection DialogTitleCapitalization
131 holder.registerProblem(value,
132 "Custom exception class should have a constructor with a single message parameter of String type",
133 ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
139 private boolean hasStringConstructor(PsiClass aClass) {
140 for (PsiMethod method : aClass.getConstructors()) {
141 PsiParameterList list = method.getParameterList();
142 if (list.getParametersCount() == 1 &&
143 list.getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
152 protected LocalQuickFix createNavigateToNullParameterUsagesFix(PsiParameter parameter) {
156 private static boolean nullabilityAnnotationsNotAvailable(final PsiFile file) {
157 final Project project = file.getProject();
158 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
159 final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
160 return ContainerUtil.find(NullableNotNullManager.getInstance(project).getNullables(), s -> facade.findClass(s, scope) != null) == null;
163 private static boolean checkNonStandardAnnotations(PsiField field,
165 NullableNotNullManager manager, String anno, @NotNull ProblemsHolder holder) {
166 if (!AnnotationUtil.isAnnotatingApplicable(field, anno)) {
167 final PsiAnnotation notNull = AnnotationUtil.findAnnotation(field, manager.getNotNulls());
168 final PsiAnnotation nullable = AnnotationUtil.findAnnotation(field, manager.getNullables());
169 final PsiAnnotation annotation;
170 String message = "Not \'";
171 if (annotated.isDeclaredNullable) {
172 message += nullable.getQualifiedName();
173 annotation = nullable;
175 message += notNull.getQualifiedName();
176 annotation = notNull;
178 message += "\' but \'" + anno + "\' would be used for code generation.";
179 final PsiJavaCodeReferenceElement annotationNameReferenceElement = annotation.getNameReferenceElement();
180 holder.registerProblem(annotationNameReferenceElement != null && annotationNameReferenceElement.isPhysical() ? annotationNameReferenceElement : field.getNameIdentifier(),
182 ProblemHighlightType.WEAK_WARNING,
183 new ChangeNullableDefaultsFix(notNull, nullable, manager));
189 private void checkAccessors(PsiField field,
192 NullableNotNullManager manager, final String anno, final List<String> annoToRemove, @NotNull ProblemsHolder holder) {
193 String propName = JavaCodeStyleManager.getInstance(project).variableNameToPropertyName(field.getName(), VariableKind.FIELD);
194 final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
195 final PsiMethod getter = PropertyUtil.findPropertyGetter(field.getContainingClass(), propName, isStatic, false);
196 final PsiIdentifier nameIdentifier = getter == null ? null : getter.getNameIdentifier();
197 if (nameIdentifier != null && nameIdentifier.isPhysical()) {
198 if (PropertyUtil.isSimpleGetter(getter)) {
199 AnnotateMethodFix getterAnnoFix = new AnnotateMethodFix(anno, ArrayUtil.toStringArray(annoToRemove)) {
201 public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) {
205 if (REPORT_NOT_ANNOTATED_GETTER) {
206 if (!manager.hasNullability(getter) && !TypeConversionUtil.isPrimitiveAndNotNull(getter.getReturnType())) {
207 holder.registerProblem(nameIdentifier, InspectionsBundle
208 .message("inspection.nullable.problems.annotated.field.getter.not.annotated", getPresentableAnnoName(field)),
209 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix);
212 if (annotated.isDeclaredNotNull && isNullableNotInferred(getter, false) ||
213 annotated.isDeclaredNullable && isNotNullNotInferred(getter, false, false)) {
214 holder.registerProblem(nameIdentifier, InspectionsBundle.message(
215 "inspection.nullable.problems.annotated.field.getter.conflict", getPresentableAnnoName(field), getPresentableAnnoName(getter)),
216 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, getterAnnoFix);
221 final PsiClass containingClass = field.getContainingClass();
222 final PsiMethod setter = PropertyUtil.findPropertySetter(containingClass, propName, isStatic, false);
223 if (setter != null && setter.isPhysical()) {
224 final PsiParameter[] parameters = setter.getParameterList().getParameters();
225 assert parameters.length == 1 : setter.getText();
226 final PsiParameter parameter = parameters[0];
227 LOG.assertTrue(parameter != null, setter.getText());
228 AddAnnotationPsiFix addAnnoFix = createAddAnnotationFix(anno, annoToRemove, parameter);
229 if (REPORT_NOT_ANNOTATED_GETTER && !manager.hasNullability(parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
230 final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
231 assertValidElement(setter, parameter, nameIdentifier1);
232 holder.registerProblem(nameIdentifier1,
233 InspectionsBundle.message("inspection.nullable.problems.annotated.field.setter.parameter.not.annotated",
234 getPresentableAnnoName(field)),
235 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
238 if (PropertyUtil.isSimpleSetter(setter)) {
239 if (annotated.isDeclaredNotNull && isNullableNotInferred(parameter, false)) {
240 final PsiIdentifier nameIdentifier1 = parameter.getNameIdentifier();
241 assertValidElement(setter, parameter, nameIdentifier1);
242 holder.registerProblem(nameIdentifier1, InspectionsBundle.message(
243 "inspection.nullable.problems.annotated.field.setter.parameter.conflict",
244 getPresentableAnnoName(field), getPresentableAnnoName(parameter)),
245 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
253 private static AddAnnotationPsiFix createAddAnnotationFix(String anno, List<String> annoToRemove, PsiParameter parameter) {
254 return new AddAnnotationPsiFix(anno, parameter, PsiNameValuePair.EMPTY_ARRAY, ArrayUtil.toStringArray(annoToRemove));
257 private static void assertValidElement(PsiMethod setter, PsiParameter parameter, PsiIdentifier nameIdentifier1) {
258 LOG.assertTrue(nameIdentifier1 != null && nameIdentifier1.isPhysical(), setter.getText());
259 LOG.assertTrue(parameter.isPhysical(), setter.getText());
262 private static void checkNotNullFieldsInitialized(PsiField field, NullableNotNullManager manager, @NotNull ProblemsHolder holder) {
263 PsiAnnotation annotation = manager.getNotNullAnnotation(field, false);
264 if (annotation == null || HighlightControlFlowUtil.isFieldInitializedAfterObjectConstruction(field)) return;
266 boolean byDefault = manager.isContainerAnnotation(annotation);
267 PsiJavaCodeReferenceElement name = annotation.getNameReferenceElement();
268 holder.registerProblem(annotation.isPhysical() && !byDefault ? annotation : field.getNameIdentifier(),
269 (byDefault && name != null ? "@" + name.getReferenceName() : "Not-null") + " fields must be initialized");
272 private void checkConstructorParameters(PsiField field,
274 NullableNotNullManager manager,
275 String anno, List<String> annoToRemove, @NotNull ProblemsHolder holder) {
276 List<PsiExpression> initializers = DfaPsiUtil.findAllConstructorInitializers(field);
277 if (initializers.isEmpty()) return;
279 List<PsiParameter> notNullParams = ContainerUtil.newArrayList();
281 boolean isFinal = field.hasModifierProperty(PsiModifier.FINAL);
283 for (PsiExpression rhs : initializers) {
284 if (rhs instanceof PsiReferenceExpression) {
285 PsiElement target = ((PsiReferenceExpression)rhs).resolve();
286 if (target instanceof PsiParameter && target.isPhysical()) {
287 PsiParameter parameter = (PsiParameter)target;
288 if (REPORT_NOT_ANNOTATED_GETTER && !manager.hasNullability(parameter) && !TypeConversionUtil.isPrimitiveAndNotNull(parameter.getType())) {
289 final PsiIdentifier nameIdentifier = parameter.getNameIdentifier();
290 if (nameIdentifier != null && nameIdentifier.isPhysical()) {
291 holder.registerProblem(
293 InspectionsBundle.message("inspection.nullable.problems.annotated.field.constructor.parameter.not.annotated", getPresentableAnnoName(field)),
294 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, createAddAnnotationFix(anno, annoToRemove, parameter));
299 if (isFinal && annotated.isDeclaredNullable && isNotNullNotInferred(parameter, false, false)) {
300 notNullParams.add(parameter);
307 if (notNullParams.size() != initializers.size()) {
308 // it's not the case that the field is final and @Nullable and always initialized via @NotNull parameters
309 // so there might be other initializers that could justify it being nullable
310 // so don't highlight field and constructor parameter annotation inconsistency
314 PsiIdentifier nameIdentifier = field.getNameIdentifier();
315 if (nameIdentifier.isPhysical()) {
316 holder.registerProblem(nameIdentifier, "@" + getPresentableAnnoName(field) + " field is always initialized not-null",
317 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new AddNotNullAnnotationFix(field));
322 private static String getPresentableAnnoName(@NotNull PsiModifierListOwner owner) {
323 NullableNotNullManager manager = NullableNotNullManager.getInstance(owner.getProject());
324 Set<String> names = ContainerUtil.newHashSet(manager.getNullables());
325 names.addAll(manager.getNotNulls());
327 PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(owner, names);
328 if (annotation != null) return getPresentableAnnoName(annotation);
330 String anno = manager.getNotNull(owner);
331 return StringUtil.getShortName(anno != null ? anno : StringUtil.notNullize(manager.getNullable(owner), "???"));
334 public static String getPresentableAnnoName(@NotNull PsiAnnotation annotation) {
335 return StringUtil.getShortName(StringUtil.notNullize(annotation.getQualifiedName(), "???"));
338 private static class Annotated {
339 private final boolean isDeclaredNotNull;
340 private final boolean isDeclaredNullable;
342 private Annotated(final boolean isDeclaredNotNull, final boolean isDeclaredNullable) {
343 this.isDeclaredNotNull = isDeclaredNotNull;
344 this.isDeclaredNullable = isDeclaredNullable;
347 private static Annotated check(final PsiModifierListOwner parameter, final ProblemsHolder holder, PsiType type) {
348 final NullableNotNullManager manager = NullableNotNullManager.getInstance(holder.getProject());
349 PsiAnnotation isDeclaredNotNull = AnnotationUtil.findAnnotation(parameter, manager.getNotNulls());
350 PsiAnnotation isDeclaredNullable = AnnotationUtil.findAnnotation(parameter, manager.getNullables());
351 if (isDeclaredNullable != null && isDeclaredNotNull != null) {
352 reportNullableNotNullConflict(holder, parameter, isDeclaredNullable, isDeclaredNotNull);
354 if ((isDeclaredNotNull != null || isDeclaredNullable != null) && type != null && TypeConversionUtil.isPrimitive(type.getCanonicalText())) {
355 PsiAnnotation annotation = isDeclaredNotNull == null ? isDeclaredNullable : isDeclaredNotNull;
356 reportPrimitiveType(holder, annotation, annotation, parameter);
358 return new Annotated(isDeclaredNotNull != null,isDeclaredNullable != null);
361 private static void reportPrimitiveType(final ProblemsHolder holder, final PsiElement psiElement, final PsiAnnotation annotation,
362 final PsiModifierListOwner listOwner) {
363 holder.registerProblem(psiElement.isPhysical() ? psiElement : listOwner.getNavigationElement(),
364 InspectionsBundle.message("inspection.nullable.problems.primitive.type.annotation"),
365 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(annotation, listOwner));
370 public String getDisplayName() {
371 return InspectionsBundle.message("inspection.nullable.problems.display.name");
376 public String getGroupDisplayName() {
377 return GroupNames.BUGS_GROUP_NAME;
382 public String getShortName() {
383 return "NullableProblems";
386 private void checkNullableStuffForMethod(PsiMethod method, final ProblemsHolder holder, boolean isOnFly) {
387 Annotated annotated = check(method, holder, method.getReturnType());
389 List<PsiMethod> superMethods = ContainerUtil.map(
390 method.findSuperMethodSignaturesIncludingStatic(true), signature -> signature.getMethod());
392 final NullableNotNullManager nullableManager = NullableNotNullManager.getInstance(holder.getProject());
394 checkSupers(method, holder, annotated, superMethods, nullableManager);
395 checkParameters(method, holder, superMethods, nullableManager, isOnFly);
396 checkOverriders(method, holder, annotated, nullableManager);
399 private void checkSupers(PsiMethod method,
400 ProblemsHolder holder,
402 List<PsiMethod> superMethods, NullableNotNullManager nullableManager) {
403 if (REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE) {
404 for (PsiMethod superMethod : superMethods) {
405 if (annotated.isDeclaredNullable && isNotNullNotInferred(superMethod, true, false)) {
406 final PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, nullableManager.getNullables(), true);
407 holder.registerProblem(annotation != null ? annotation : method.getNameIdentifier(),
408 InspectionsBundle.message("inspection.nullable.problems.Nullable.method.overrides.NotNull",
409 getPresentableAnnoName(method), getPresentableAnnoName(superMethod)),
410 ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
416 if (REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
417 for (PsiMethod superMethod : superMethods) {
418 if (!nullableManager.hasNullability(method) && isNotNullNotInferred(superMethod, true, IGNORE_EXTERNAL_SUPER_NOTNULL)) {
419 final String defaultNotNull = nullableManager.getDefaultNotNull();
420 final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
421 final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull)
422 ? createAnnotateMethodFix(defaultNotNull, annotationsToRemove)
423 : createChangeDefaultNotNullFix(nullableManager, superMethod);
424 holder.registerProblem(method.getNameIdentifier(),
425 InspectionsBundle.message("inspection.nullable.problems.method.overrides.NotNull", getPresentableAnnoName(superMethod)),
426 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
434 private void checkParameters(PsiMethod method,
435 ProblemsHolder holder,
436 List<PsiMethod> superMethods,
437 NullableNotNullManager nullableManager,
439 PsiParameter[] parameters = method.getParameterList().getParameters();
440 for (int i = 0; i < parameters.length; i++) {
441 PsiParameter parameter = parameters[i];
443 List<PsiParameter> superParameters = ContainerUtil.newArrayList();
444 for (PsiMethod superMethod : superMethods) {
445 PsiParameter[] _superParameters = superMethod.getParameterList().getParameters();
446 if (_superParameters.length == parameters.length) {
447 superParameters.add(_superParameters[i]);
451 if (REPORT_NOTNULL_PARAMETER_OVERRIDES_NULLABLE) {
452 for (PsiParameter superParameter : superParameters) {
453 if (isNotNullNotInferred(parameter, false, false) &&
454 isNullableNotInferred(superParameter, false)) {
455 final PsiAnnotation annotation = AnnotationUtil.findAnnotation(parameter, nullableManager.getNotNulls(), true);
456 holder.registerProblem(annotation != null ? annotation : parameter.getNameIdentifier(),
457 InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.overrides.Nullable",
458 getPresentableAnnoName(parameter),
459 getPresentableAnnoName(superParameter)),
460 ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
465 if (REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL) {
466 for (PsiParameter superParameter : superParameters) {
467 if (!nullableManager.hasNullability(parameter) && isNotNullNotInferred(superParameter, false, IGNORE_EXTERNAL_SUPER_NOTNULL)) {
468 final LocalQuickFix fix = AnnotationUtil.isAnnotatingApplicable(parameter, nullableManager.getDefaultNotNull())
469 ? new AddNotNullAnnotationFix(parameter)
470 : createChangeDefaultNotNullFix(nullableManager, superParameter);
471 holder.registerProblem(parameter.getNameIdentifier(),
472 InspectionsBundle.message("inspection.nullable.problems.parameter.overrides.NotNull", getPresentableAnnoName(superParameter)),
473 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
479 if (REPORT_NOTNULL_PARAMETERS_OVERRIDES_NOT_ANNOTATED) {
480 for (PsiParameter superParameter : superParameters) {
481 if (!nullableManager.hasNullability(superParameter) && isNotNullNotInferred(parameter, false, false)) {
482 PsiAnnotation notNullAnnotation = nullableManager.getNotNullAnnotation(parameter, false);
483 assert notNullAnnotation != null;
484 boolean physical = PsiTreeUtil.isAncestor(parameter, notNullAnnotation, true);
485 final LocalQuickFix fix = physical ? new RemoveAnnotationQuickFix(notNullAnnotation, parameter) : null;
486 holder.registerProblem(physical ? notNullAnnotation : parameter.getNameIdentifier(),
487 InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.overrides.not.annotated", getPresentableAnnoName(parameter)),
488 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
495 checkNullLiteralArgumentOfNotNullParameterUsages(method, holder, nullableManager, isOnFly, i, parameter);
499 private void checkNullLiteralArgumentOfNotNullParameterUsages(PsiMethod method,
500 ProblemsHolder holder,
501 NullableNotNullManager nullableManager,
504 PsiParameter parameter) {
505 if (REPORT_NULLS_PASSED_TO_NOT_NULL_PARAMETER && isOnFly && isNotNullNotInferred(parameter, false, false)) {
506 PsiAnnotation notNullAnnotation = nullableManager.getNotNullAnnotation(parameter, false);
507 if (JavaNullMethodArgumentUtil.hasNullArgument(method, parameterIdx)) {
508 boolean physical = PsiTreeUtil.isAncestor(parameter, notNullAnnotation, true);
509 holder.registerProblem(physical ? notNullAnnotation : parameter.getNameIdentifier(),
510 InspectionsBundle.message("inspection.nullable.problems.NotNull.parameter.receives.null.literal", getPresentableAnnoName(parameter)),
511 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
512 createNavigateToNullParameterUsagesFix(parameter));
517 private void checkOverriders(PsiMethod method,
518 ProblemsHolder holder,
520 NullableNotNullManager nullableManager) {
521 PsiParameter[] parameters = method.getParameterList().getParameters();
522 if (REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS) {
523 boolean[] parameterAnnotated = new boolean[parameters.length];
524 boolean[] parameterQuickFixSuggested = new boolean[parameters.length];
525 boolean hasAnnotatedParameter = false;
526 for (int i = 0; i < parameters.length; i++) {
527 PsiParameter parameter = parameters[i];
528 parameterAnnotated[i] = isNotNullNotInferred(parameter, false, false);
529 hasAnnotatedParameter |= parameterAnnotated[i];
531 if (hasAnnotatedParameter || annotated.isDeclaredNotNull) {
532 PsiManager manager = method.getManager();
533 final String defaultNotNull = nullableManager.getDefaultNotNull();
534 final boolean superMethodApplicable = AnnotationUtil.isAnnotatingApplicable(method, defaultNotNull);
535 PsiMethod[] overridings =
536 OverridingMethodsSearch.search(method).toArray(PsiMethod.EMPTY_ARRAY);
537 boolean methodQuickFixSuggested = false;
538 for (PsiMethod overriding : overridings) {
539 if (!manager.isInProject(overriding)) continue;
541 final boolean applicable = AnnotationUtil.isAnnotatingApplicable(overriding, defaultNotNull);
542 if (!methodQuickFixSuggested
543 && annotated.isDeclaredNotNull
544 && !isNotNullNotInferred(overriding, false, false)
545 && (isNullableNotInferred(overriding, false) || !isNullableNotInferred(overriding, true))) {
546 method.getNameIdentifier(); //load tree
547 PsiAnnotation annotation = AnnotationUtil.findAnnotation(method, nullableManager.getNotNulls());
548 final String[] annotationsToRemove = ArrayUtil.toStringArray(nullableManager.getNullables());
550 final LocalQuickFix fix;
552 fix = new MyAnnotateMethodFix(defaultNotNull, annotationsToRemove);
555 fix = superMethodApplicable ? null : createChangeDefaultNotNullFix(nullableManager, method);
558 PsiElement psiElement = annotation;
559 if (!annotation.isPhysical()) {
560 psiElement = method.getNameIdentifier();
561 if (psiElement == null) continue;
563 holder.registerProblem(psiElement, InspectionsBundle.message("nullable.stuff.problems.overridden.methods.are.not.annotated"),
564 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
566 methodQuickFixSuggested = true;
568 if (hasAnnotatedParameter) {
569 PsiParameter[] psiParameters = overriding.getParameterList().getParameters();
570 for (int i = 0; i < psiParameters.length; i++) {
571 if (parameterQuickFixSuggested[i]) continue;
572 PsiParameter parameter = psiParameters[i];
573 if (parameterAnnotated[i] && !isNotNullNotInferred(parameter, false, false) && !isNullableNotInferred(parameter, false)) {
574 parameters[i].getNameIdentifier(); //be sure that corresponding tree element available
575 PsiAnnotation annotation = AnnotationUtil.findAnnotation(parameters[i], nullableManager.getNotNulls());
576 PsiElement psiElement = annotation;
577 if (annotation == null || !annotation.isPhysical()) {
578 psiElement = parameters[i].getNameIdentifier();
579 if (psiElement == null) continue;
581 holder.registerProblem(psiElement,
582 InspectionsBundle.message("nullable.stuff.problems.overridden.method.parameters.are.not.annotated"),
583 ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
585 ? createChangeDefaultNotNullFix(nullableManager, parameters[i])
586 : new AnnotateOverriddenMethodParameterFix(defaultNotNull,
587 nullableManager.getDefaultNullable()));
588 parameterQuickFixSuggested[i] = true;
597 private static boolean isNotNullNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases, boolean skipExternal) {
598 Project project = owner.getProject();
599 NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
600 if (!manager.isNotNull(owner, checkBases)) return false;
602 PsiAnnotation anno = manager.getNotNullAnnotation(owner, checkBases);
603 if (anno == null || AnnotationUtil.isInferredAnnotation(anno)) return false;
604 if (skipExternal && AnnotationUtil.isExternalAnnotation(anno)) return false;
608 public static boolean isNullableNotInferred(@NotNull PsiModifierListOwner owner, boolean checkBases) {
609 Project project = owner.getProject();
610 NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
611 if (!manager.isNullable(owner, checkBases)) return false;
613 PsiAnnotation anno = manager.getNullableAnnotation(owner, checkBases);
614 return !(anno != null && AnnotationUtil.isInferredAnnotation(anno));
617 private static LocalQuickFix createChangeDefaultNotNullFix(NullableNotNullManager nullableManager, PsiModifierListOwner modifierListOwner) {
618 final PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierListOwner, nullableManager.getNotNulls());
619 if (annotation != null) {
620 final PsiJavaCodeReferenceElement referenceElement = annotation.getNameReferenceElement();
621 if (referenceElement != null && referenceElement.resolve() != null) {
622 return new ChangeNullableDefaultsFix(annotation.getQualifiedName(), null, nullableManager);
628 protected AnnotateMethodFix createAnnotateMethodFix(final String defaultNotNull, final String[] annotationsToRemove) {
629 return new AnnotateMethodFix(defaultNotNull, annotationsToRemove);
632 private static void reportNullableNotNullConflict(final ProblemsHolder holder, final PsiModifierListOwner listOwner, final PsiAnnotation declaredNullable,
633 final PsiAnnotation declaredNotNull) {
634 final String bothNullableNotNullMessage = InspectionsBundle.message("inspection.nullable.problems.Nullable.NotNull.conflict",
635 getPresentableAnnoName(declaredNullable),
636 getPresentableAnnoName(declaredNotNull));
637 holder.registerProblem(declaredNotNull.isPhysical() ? declaredNotNull : listOwner.getNavigationElement(),
638 bothNullableNotNullMessage,
639 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNotNull, listOwner));
640 holder.registerProblem(declaredNullable.isPhysical() ? declaredNullable : listOwner.getNavigationElement(),
641 bothNullableNotNullMessage,
642 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new RemoveAnnotationQuickFix(declaredNullable, listOwner));
646 public JComponent createOptionsPanel() {
647 throw new RuntimeException("No UI in headless mode");
650 private static class MyAnnotateMethodFix extends AnnotateMethodFix {
651 public MyAnnotateMethodFix(String defaultNotNull, String[] annotationsToRemove) {
652 super(defaultNotNull, annotationsToRemove);
656 protected boolean annotateOverriddenMethods() {
661 public int shouldAnnotateBaseMethod(PsiMethod method, PsiMethod superMethod, Project project) {
667 public String getName() {
668 return InspectionsBundle.message("annotate.overridden.methods.as.notnull", ClassUtil.extractClassName(myAnnotation));