diff --git a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java index e05cad70bc6d..f97b948c95a1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/NullableNotNullManagerImpl.java @@ -30,7 +30,10 @@ import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; import com.intellij.psi.search.DelegatingGlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValueProvider.Result; -import com.intellij.psi.util.*; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; import one.util.streamex.StreamEx; import org.jdom.Element; @@ -290,30 +293,53 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement return result; } + @Override + protected @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes, + PsiFile file) { + boolean superPackage = false; + ContextNullabilityInfo info = ContextNullabilityInfo.EMPTY; + ProjectFileIndex index = ProjectRootManager.getInstance(myProject).getFileIndex(); + VirtualFile vFile = file.getVirtualFile(); + if (vFile == null) return info; + VirtualFile root = index.getSourceRootForFile(vFile); + boolean compiled = false; + if (root == null) { + root = index.getClassRootForFile(vFile); + if (root == null) return info; + compiled = true; + } + // Single-file source root -- no package-info processing for now + if (root.equals(vFile)) return info; + PsiDirectory directory = file.getContainingDirectory(); + while (directory != null) { + PsiFile packageFile = directory.findFile(compiled ? PsiPackage.PACKAGE_INFO_CLS_FILE : PsiPackage.PACKAGE_INFO_FILE); + if (packageFile instanceof PsiJavaFile javaFile) { + PsiPackageStatement stmt = javaFile.getPackageStatement(); + if (stmt != null) { + PsiModifierList modifierList = stmt.getAnnotationList(); + if (modifierList != null) { + for (PsiAnnotation annotation : modifierList.getAnnotations()) { + info = info.orElse(checkNullityDefault(annotation, placeTargetTypes, superPackage)); + } + } + } + } + if (root.equals(directory.getVirtualFile())) break; + directory = directory.getParentDirectory(); + superPackage = true; + } + return info; + } + @Override protected @NotNull ContextNullabilityInfo getNullityDefault(@NotNull PsiModifierListOwner container, - PsiAnnotation.TargetType @NotNull [] placeTargetTypes, - boolean superPackage) { + PsiAnnotation.TargetType @NotNull [] placeTargetTypes) { + LOG.assertTrue(!(container instanceof PsiPackage)); // Packages are handled separately in findNullityDefaultOnPackage ContextNullabilityInfo res = ContextNullabilityInfo.EMPTY; PsiModifierList modifierList = container.getModifierList(); if (modifierList != null) { for (PsiAnnotation annotation : modifierList.getAnnotations()) { - ContextNullabilityInfo info = checkNullityDefault(annotation, placeTargetTypes, superPackage); - if (container instanceof PsiPackage) { - VirtualFile annotationFile = PsiUtilCore.getVirtualFile(annotation); - info = info.filtering(context -> { - VirtualFile ownerFile = PsiUtilCore.getVirtualFile(context); - if (annotationFile != null && ownerFile != null && !annotationFile.equals(ownerFile)) { - ProjectFileIndex index = ProjectRootManager.getInstance(container.getProject()).getFileIndex(); - VirtualFile annotationRoot = index.getClassRootForFile(annotationFile); - VirtualFile ownerRoot = index.getClassRootForFile(ownerFile); - if (ownerRoot != null && !ownerRoot.equals(annotationRoot)) { - return false; - } - } - return true; - }); - } + ContextNullabilityInfo info = checkNullityDefault(annotation, placeTargetTypes, false); res = res.orElse(info); } } @@ -407,7 +433,7 @@ public class NullableNotNullManagerImpl extends NullableNotNullManager implement @NotNull PsiElement element) { PsiJavaModule module = JavaPsiModuleUtil.findDescriptorByElement(element); if (module != null) { - return getNullityDefault(module, targetTypes, false); + return getNullityDefault(module, targetTypes); } return ContextNullabilityInfo.EMPTY; } diff --git a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java index d0430de62fa9..22edd3a12dec 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/NullableNotNullManager.java @@ -274,8 +274,7 @@ public abstract class NullableNotNullManager { } protected abstract @NotNull ContextNullabilityInfo getNullityDefault(@NotNull PsiModifierListOwner container, - PsiAnnotation.TargetType @NotNull [] placeTargetTypes, - boolean superPackage); + PsiAnnotation.TargetType @NotNull [] placeTargetTypes); @ApiStatus.Internal @NotNull @@ -316,7 +315,7 @@ public abstract class NullableNotNullManager { if (element instanceof PsiModifierListOwner) { PsiModifierListOwner listOwner = (PsiModifierListOwner)element; NullabilityAnnotationInfo result = CachedValuesManager.getCachedValue(listOwner, () -> { - return CachedValueProvider.Result.create(getNullityDefault(listOwner, TYPE_USE_TARGET, false), + return CachedValueProvider.Result.create(getNullityDefault(listOwner, TYPE_USE_TARGET), PsiModificationTracker.MODIFICATION_COUNT); }).forContext(context); if (result != null) { @@ -327,9 +326,7 @@ public abstract class NullableNotNullManager { if (element instanceof PsiClassOwner) { PsiClassOwner classOwner = (PsiClassOwner)element; return CachedValuesManager.getCachedValue(classOwner, () -> { - String packageName = classOwner.getPackageName(); - PsiPackage psiPackage = JavaPsiFacade.getInstance(classOwner.getProject()).findPackage(packageName); - ContextNullabilityInfo fromPackage = findNullityDefaultOnPackage(TYPE_USE_TARGET, psiPackage); + ContextNullabilityInfo fromPackage = findNullityDefaultOnPackage(TYPE_USE_TARGET, classOwner.getContainingFile()); return CachedValueProvider.Result.create(fromPackage.orElse(findNullityDefaultOnModule(TYPE_USE_TARGET, classOwner)), PsiModificationTracker.MODIFICATION_COUNT); }).forContext(context); @@ -357,16 +354,14 @@ public abstract class NullableNotNullManager { PsiElement element = place.getContext(); while (element != null) { if (element instanceof PsiModifierListOwner) { - NullabilityAnnotationInfo result = getNullityDefault((PsiModifierListOwner)element, placeTargetTypes, false).forContext(place); + NullabilityAnnotationInfo result = getNullityDefault((PsiModifierListOwner)element, placeTargetTypes).forContext(place); if (result != null) { return result; } } if (element instanceof PsiClassOwner) { - String packageName = ((PsiClassOwner)element).getPackageName(); - PsiPackage psiPackage = JavaPsiFacade.getInstance(element.getProject()).findPackage(packageName); - NullabilityAnnotationInfo fromPackage = findNullityDefaultOnPackage(placeTargetTypes, psiPackage).forContext(place); + NullabilityAnnotationInfo fromPackage = findNullityDefaultOnPackage(placeTargetTypes, element.getContainingFile()).forContext(place); if (fromPackage != null) { return fromPackage; } @@ -383,16 +378,9 @@ public abstract class NullableNotNullManager { return ContextNullabilityInfo.EMPTY; } - private @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes, - @Nullable PsiPackage psiPackage) { - boolean superPackage = false; - ContextNullabilityInfo info = ContextNullabilityInfo.EMPTY; - while (psiPackage != null) { - info = info.orElse(getNullityDefault(psiPackage, placeTargetTypes, superPackage)); - superPackage = true; - psiPackage = psiPackage.getParentPackage(); - } - return info; + protected @NotNull ContextNullabilityInfo findNullityDefaultOnPackage(PsiAnnotation.TargetType @NotNull [] placeTargetTypes, + PsiFile file) { + return ContextNullabilityInfo.EMPTY; } public abstract @NotNull List getNullables(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsPackageStatementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsPackageStatementImpl.java index 05b2a64e3f61..4807ccb99f5a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsPackageStatementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsPackageStatementImpl.java @@ -34,6 +34,12 @@ class ClsPackageStatementImpl extends ClsElementImpl implements PsiPackageStatem @Override public PsiModifierList getAnnotationList() { + if (myFile != null && myFile.getName().equals("package-info.class")) { + PsiClass[] classes = myFile.getClasses(); + if (classes.length == 1) { + return classes[0].getModifierList(); + } + } throw new UnsupportedOperationException("Method not implemented"); } diff --git a/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.after.java b/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.after.java index b55dcf46bf4e..f673282e2c4b 100644 --- a/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.after.java +++ b/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.after.java @@ -1,4 +1,4 @@ -package p; +package refactoring.introduceVariable; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.java b/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.java index 6a8212e60f3a..9f468cc43781 100644 --- a/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.java +++ b/java/java-tests/testData/refactoring/introduceVariable/WithDefaultNullability.java @@ -1,4 +1,4 @@ -package p; +package refactoring.introduceVariable; class Test { @org.checkerframework.checker.nullness.qual.Nullable String s; diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTestCase.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTestCase.java index 4703112121c2..585606b47974 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTestCase.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/DataFlowInspectionTestCase.java @@ -10,6 +10,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import org.intellij.lang.annotations.Language; +import org.jetbrains.annotations.NotNull; import java.util.function.BiConsumer; @@ -26,7 +27,11 @@ public abstract class DataFlowInspectionTestCase extends LightJavaCodeInsightFix ConstantValueInspection cvInspection = new ConstantValueInspection(); inspectionMutator.accept(inspection, cvInspection); myFixture.enableInspections(inspection, cvInspection); - myFixture.testHighlighting(true, false, true, getTestName(false) + ".java"); + myFixture.testHighlighting(true, false, true, getTestFileName()); + } + + protected @NotNull String getTestFileName() { + return getTestName(false) + ".java"; } static void addCheckerAnnotations(JavaCodeInsightTestFixture fixture) { diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/LightIntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/LightIntroduceVariableTest.java index 9d22df46f847..288255547aec 100644 --- a/java/java-tests/testSrc/com/intellij/java/refactoring/LightIntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/LightIntroduceVariableTest.java @@ -40,9 +40,9 @@ public class LightIntroduceVariableTest extends LightJavaCodeInsightFixtureTestC myFixture.addClass("package org.checkerframework.framework.qual; public class DefaultQualifier {}"); myFixture.addClass("package org.checkerframework.checker.nullness.qual; @java.lang.annotation.Target({ElementType.TYPE_USE}) public class NonNull {}"); myFixture.addClass("package org.checkerframework.checker.nullness.qual; @java.lang.annotation.Target({ElementType.TYPE_USE}) public @interface Nullable {}"); - myFixture.addFileToProject("p/package-info.java", """ + myFixture.addFileToProject("refactoring/introduceVariable/package-info.java", """ @org.checkerframework.framework.qual.DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.ALL) - package p; + package refactoring.introduceVariable; import org.checkerframework.checker.nullness.qual.NonNull; """); MockIntroduceVariableHandler handler = new MockIntroduceVariableHandler("m", false, false, false, diff --git a/plugins/lombok/src/test/java/com/intellij/java/lomboktest/LombokDataFlowInspectionTest.java b/plugins/lombok/src/test/java/com/intellij/java/lomboktest/LombokDataFlowInspectionTest.java index aeafbcdb2724..f827c192725d 100644 --- a/plugins/lombok/src/test/java/com/intellij/java/lomboktest/LombokDataFlowInspectionTest.java +++ b/plugins/lombok/src/test/java/com/intellij/java/lomboktest/LombokDataFlowInspectionTest.java @@ -48,4 +48,9 @@ public class LombokDataFlowInspectionTest extends DataFlowInspectionTestCase { doTest(); } + + @Override + protected @NotNull String getTestFileName() { + return "test/" + super.getTestFileName(); + } } \ No newline at end of file diff --git a/plugins/lombok/testData/inspection/dataflow/SpringNonNullApiOnPackage.java b/plugins/lombok/testData/inspection/dataflow/test/SpringNonNullApiOnPackage.java similarity index 100% rename from plugins/lombok/testData/inspection/dataflow/SpringNonNullApiOnPackage.java rename to plugins/lombok/testData/inspection/dataflow/test/SpringNonNullApiOnPackage.java