mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
[java] Migrate contract provider extension point to hard coded values
#IDEA-366120 #IJ-CR-155341 (cherry picked from commit f0a38cf77f2a636e4d2b58f93f6d225d57d5738f) GitOrigin-RevId: 34263eb95a904729580bd9180dbbad2c1124ea33
This commit is contained in:
committed by
intellij-monorepo-bot
parent
edfee4abbf
commit
09b55a16cc
@@ -1,10 +1,7 @@
|
||||
// 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.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.NullabilityAnnotationInfo;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.*;
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.dataFlow.StandardMethodContract.ValueConstraint;
|
||||
@@ -52,7 +49,10 @@ public final class ContractInspection extends AbstractBaseJavaLocalInspectionToo
|
||||
public void visitAnnotation(@NotNull PsiAnnotation annotation) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (qualifiedName == null) return;
|
||||
if (!JvmContractAnnotationProvider.isMethodContract(qualifiedName)) return;
|
||||
if (!ContainerUtil.exists(
|
||||
StaticAnalysisAnnotationManager.getInstance().getKnownContractAnnotations(),
|
||||
fqn -> fqn.equals(qualifiedName))
|
||||
) return;
|
||||
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(annotation, PsiMethod.class);
|
||||
if (method == null) return;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
// 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.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.NullabilityAnnotationInfo;
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.*;
|
||||
import com.intellij.java.library.JavaLibraryModificationTracker;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -29,7 +26,7 @@ public final class JavaMethodContractUtil {
|
||||
private JavaMethodContractUtil() {}
|
||||
|
||||
/**
|
||||
* @deprecated To support contracts from different libraries please use {@link JvmContractAnnotationProvider}
|
||||
* @deprecated To support contracts from different libraries please use {@link StaticAnalysisAnnotationManager#getKnownContractAnnotations}
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName();
|
||||
@@ -235,7 +232,9 @@ public final class JavaMethodContractUtil {
|
||||
* @return a found annotation (null if not found)
|
||||
*/
|
||||
public static @Nullable PsiAnnotation findContractAnnotation(@NotNull PsiMethod method, boolean skipExternal) {
|
||||
return AnnotationUtil.findAnnotationInHierarchy(method, new HashSet<>(JvmContractAnnotationProvider.qualifiedNames()), skipExternal);
|
||||
return AnnotationUtil.findAnnotationInHierarchy(method,
|
||||
Set.of(StaticAnalysisAnnotationManager.getInstance().getKnownContractAnnotations()),
|
||||
skipExternal);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +244,9 @@ public final class JavaMethodContractUtil {
|
||||
* @return a found annotation (null if not found)
|
||||
*/
|
||||
public static @Nullable PsiAnnotation findContractAnnotation(@NotNull PsiMethod method) {
|
||||
return AnnotationUtil.findAnnotationInHierarchy(method, new HashSet<>(JvmContractAnnotationProvider.qualifiedNames()), false);
|
||||
return AnnotationUtil.findAnnotationInHierarchy(method,
|
||||
Set.of(StaticAnalysisAnnotationManager.getInstance().getKnownContractAnnotations()),
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// 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.codeInspection.dataFlow
|
||||
|
||||
class JbContractAnnotationProvider : JvmContractAnnotationProvider {
|
||||
override val fqn: String = "org.jetbrains.annotations.Contract"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// 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.codeInspection.dataFlow
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName
|
||||
|
||||
interface JvmContractAnnotationProvider {
|
||||
val fqn: String
|
||||
|
||||
companion object {
|
||||
private val EP_NAME: ExtensionPointName<JvmContractAnnotationProvider> =
|
||||
ExtensionPointName.Companion.create<JvmContractAnnotationProvider>("com.intellij.codeInsight.contractProvider")
|
||||
|
||||
@JvmStatic
|
||||
fun qualifiedNames(): List<String> {
|
||||
return EP_NAME.extensionList.map { it.fqn }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isMethodContract(fqn: String): Boolean {
|
||||
return qualifiedNames().any { it == fqn }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,6 @@
|
||||
</projectListeners>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint qualifiedName="com.intellij.codeInsight.contractProvider" interface="com.intellij.codeInspection.dataFlow.JvmContractAnnotationProvider" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.methodImplementor" interface="com.intellij.codeInsight.MethodImplementor" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.javaExpressionSurrounder"
|
||||
interface="com.intellij.codeInsight.generation.surroundWith.JavaExpressionSurrounder" dynamic="true"/>
|
||||
@@ -2762,7 +2761,6 @@
|
||||
<registryKey defaultValue="false" description="Suggested refactoring from call-site in Java"
|
||||
key="ide.java.refactoring.suggested.call.site"/>
|
||||
<dataflowIRProvider language="JAVA" implementationClass="com.intellij.codeInspection.dataFlow.java.JavaDataFlowIRProvider"/>
|
||||
<codeInsight.contractProvider implementation="com.intellij.codeInspection.dataFlow.JbContractAnnotationProvider"/>
|
||||
<java.effectively.final.fixer implementation="com.intellij.codeInsight.daemon.impl.quickfix.makefinal.MoveInitializerToIfBranchFixer"/>
|
||||
<java.effectively.final.fixer implementation="com.intellij.codeInspection.streamMigration.ConvertToStreamFixer"/>
|
||||
<postStartupActivity implementation="com.intellij.ide.FileNotInSourceRootChecker"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2021 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.codeInsight;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -24,6 +24,11 @@ public final class StaticAnalysisAnnotationManager {
|
||||
"org.gradle.api.Incubating"
|
||||
};
|
||||
|
||||
private static final String[] KNOWN_CONTRACT_ANNOTATIONS = {
|
||||
"org.jetbrains.annotations.Contract",
|
||||
"org.springframework.lang.Contract"
|
||||
};
|
||||
|
||||
public static StaticAnalysisAnnotationManager getInstance() {
|
||||
return ApplicationManager.getApplication().getService(StaticAnalysisAnnotationManager.class);
|
||||
}
|
||||
@@ -35,4 +40,11 @@ public final class StaticAnalysisAnnotationManager {
|
||||
public @NotNull String @NotNull [] getKnownUnstableApiAnnotations() {
|
||||
return KNOWN_UNSTABLE_API_ANNOTATIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of contract annotations that uses the contract syntax specified by {@link org.jetbrains.annotations.Contract}
|
||||
*/
|
||||
public @NotNull String @NotNull [] getKnownContractAnnotations() {
|
||||
return KNOWN_CONTRACT_ANNOTATIONS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.spring
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.refactoring.changeSignature.JavaChangeSignatureDialog
|
||||
|
||||
class SpringChangeSignatureContractTest : SpringJSpecifyLightHighlightingTestCase() {
|
||||
fun `test signature string contains contract annotation`() {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, """
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.lang.Contract;
|
||||
|
||||
class Baz {
|
||||
@Contract("null -> false")
|
||||
boolean fo<caret>o(@Nullable String name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
val psiMethod = myFixture.elementAtCaret as PsiMethod
|
||||
val actualSignature = JavaChangeSignatureDialog(myFixture.project, psiMethod, false, psiMethod).calculateSignature()
|
||||
assertEquals("""
|
||||
@Contract("null -> false")
|
||||
boolean foo(@Nullable String name)
|
||||
""".trimIndent(), actualSignature)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.spring
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.ConstantValueInspection
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
|
||||
class SpringConstantValueInspectionTest : SpringJSpecifyLightHighlightingTestCase() {
|
||||
private val inspection = ConstantValueInspection()
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(inspection)
|
||||
}
|
||||
|
||||
fun `test condition is always false`() {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, """
|
||||
class Baz {
|
||||
@org.springframework.lang.Contract("null -> false")
|
||||
boolean foo(@org.jspecify.annotations.Nullable String name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void bar() {
|
||||
if (<warning descr="Condition 'foo(null)' is always 'false'">foo(null)</warning>) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting()
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
try {
|
||||
myFixture.disableInspections(inspection)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
addSuppressedException(e)
|
||||
}
|
||||
finally {
|
||||
super.tearDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.spring
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.ContractInspection
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
|
||||
class SpringContractIssuesInspectionTest : SpringJSpecifyLightHighlightingTestCase() {
|
||||
private val inspection = ContractInspection()
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.enableInspections(inspection)
|
||||
}
|
||||
|
||||
fun `test parameter mismatch`() {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, """
|
||||
class Baz {
|
||||
@org.springframework.lang.Contract("<warning descr="Method takes 0 parameters, while contract clause '_ -> fail' expects 1">_ -> fail</warning>")
|
||||
void foo() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
myFixture.testHighlighting()
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
try {
|
||||
myFixture.disableInspections(inspection)
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
addSuppressedException(e)
|
||||
}
|
||||
finally {
|
||||
super.tearDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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.spring
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ContentEntry
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase
|
||||
import com.intellij.testFramework.fixtures.MavenDependencyUtil
|
||||
|
||||
abstract class SpringJSpecifyLightHighlightingTestCase : LightJavaCodeInsightFixtureTestCase() {
|
||||
override fun getProjectDescriptor() = object : ProjectDescriptor(LanguageLevel.HIGHEST) {
|
||||
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
|
||||
super.configureModule(module, model, contentEntry)
|
||||
MavenDependencyUtil.addFromMaven(model, "org.springframework:spring-core:6.2.3")
|
||||
MavenDependencyUtil.addFromMaven(model, "org.jspecify:jspecify:1.0.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.spring
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType
|
||||
import com.intellij.psi.PsiParameter
|
||||
import com.intellij.refactoring.safeDelete.SafeDeleteHandler
|
||||
|
||||
class SpringSafeDeleteContractTest : SpringJSpecifyLightHighlightingTestCase() {
|
||||
fun `test signature string contains contract annotation`() {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, """
|
||||
class Baz {
|
||||
@org.springframework.lang.Contract("null, _ -> false")
|
||||
public static boolean foo(Object o1, Object o<caret>2) {
|
||||
return o1 != null;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
val psiParameter = myFixture.elementAtCaret as PsiParameter
|
||||
SafeDeleteHandler.invoke(project, arrayOf(psiParameter), true)
|
||||
myFixture.checkResult("""
|
||||
class Baz {
|
||||
@org.springframework.lang.Contract("null -> false")
|
||||
public static boolean foo(Object o1) {
|
||||
return o1 != null;
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user