[Java. Code Formatting] IDEA-168035 Distinct annotation formatting option for type/value/field/method annotations

- Add jspecify nullability annotations as known type annotations

GitOrigin-RevId: 6560fc183a995a7c5b43b50e0c27995df090b7a6
This commit is contained in:
Georgii Ustinov
2025-08-27 17:37:41 +03:00
committed by intellij-monorepo-bot
parent fd3e7fd3df
commit 168c2a50db
30 changed files with 608 additions and 2 deletions

View File

@@ -17,7 +17,9 @@ import com.intellij.psi.util.PsiTreeUtil
internal object JavaFormatterAnnotationUtil {
private val KNOWN_TYPE_ANNOTATIONS: Set<String> = setOf(
"org.jetbrains.annotations.NotNull",
"org.jetbrains.annotations.Nullable"
"org.jetbrains.annotations.Nullable",
"org.jspecify.annotations.NonNull",
"org.jspecify.annotations.Nullable",
)
/**

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_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";

View File

@@ -19,7 +19,9 @@ object DumbAwareAnnotationUtil {
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
)
/**

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,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,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,71 @@
// 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.setProjectLanguageLevel(myFixture.project, 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.setProjectLanguageLevel(myFixture.project, LanguageLevel.JDK_1_7)
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")
}
}