diff --git a/java/java-analysis-impl/src/com/siyeh/ig/style/TypeParameterExtendsObjectInspection.java b/java/java-analysis-impl/src/com/siyeh/ig/style/TypeParameterExtendsObjectInspection.java index b10bcf2b0a4e..18c9c9fb5d92 100644 --- a/java/java-analysis-impl/src/com/siyeh/ig/style/TypeParameterExtendsObjectInspection.java +++ b/java/java-analysis-impl/src/com/siyeh/ig/style/TypeParameterExtendsObjectInspection.java @@ -15,6 +15,7 @@ */ package com.siyeh.ig.style; +import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.options.OptPane; import com.intellij.modcommand.ModPsiUpdater; @@ -22,11 +23,13 @@ import com.intellij.modcommand.PsiUpdateModCommandQuickFix; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static com.intellij.codeInspection.options.OptPane.checkbox; import static com.intellij.codeInspection.options.OptPane.pane; @@ -135,7 +138,6 @@ public final class TypeParameterExtendsObjectInspection extends BaseInspection { registerError(nameIdentifier, Integer.valueOf(1)); } - @Override public void visitTypeElement(@NotNull PsiTypeElement typeElement) { super.visitTypeElement(typeElement); @@ -154,11 +156,29 @@ public final class TypeParameterExtendsObjectInspection extends BaseInspection { if ((ignoreAnnotatedObject && extendsBound.hasAnnotations()) || !TypeUtils.isJavaLangObject(extendsBound.getType())) { return; } + if (ignoreAnnotatedObject && hasContainerAnnotations(PsiTreeUtil.getParentOfType(typeElement, PsiModifierListOwner.class))) return; final PsiElement firstChild = typeElement.getFirstChild(); if (firstChild == null) { return; } registerError(firstChild, Integer.valueOf(2)); } + + /** + * If there is a container {@code @NullMarked} annotation, then {@code } means {@code } + * but {@code } mean {@code }. + * So, in this case {@code extends Object} changes the behavior. + * There is a case when we can delete it: + * {@code }, but it seems it is better to preserve it because it makes code easier to understand + * @return if there is a container annotation on the given element + */ + private static boolean hasContainerAnnotations(@Nullable PsiModifierListOwner modifierListOwner) { + if(modifierListOwner == null) return false; + NullableNotNullManager manager = NullableNotNullManager.getInstance(modifierListOwner.getProject()); + if (manager != null && manager.findContainerAnnotation(modifierListOwner) != null) { + return true; + } + return false; + } } } \ No newline at end of file diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/type_parameter_extends_object/TypeParameterExtendsObjectWithContainerAnnotation.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/type_parameter_extends_object/TypeParameterExtendsObjectWithContainerAnnotation.java new file mode 100644 index 000000000000..2e87d544a8fd --- /dev/null +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/type_parameter_extends_object/TypeParameterExtendsObjectWithContainerAnnotation.java @@ -0,0 +1,17 @@ +import org.jspecify.annotations.NullMarked; + +class TypeParameterExtendsObjectWithContainerAnnotation { + @NullMarked + interface SuperSuper { + Lib t(); + + void checkNeverNull(Lib lib); + } +} + +@NullMarked +final class Inner1<T extends Object> { +} + +class Lib { +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/siyeh/ig/style/TypeParameterExtendsObjectInspectionTest.java b/java/java-tests/testSrc/com/siyeh/ig/style/TypeParameterExtendsObjectInspectionTest.java index 6bfd4cd2ac95..4d2b67803902 100644 --- a/java/java-tests/testSrc/com/siyeh/ig/style/TypeParameterExtendsObjectInspectionTest.java +++ b/java/java-tests/testSrc/com/siyeh/ig/style/TypeParameterExtendsObjectInspectionTest.java @@ -6,6 +6,7 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.InspectionProfileEntry; import com.intellij.testFramework.LightProjectDescriptor; import com.siyeh.ig.LightJavaInspectionTestCase; +import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,20 @@ public class TypeParameterExtendsObjectInspectionTest extends LightJavaInspectio doTest(); } + public void testTypeParameterExtendsObjectWithContainerAnnotation() { + final TypeParameterExtendsObjectInspection inspection = getTypeParameterExtendsObjectInspection(true); + myFixture.enableInspections(inspection); + @Language("JAVA") String nullMarked = + """ + package org.jspecify.annotations; + import java.lang.annotation.*; + @Target({ElementType.TYPE, ElementType.METHOD, ElementType.MODULE}) + public @interface NullMarked {}"""; + myFixture.addClass(nullMarked); + + doTest(); + } + public void testTypeParameterExtendsObjectIgnoreAnnotated() { final TypeParameterExtendsObjectInspection inspection = getTypeParameterExtendsObjectInspection(true); myFixture.enableInspections(inspection);