diff --git a/java/java-frontback-impl/src/com/intellij/psi/formatter/java/JavaFormatterAnnotationUtil.kt b/java/java-frontback-impl/src/com/intellij/psi/formatter/java/JavaFormatterAnnotationUtil.kt index aed734f2d2b5..8cc9b9c5ff40 100644 --- a/java/java-frontback-impl/src/com/intellij/psi/formatter/java/JavaFormatterAnnotationUtil.kt +++ b/java/java-frontback-impl/src/com/intellij/psi/formatter/java/JavaFormatterAnnotationUtil.kt @@ -1,10 +1,10 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.psi.formatter.java +import com.intellij.codeInsight.AnnotationUtil import com.intellij.codeInsight.DumbAwareAnnotationUtil import com.intellij.lang.ASTNode -import com.intellij.openapi.roots.LanguageLevelProjectExtension -import com.intellij.pom.java.LanguageLevel +import com.intellij.pom.java.JavaFeature import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiKeyword import com.intellij.psi.PsiModifierListOwner @@ -13,11 +13,14 @@ import com.intellij.psi.formatter.FormatterUtil import com.intellij.psi.formatter.java.JavaFormatterAnnotationUtil.isFieldWithAnnotations import com.intellij.psi.impl.source.tree.JavaElementType import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiUtil internal object JavaFormatterAnnotationUtil { private val KNOWN_TYPE_ANNOTATIONS: Set = setOf( - "org.jetbrains.annotations.NotNull", - "org.jetbrains.annotations.Nullable" + AnnotationUtil.NOT_NULL, + AnnotationUtil.NULLABLE, + AnnotationUtil.J_SPECIFY_NON_NULL, + AnnotationUtil.J_SPECIFY_NULLABLE, ) /** @@ -31,8 +34,7 @@ internal object JavaFormatterAnnotationUtil { fun isTypeAnnotation(annotation: ASTNode): Boolean { val node = annotation.psi as? PsiAnnotation ?: return false - val languageLevel = LanguageLevelProjectExtension.getInstance(node.project).languageLevel - if (languageLevel.isLessThan(LanguageLevel.JDK_1_8)) return false + if (!PsiUtil.isAvailable(JavaFeature.TYPE_ANNOTATIONS, node)) return false val next = PsiTreeUtil.skipSiblingsForward(node, PsiWhiteSpace::class.java, PsiAnnotation::class.java) if (next is PsiKeyword) return false diff --git a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java index 83463bbdae35..b07b6862d527 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/AnnotationUtil.java @@ -26,6 +26,9 @@ public class AnnotationUtil { public static final String NOT_NULL = "org.jetbrains.annotations.NotNull"; public static final String NOT_NULL_BY_DEFAULT = "org.jetbrains.annotations.NotNullByDefault"; + public static final String J_SPECIFY_NON_NULL = "org.jspecify.annotations.NonNull"; + public static final String J_SPECIFY_NULLABLE = "org.jspecify.annotations.Nullable"; + public static final String NON_NLS = "org.jetbrains.annotations.NonNls"; public static final String NLS = "org.jetbrains.annotations.Nls"; diff --git a/java/java-psi-api/src/com/intellij/codeInsight/DumbAwareAnnotationUtil.kt b/java/java-psi-api/src/com/intellij/codeInsight/DumbAwareAnnotationUtil.kt index ee8dbf16aede..f916d68153e2 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/DumbAwareAnnotationUtil.kt +++ b/java/java-psi-api/src/com/intellij/codeInsight/DumbAwareAnnotationUtil.kt @@ -5,10 +5,12 @@ import com.intellij.codeInsight.DumbAwareAnnotationUtil.KNOWN_ANNOTATIONS import com.intellij.codeInsight.DumbAwareAnnotationUtil.hasAnnotation import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.text.StringUtil +import com.intellij.pom.java.JavaFeature import com.intellij.psi.* import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker +import com.intellij.psi.util.PsiUtil /** * Utility which helps to detect annotation in `Dumb mode`. @@ -16,10 +18,24 @@ import com.intellij.psi.util.PsiModificationTracker object DumbAwareAnnotationUtil { private const val JAVA_LANG_PACKAGE = "java.lang" + /** + * Represents a list of fully qualified names for annotations that are treated as a type annotations. + */ private val KNOWN_ANNOTATIONS = setOf( AnnotationUtil.NOT_NULL, AnnotationUtil.NULLABLE, - AnnotationUtil.NON_NLS + AnnotationUtil.NON_NLS, + AnnotationUtil.J_SPECIFY_NON_NULL, + AnnotationUtil.J_SPECIFY_NULLABLE + ) + + /** + * Represents a mapping from a fully qualified name of a module to a set of fully qualified names of annotations + * that are treated as type annotations and located in this module + */ + private val KNOWN_MODULE_TO_ANNOTATIONS_MAP = mapOf( + "org.jetbrains.annotations" to setOf(AnnotationUtil.NOT_NULL, AnnotationUtil.NULLABLE, AnnotationUtil.NON_NLS), + "org.jspecify" to setOf(AnnotationUtil.J_SPECIFY_NON_NULL, AnnotationUtil.J_SPECIFY_NULLABLE) ) /** @@ -57,12 +73,12 @@ object DumbAwareAnnotationUtil { * Formats the given fully qualified name (FQN) by trimming whitespace around each segment. */ @JvmStatic - fun getFormattedReferenceFqn(referenceText: @NlsSafe String) = referenceText.split(".").joinToString(separator = ".") { pathPart -> pathPart.trim() } + fun getFormattedReferenceFqn(referenceText: @NlsSafe String): String = referenceText.split(".").joinToString(separator = ".") { pathPart -> pathPart.trim() } private fun getImportedKnownAnnotations(file: PsiJavaFile): Set = CachedValuesManager.getCachedValue(file) { val importList = file.importList ?: return@getCachedValue CachedValueProvider.Result(emptySet(), PsiModificationTracker.MODIFICATION_COUNT) - val filteredAnnotations = KNOWN_ANNOTATIONS.filter { isAnnotationInImportList(it, importList) } + val filteredAnnotations = KNOWN_ANNOTATIONS.filter { isAnnotationInImportList(it, importList) || isAnnotationInModuleImportList(it, importList) } .mapNotNull { fqn -> fqn.split(".").lastOrNull() } .toSet() CachedValueProvider.Result.create(filteredAnnotations, PsiModificationTracker.MODIFICATION_COUNT) @@ -77,6 +93,16 @@ object DumbAwareAnnotationUtil { } } + private fun isAnnotationInModuleImportList(annotationFqn: String, moduleList: PsiImportList): Boolean { + if (!PsiUtil.isAvailable(JavaFeature.MODULE_IMPORT_DECLARATIONS, moduleList)) return false + return moduleList.importModuleStatements.any { statement: PsiImportModuleStatement -> + val referenceName = statement.referenceName ?: return@any false + val formattedReferenceName = getFormattedReferenceFqn(referenceName) + if (formattedReferenceName !in KNOWN_MODULE_TO_ANNOTATIONS_MAP) return@any false + annotationFqn in KNOWN_MODULE_TO_ANNOTATIONS_MAP.getValue(formattedReferenceName) + } + } + private fun getAnnotationImportInfo(annotationFqn: String): AnnotationImportInfo { val packageName = StringUtil.getPackageName(annotationFqn) val className = StringUtil.getShortName(annotationFqn) diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation.java new file mode 100644 index 000000000000..03191e1d2b45 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation.java @@ -0,0 +1,18 @@ +package org.example; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.List; + +@Target({ElementType.TYPE_USE}) +@interface CustomAnno {} + +public class Formatter { + @CustomAnno String getCustomString() { + return null; + } + + @CustomAnno List getCustomList() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation_after.java new file mode 100644 index 000000000000..3861f2d10ebd --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/CustomAnnotation_after.java @@ -0,0 +1,21 @@ +package org.example; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.List; + +@Target({ElementType.TYPE_USE}) +@interface CustomAnno { +} + +public class Formatter { + @CustomAnno + String getCustomString() { + return null; + } + + @CustomAnno + List getCustomList() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName.java new file mode 100644 index 000000000000..c6ccb7c17f97 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName.java @@ -0,0 +1,23 @@ +package org.example; + +public class Formatter { + @org.jspecify.annotations.NonNull + List getNotNullList() { + return List.of(); + } + + @org.jspecify.annotations.Nullable + List getNullableList() { + return null; + } + + @org.jspecify.annotations.NonNull + String getNotNullName() { + return ""; + } + + @org.jspecify.annotations.Nullable + String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName_after.java new file mode 100644 index 000000000000..d1236b91cc1b --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/FullyQualifiedName_after.java @@ -0,0 +1,19 @@ +package org.example; + +public class Formatter { + @org.jspecify.annotations.NonNull List getNotNullList() { + return List.of(); + } + + @org.jspecify.annotations.Nullable List getNullableList() { + return null; + } + + @org.jspecify.annotations.NonNull String getNotNullName() { + return ""; + } + + @org.jspecify.annotations.Nullable String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix.java new file mode 100644 index 000000000000..2682acabb254 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix.java @@ -0,0 +1,16 @@ +package org.example; + +import org.jspecify.annotations.*; +import java.util.List; + +public class Formatter { + @NonNull @org.jspecify.annotations.Nullable + List getNotNullList() { + return List.of(); + } + + @NonNull @org.jspecify.annotations.Nullable + String getNotNullName() { + return ""; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix_after.java new file mode 100644 index 000000000000..4e0aabae7d2a --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportMix_after.java @@ -0,0 +1,15 @@ +package org.example; + +import org.jspecify.annotations.*; + +import java.util.List; + +public class Formatter { + @NonNull @org.jspecify.annotations.Nullable List getNotNullList() { + return List.of(); + } + + @NonNull @org.jspecify.annotations.Nullable String getNotNullName() { + return ""; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand.java new file mode 100644 index 000000000000..65f50511b18f --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand.java @@ -0,0 +1,26 @@ +package org.example; + +import org.jspecify.annotations.*; +import java.util.List; + +public class Formatter { + @NonNull + List getNotNullList() { + return List.of(); + } + + @Nullable + List getNullableList() { + return null; + } + + @NonNull + String getNotNullName() { + return ""; + } + + @Nullable + String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand_after.java new file mode 100644 index 000000000000..5836f9981c8b --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ImportOnDemand_after.java @@ -0,0 +1,23 @@ +package org.example; + +import org.jspecify.annotations.*; + +import java.util.List; + +public class Formatter { + @NonNull List getNotNullList() { + return List.of(); + } + + @Nullable List getNullableList() { + return null; + } + + @NonNull String getNotNullName() { + return ""; + } + + @Nullable String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks.java new file mode 100644 index 000000000000..f5722b29d618 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks.java @@ -0,0 +1,27 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull + @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NonNull + String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks_after.java new file mode 100644 index 000000000000..147df47b1940 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KeepLineBreaks_after.java @@ -0,0 +1,27 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull + @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NonNull + String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType.java new file mode 100644 index 000000000000..85b8b322481f --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType.java @@ -0,0 +1,14 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull String getNotNullName() { + return ""; + } + + @Nullable String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList.java new file mode 100644 index 000000000000..4266bedf1a32 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList.java @@ -0,0 +1,16 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull List getNotNullList() { + return List.of(); + } + + @Nullable List getNullableList() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList_after.java new file mode 100644 index 000000000000..4266bedf1a32 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeTypeParameterList_after.java @@ -0,0 +1,16 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull List getNotNullList() { + return List.of(); + } + + @Nullable List getNullableList() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType_after.java new file mode 100644 index 000000000000..85b8b322481f --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/KnownAnnotationBeforeType_after.java @@ -0,0 +1,14 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull String getNotNullName() { + return ""; + } + + @Nullable String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel.java new file mode 100644 index 000000000000..1266e15823b5 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel.java @@ -0,0 +1,24 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport.java new file mode 100644 index 000000000000..29b7513cd4b6 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport.java @@ -0,0 +1,23 @@ +package org.example; + +import module org.jspecify; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport_after.java new file mode 100644 index 000000000000..3214125cafd6 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevelForModuleImport_after.java @@ -0,0 +1,28 @@ +package org.example; + +import module org.jspecify; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull + String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel_after.java new file mode 100644 index 000000000000..53735d380236 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/LowLanguageLevel_after.java @@ -0,0 +1,29 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull + String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations.java new file mode 100644 index 000000000000..55dc9b110e73 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations.java @@ -0,0 +1,17 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + + +public class Formatter { + @Nullable @NonNull String getCustomString() { + return null; + } + + @Nullable @NonNull List getCustomList() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations_after.java new file mode 100644 index 000000000000..55dc9b110e73 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ManyKnownAnnotations_after.java @@ -0,0 +1,17 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + + +public class Formatter { + @Nullable @NonNull String getCustomString() { + return null; + } + + @Nullable @NonNull List getCustomList() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport.java new file mode 100644 index 000000000000..7ab1f0a4d8fc --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport.java @@ -0,0 +1,27 @@ +package org.example; + +import module org.jspecify; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn.java new file mode 100644 index 000000000000..509897c215d9 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn.java @@ -0,0 +1,28 @@ +package org.example; + + +import module org.jspecify; + +public class Formatter { + @NonNull + @org.jspecify.annotations.Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @org.jspecify.annotations.Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @org.jspecify.annotations.Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @org.jspecify.annotations.Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn_after.java new file mode 100644 index 000000000000..0e5b11f10811 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithFqn_after.java @@ -0,0 +1,22 @@ +package org.example; + + +import module org.jspecify; + +public class Formatter { + @NonNull @org.jspecify.annotations.Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @org.jspecify.annotations.Nullable @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @org.jspecify.annotations.Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @org.jspecify.annotations.Nullable @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport.java new file mode 100644 index 000000000000..28dfa26e9bcc --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport.java @@ -0,0 +1,29 @@ +package org.example; + +import org.jspecify.annotations.Nullable; + +import module org.jspecify; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport_after.java new file mode 100644 index 000000000000..c55f7b16a11a --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportMixedWithPackageImport_after.java @@ -0,0 +1,23 @@ +package org.example; + +import org.jspecify.annotations.Nullable; + +import module org.jspecify; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces.java new file mode 100644 index 000000000000..7d142e190cc4 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces.java @@ -0,0 +1,28 @@ +package org.example; + +import module org . + jspecify; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces_after.java new file mode 100644 index 000000000000..367f06fc71d5 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImportWithSpaces_after.java @@ -0,0 +1,22 @@ +package org.example; + +import module org . + jspecify; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport_after.java new file mode 100644 index 000000000000..ff77ee3535a6 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/ModuleImport_after.java @@ -0,0 +1,21 @@ +package org.example; + +import module org.jspecify; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations.java new file mode 100644 index 000000000000..1084cb6918b8 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations.java @@ -0,0 +1,20 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull + @Nullable + List getStrangeList() { + return List.of(); + } + + @NonNull + @Nullable + String getNotNullName() { + return ""; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations_after.java new file mode 100644 index 000000000000..b862adecf915 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingManyAnnotations_after.java @@ -0,0 +1,16 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull @Nullable List getStrangeList() { + return List.of(); + } + + @NonNull @Nullable String getNotNullName() { + return ""; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation.java new file mode 100644 index 000000000000..a1810bf76396 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation.java @@ -0,0 +1,28 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull + List getNotNullList() { + return List.of(); + } + + @Nullable + List getNullableList() { + return null; + } + + @NonNull + String getNotNullName() { + return ""; + } + + @Nullable + String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation_after.java new file mode 100644 index 000000000000..a5f6c72bd28b --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/PreserveWrappingSingleAnnotation_after.java @@ -0,0 +1,24 @@ +package org.example; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.List; + +public class Formatter { + @NonNull List getNotNullList() { + return List.of(); + } + + @Nullable List getNullableList() { + return null; + } + + @NonNull String getNotNullName() { + return ""; + } + + @Nullable String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations.java new file mode 100644 index 000000000000..7adaee000e88 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations.java @@ -0,0 +1,24 @@ +package org.example; + +public class Formatter { + @ org . jspecify .annotations . NonNull + List getNotNullList() { + return List.of(); + } + + @org. jspecify. annotations . + Nullable + List getNullableList() { + return null; + } + + @ org. jspecify . annotations. NonNull + String getNotNullName() { + return ""; + } + + @ org . jspecify . annotations . Nullable + String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations_after.java new file mode 100644 index 000000000000..d1236b91cc1b --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInFqnAnnotations_after.java @@ -0,0 +1,19 @@ +package org.example; + +public class Formatter { + @org.jspecify.annotations.NonNull List getNotNullList() { + return List.of(); + } + + @org.jspecify.annotations.Nullable List getNullableList() { + return null; + } + + @org.jspecify.annotations.NonNull String getNotNullName() { + return ""; + } + + @org.jspecify.annotations.Nullable String getNullableName() { + return null; + } +} diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList.java new file mode 100644 index 000000000000..2fc19d6a093d --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList.java @@ -0,0 +1,20 @@ +package org.example + +import + org . jspecify . annotations. NonNull; +import org. jspecify . annotations. + Nullable; +import org . jspecify . + annotations .*; + +public class Formatter { + @NonNull + String getNotNullName() { + return ""; + } + + @Nullable + String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList_after.java new file mode 100644 index 000000000000..4e8253a07831 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/SpacesInImportList_after.java @@ -0,0 +1,15 @@ +package org.example + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; +import org.jspecify.annotations.*; + +public class Formatter { + @NonNull String getNotNullName() { + return ""; + } + + @Nullable String getNullableName() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport.java new file mode 100644 index 000000000000..8e229ca6dc5d --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport.java @@ -0,0 +1,23 @@ +package org.example; + +import module org.jspecify.annotations; + +public class Formatter { + @NonNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport_after.java b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport_after.java new file mode 100644 index 000000000000..ad9a43ed3996 --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/jSpecifyTypeAnnotation/UnknownModuleImport_after.java @@ -0,0 +1,28 @@ +package org.example; + +import module org.jspecify.annotations; + +public class Formatter { + @NonNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NonNull + String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable + String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NonNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport.java b/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport.java new file mode 100644 index 000000000000..c94610c11a4d --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport.java @@ -0,0 +1,26 @@ +package org.example; + +import module org.jetbrains.annotations; + +public class Formatter { + @NotNull + @Nullable + String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable + @NotNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable + @NotNull + String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport_after.java b/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport_after.java new file mode 100644 index 000000000000..c00523dcb35f --- /dev/null +++ b/java/java-tests/testData/psi/formatter/java/typeAnnotation/ModuleImport_after.java @@ -0,0 +1,21 @@ +package org.example; + +import module org.jetbrains.annotations; + +public class Formatter { + @NotNull @Nullable String breakLineBetweenAnnotations() { + return ""; + } + + @Nullable @NotNull String breakLineBetweenTypeAndAnnotations() { + return null; + } + + @Nullable String breakLineBetweenTypeParameterAndAnnotation() { + return null; + } + + @Nullable @NotNull String breakLineMixed() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java index 2069c3b69272..c8426818541d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java @@ -8,7 +8,9 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.GeneratedSourcesFilter; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiClass; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -38,6 +40,7 @@ public class AnnotateMethodInGeneratedFilesTest extends LightJavaCodeInsightFixt } public void testAnnotateOverriddenMethod() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.getModule(), LanguageLevel.JDK_1_6); doTest("Annotate overriding methods"); } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/ExternalAnnotationsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/ExternalAnnotationsTest.java index 00d613f9f2ea..411a499bc169 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/ExternalAnnotationsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/ExternalAnnotationsTest.java @@ -14,12 +14,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.JavaModuleExternalPaths; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.*; @@ -70,6 +72,7 @@ public class ExternalAnnotationsTest extends UsefulTestCase { public void testAddedAnnotationInCodeWhenAlreadyPresent() { myFixture.configureByFile("src/withAnnotation/Foo.java"); + IdeaTestUtil.setModuleLanguageLevel(myFixture.getModule(), LanguageLevel.JDK_1_8); PsiMethod method = PsiTreeUtil.getParentOfType(myFixture.getElementAtCaret(), PsiMethod.class, false); assertNotNull(method); ActionContext context = myFixture.getActionContext(); diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JSpecifyTypeAnnotationFormatterTest.kt b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JSpecifyTypeAnnotationFormatterTest.kt new file mode 100644 index 000000000000..655cd6525d2b --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/JSpecifyTypeAnnotationFormatterTest.kt @@ -0,0 +1,102 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.psi.formatter.java + +import com.intellij.JavaTestUtil +import com.intellij.application.options.CodeStyle +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.roots.ModuleRootModificationUtil +import com.intellij.pom.java.LanguageLevel +import com.intellij.psi.codeStyle.CodeStyleManager +import com.intellij.psi.codeStyle.CommonCodeStyleSettings +import com.intellij.psi.codeStyle.CommonCodeStyleSettings.WRAP_ALWAYS +import com.intellij.testFramework.IdeaTestUtil +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase +import com.intellij.testFramework.fixtures.MavenDependencyUtil + +class JSpecifyTypeAnnotationFormatterTest : LightJavaCodeInsightFixtureTestCase() { + private val commonSettings: CommonCodeStyleSettings + get() = CodeStyle.getSettings(project).getCommonSettings(JavaLanguage.INSTANCE) + + override fun getTestDataPath() = "${JavaTestUtil.getJavaTestDataPath()}/psi/formatter/java/jSpecifyTypeAnnotation/" + + override fun setUp() { + super.setUp() + commonSettings.KEEP_LINE_BREAKS = false + commonSettings.METHOD_ANNOTATION_WRAP = WRAP_ALWAYS + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_1_8) + ModuleRootModificationUtil.updateModel(module) { model -> + MavenDependencyUtil.addFromMaven(model, "org.jspecify:jspecify:1.0.0") + } + } + + fun testKnownAnnotationBeforeType() = doTest() + + fun testKnownAnnotationBeforeTypeParameterList() = doTest() + + fun testCustomAnnotation() = doTest() + + fun testManyKnownAnnotations() = doTest() + + fun testPreserveWrappingSingleAnnotation() = doTest() + + fun testPreserveWrappingManyAnnotations() = doTest() + + fun testFullyQualifiedName() = doTest() + + fun testImportOnDemand() = doTest() + + fun testImportMix() = doTest() + + fun testSpacesInImportList() = doTest() + + fun testSpacesInFqnAnnotations() = doTest() + + fun testKeepLineBreaks() { + commonSettings.KEEP_LINE_BREAKS = true + doTest() + } + + fun testLowLanguageLevel() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_1_7) + doTest() + } + + fun testModuleImport() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) + doTest() + } + + fun testModuleImportWithSpaces() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) + doTest() + } + + fun testModuleImportMixedWithPackageImport() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) + doTest() + } + + fun testModuleImportMixedWithFqn() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) + doTest() + } + + + fun testLowLanguageLevelForModuleImport() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_24) + doTest() + } + + fun testUnknownModuleImport() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) + doTest() + } + + private fun doTest() { + val testName = getTestName(false) + myFixture.configureByFile("$testName.java") + WriteCommandAction.runWriteCommandAction(project) { CodeStyleManager.getInstance(project).reformatText(file, 0, editor.document.textLength) } + myFixture.checkResultByFile("${testName}_after.java") + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TypeAnnotationFormatterTest.kt b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TypeAnnotationFormatterTest.kt index 3ab429db5549..5016912be901 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TypeAnnotationFormatterTest.kt +++ b/java/java-tests/testSrc/com/intellij/java/psi/formatter/java/TypeAnnotationFormatterTest.kt @@ -24,7 +24,7 @@ class TypeAnnotationFormatterTest : LightJavaCodeInsightFixtureTestCase() { super.setUp() commonSettings.KEEP_LINE_BREAKS = false commonSettings.METHOD_ANNOTATION_WRAP = WRAP_ALWAYS - IdeaTestUtil.setProjectLanguageLevel(myFixture.project, LanguageLevel.JDK_1_8) + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_1_8) ModuleRootModificationUtil.updateModel(module, DefaultLightProjectDescriptor::addJetBrainsAnnotations) } @@ -56,7 +56,12 @@ class TypeAnnotationFormatterTest : LightJavaCodeInsightFixtureTestCase() { } fun testLowLanguageLevel() { - IdeaTestUtil.setProjectLanguageLevel(myFixture.project, LanguageLevel.JDK_1_7) + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_1_7) + doTest() + } + + fun testModuleImport() { + IdeaTestUtil.setModuleLanguageLevel(myFixture.module, LanguageLevel.JDK_25) doTest() }