georgii-ustinov/252/IDEA-168035

This MR duplicates the https://code.jetbrains.team/p/ij/repositories/ultimate/reviews/173952/timeline, but fixes the conflicts community/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/AnnotateMethodInGeneratedFilesTest.java

Merge-request: IJ-MR-175150
Merged-by: Georgii Ustinov <georgii.ustinov@jetbrains.com>

GitOrigin-RevId: 69f1417a59be188fab171f8d7bfdbd57d40eddc7
This commit is contained in:
Georgii Ustinov
2025-09-10 15:01:55 +00:00
committed by intellij-monorepo-bot
parent 773f837fff
commit dd035bc29f
47 changed files with 1032 additions and 11 deletions

View File

@@ -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. // 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 package com.intellij.psi.formatter.java
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.codeInsight.DumbAwareAnnotationUtil import com.intellij.codeInsight.DumbAwareAnnotationUtil
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import com.intellij.openapi.roots.LanguageLevelProjectExtension import com.intellij.pom.java.JavaFeature
import com.intellij.pom.java.LanguageLevel
import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiKeyword import com.intellij.psi.PsiKeyword
import com.intellij.psi.PsiModifierListOwner 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.formatter.java.JavaFormatterAnnotationUtil.isFieldWithAnnotations
import com.intellij.psi.impl.source.tree.JavaElementType import com.intellij.psi.impl.source.tree.JavaElementType
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
internal object JavaFormatterAnnotationUtil { internal object JavaFormatterAnnotationUtil {
private val KNOWN_TYPE_ANNOTATIONS: Set<String> = setOf( private val KNOWN_TYPE_ANNOTATIONS: Set<String> = setOf(
"org.jetbrains.annotations.NotNull", AnnotationUtil.NOT_NULL,
"org.jetbrains.annotations.Nullable" AnnotationUtil.NULLABLE,
AnnotationUtil.J_SPECIFY_NON_NULL,
AnnotationUtil.J_SPECIFY_NULLABLE,
) )
/** /**
@@ -31,8 +34,7 @@ internal object JavaFormatterAnnotationUtil {
fun isTypeAnnotation(annotation: ASTNode): Boolean { fun isTypeAnnotation(annotation: ASTNode): Boolean {
val node = annotation.psi as? PsiAnnotation ?: return false val node = annotation.psi as? PsiAnnotation ?: return false
val languageLevel = LanguageLevelProjectExtension.getInstance(node.project).languageLevel if (!PsiUtil.isAvailable(JavaFeature.TYPE_ANNOTATIONS, node)) return false
if (languageLevel.isLessThan(LanguageLevel.JDK_1_8)) return false
val next = PsiTreeUtil.skipSiblingsForward(node, PsiWhiteSpace::class.java, PsiAnnotation::class.java) val next = PsiTreeUtil.skipSiblingsForward(node, PsiWhiteSpace::class.java, PsiAnnotation::class.java)
if (next is PsiKeyword) return false if (next is PsiKeyword) return false

View File

@@ -26,6 +26,9 @@ public class AnnotationUtil {
public static final String NOT_NULL = "org.jetbrains.annotations.NotNull"; 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 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 NON_NLS = "org.jetbrains.annotations.NonNls";
public static final String NLS = "org.jetbrains.annotations.Nls"; public static final String NLS = "org.jetbrains.annotations.Nls";

View File

@@ -5,10 +5,12 @@ import com.intellij.codeInsight.DumbAwareAnnotationUtil.KNOWN_ANNOTATIONS
import com.intellij.codeInsight.DumbAwareAnnotationUtil.hasAnnotation import com.intellij.codeInsight.DumbAwareAnnotationUtil.hasAnnotation
import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.pom.java.JavaFeature
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.PsiUtil
/** /**
* Utility which helps to detect annotation in `Dumb mode`. * Utility which helps to detect annotation in `Dumb mode`.
@@ -16,10 +18,24 @@ import com.intellij.psi.util.PsiModificationTracker
object DumbAwareAnnotationUtil { object DumbAwareAnnotationUtil {
private const val JAVA_LANG_PACKAGE = "java.lang" 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( private val KNOWN_ANNOTATIONS = setOf(
AnnotationUtil.NOT_NULL, AnnotationUtil.NOT_NULL,
AnnotationUtil.NULLABLE, 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. * Formats the given fully qualified name (FQN) by trimming whitespace around each segment.
*/ */
@JvmStatic @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<String> = CachedValuesManager.getCachedValue(file) { private fun getImportedKnownAnnotations(file: PsiJavaFile): Set<String> = CachedValuesManager.getCachedValue(file) {
val importList = file.importList val importList = file.importList
?: return@getCachedValue CachedValueProvider.Result(emptySet(), PsiModificationTracker.MODIFICATION_COUNT) ?: 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() } .mapNotNull { fqn -> fqn.split(".").lastOrNull() }
.toSet() .toSet()
CachedValueProvider.Result.create(filteredAnnotations, PsiModificationTracker.MODIFICATION_COUNT) 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 { private fun getAnnotationImportInfo(annotationFqn: String): AnnotationImportInfo {
val packageName = StringUtil.getPackageName(annotationFqn) val packageName = StringUtil.getPackageName(annotationFqn)
val className = StringUtil.getShortName(annotationFqn) val className = StringUtil.getShortName(annotationFqn)

View File

@@ -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 <T, V> List<T> getCustomList() {
return null;
}
}

View File

@@ -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
<T, V> List<T> getCustomList() {
return null;
}
}

View File

@@ -0,0 +1,23 @@
package org.example;
public class Formatter {
@org.jspecify.annotations.NonNull
<T, V> List<T> getNotNullList() {
return List.of();
}
@org.jspecify.annotations.Nullable
<T, V> List<T> getNullableList() {
return null;
}
@org.jspecify.annotations.NonNull
String getNotNullName() {
return "";
}
@org.jspecify.annotations.Nullable
String getNullableName() {
return null;
}
}

View File

@@ -0,0 +1,19 @@
package org.example;
public class Formatter {
@org.jspecify.annotations.NonNull <T, V> List<T> getNotNullList() {
return List.of();
}
@org.jspecify.annotations.Nullable <T, V> List<T> getNullableList() {
return null;
}
@org.jspecify.annotations.NonNull String getNotNullName() {
return "";
}
@org.jspecify.annotations.Nullable String getNullableName() {
return null;
}
}

View File

@@ -0,0 +1,16 @@
package org.example;
import org.jspecify.annotations.*;
import java.util.List;
public class Formatter {
@NonNull @org.jspecify.annotations.Nullable
<T, V> List<T> getNotNullList() {
return List.of();
}
@NonNull @org.jspecify.annotations.Nullable
String getNotNullName() {
return "";
}
}

View File

@@ -0,0 +1,15 @@
package org.example;
import org.jspecify.annotations.*;
import java.util.List;
public class Formatter {
@NonNull @org.jspecify.annotations.Nullable <T, V> List<T> getNotNullList() {
return List.of();
}
@NonNull @org.jspecify.annotations.Nullable String getNotNullName() {
return "";
}
}

View File

@@ -0,0 +1,26 @@
package org.example;
import org.jspecify.annotations.*;
import java.util.List;
public class Formatter {
@NonNull
<T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable
<T, V> List<T> getNullableList() {
return null;
}
@NonNull
String getNotNullName() {
return "";
}
@Nullable
String getNullableName() {
return null;
}
}

View File

@@ -0,0 +1,23 @@
package org.example;
import org.jspecify.annotations.*;
import java.util.List;
public class Formatter {
@NonNull <T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable <T, V> List<T> getNullableList() {
return null;
}
@NonNull String getNotNullName() {
return "";
}
@Nullable String getNullableName() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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;
}
}

View File

@@ -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 <T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable <T, V> List<T> getNullableList() {
return null;
}
}

View File

@@ -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 <T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable <T, V> List<T> getNullableList() {
return null;
}
}

View File

@@ -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;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T, V> List<T> getCustomList() {
return null;
}
}

View File

@@ -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 <T, V> List<T> getCustomList() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@org.jspecify.annotations.Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@org.jspecify.annotations.Nullable @NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable @NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable @NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable @NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T, V> List<T> getStrangeList() {
return List.of();
}
@NonNull
@Nullable
String getNotNullName() {
return "";
}
}

View File

@@ -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 <T, V> List<T> getStrangeList() {
return List.of();
}
@NonNull @Nullable String getNotNullName() {
return "";
}
}

View File

@@ -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
<T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable
<T, V> List<T> getNullableList() {
return null;
}
@NonNull
String getNotNullName() {
return "";
}
@Nullable
String getNullableName() {
return null;
}
}

View File

@@ -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 <T, V> List<T> getNotNullList() {
return List.of();
}
@Nullable <T, V> List<T> getNullableList() {
return null;
}
@NonNull String getNotNullName() {
return "";
}
@Nullable String getNullableName() {
return null;
}
}

View File

@@ -0,0 +1,24 @@
package org.example;
public class Formatter {
@ org . jspecify .annotations . NonNull
<T, V> List<T> getNotNullList() {
return List.of();
}
@org. jspecify. annotations .
Nullable
<T, V> List<T> getNullableList() {
return null;
}
@ org. jspecify . annotations. NonNull
String getNotNullName() {
return "";
}
@ org . jspecify . annotations . Nullable
String getNullableName() {
return null;
}
}

View File

@@ -0,0 +1,19 @@
package org.example;
public class Formatter {
@org.jspecify.annotations.NonNull <T, V> List<T> getNotNullList() {
return List.of();
}
@org.jspecify.annotations.Nullable <T, V> List<T> getNullableList() {
return null;
}
@org.jspecify.annotations.NonNull String getNotNullName() {
return "";
}
@org.jspecify.annotations.Nullable String getNullableName() {
return null;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -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
<T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NonNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable
@NotNull
<T> String breakLineMixed() {
return null;
}
}

View File

@@ -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 <T> String breakLineBetweenTypeParameterAndAnnotation() {
return null;
}
@Nullable @NotNull <T> String breakLineMixed() {
return null;
}
}

View File

@@ -8,7 +8,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.GeneratedSourcesFilter; import com.intellij.openapi.roots.GeneratedSourcesFilter;
import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClass;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -38,6 +40,7 @@ public class AnnotateMethodInGeneratedFilesTest extends LightJavaCodeInsightFixt
} }
public void testAnnotateOverriddenMethod() { public void testAnnotateOverriddenMethod() {
IdeaTestUtil.setModuleLanguageLevel(myFixture.getModule(), LanguageLevel.JDK_1_6);
doTest("Annotate overriding methods"); doTest("Annotate overriding methods");
} }

View File

@@ -14,12 +14,14 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JavaModuleExternalPaths; import com.intellij.openapi.roots.JavaModuleExternalPaths;
import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiMethod;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.*; import com.intellij.testFramework.fixtures.*;
@@ -70,6 +72,7 @@ public class ExternalAnnotationsTest extends UsefulTestCase {
public void testAddedAnnotationInCodeWhenAlreadyPresent() { public void testAddedAnnotationInCodeWhenAlreadyPresent() {
myFixture.configureByFile("src/withAnnotation/Foo.java"); myFixture.configureByFile("src/withAnnotation/Foo.java");
IdeaTestUtil.setModuleLanguageLevel(myFixture.getModule(), LanguageLevel.JDK_1_8);
PsiMethod method = PsiTreeUtil.getParentOfType(myFixture.getElementAtCaret(), PsiMethod.class, false); PsiMethod method = PsiTreeUtil.getParentOfType(myFixture.getElementAtCaret(), PsiMethod.class, false);
assertNotNull(method); assertNotNull(method);
ActionContext context = myFixture.getActionContext(); ActionContext context = myFixture.getActionContext();

View File

@@ -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")
}
}

View File

@@ -24,7 +24,7 @@ class TypeAnnotationFormatterTest : LightJavaCodeInsightFixtureTestCase() {
super.setUp() super.setUp()
commonSettings.KEEP_LINE_BREAKS = false commonSettings.KEEP_LINE_BREAKS = false
commonSettings.METHOD_ANNOTATION_WRAP = WRAP_ALWAYS 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) ModuleRootModificationUtil.updateModel(module, DefaultLightProjectDescriptor::addJetBrainsAnnotations)
} }
@@ -56,7 +56,12 @@ class TypeAnnotationFormatterTest : LightJavaCodeInsightFixtureTestCase() {
} }
fun testLowLanguageLevel() { 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() doTest()
} }