mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-inspections] IDEA-376220 Fix JavaApiUsageInspection not respecting preview features
Previously: Assume we use language level 24, but JDK 25. And that we used some *preview* API from JDK 25. The quick-fix from JavaApiUsageInspection suggested bumping language level to 25. Now: It is fixed, i.e. the quick-fix suggests bumping language level to 25 preview. Merge-request: IJ-MR-172531 Merged-by: Bartek Pacia <bartek.pacia@jetbrains.com> GitOrigin-RevId: 4966e043c75a3b7ff5c19debe8e600579aad47c3
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b5cc3b7714
commit
954aed5dda
+13
-5
@@ -86,11 +86,7 @@ public final class JavaPreviewFeatureUtil {
|
||||
if (element instanceof PsiJavaCodeReferenceElement refElement) {
|
||||
PsiElement resolved = refElement.resolve();
|
||||
if (resolved instanceof PsiModifierListOwner owner) {
|
||||
PsiAnnotation annotation = getPreviewFeatureAnnotationInternal(owner);
|
||||
JavaFeature feature = fromPreviewFeatureAnnotation(annotation);
|
||||
if (feature == null) return null;
|
||||
if (isParticipating(refElement, owner)) return null;
|
||||
return new PreviewFeatureUsage(feature, refElement, owner, annotation);
|
||||
return getPreviewFeatureUsage(refElement, owner);
|
||||
}
|
||||
}
|
||||
else if (element instanceof PsiRequiresStatement requiresStatement) {
|
||||
@@ -122,6 +118,18 @@ public final class JavaPreviewFeatureUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getPreviewFeatureUsage(PsiElement)
|
||||
*/
|
||||
public static @Nullable PreviewFeatureUsage getPreviewFeatureUsage(@NotNull PsiJavaCodeReferenceElement refElement,
|
||||
@NotNull PsiModifierListOwner owner) {
|
||||
PsiAnnotation annotation = getPreviewFeatureAnnotationInternal(owner);
|
||||
JavaFeature feature = fromPreviewFeatureAnnotation(annotation);
|
||||
if (feature == null) return null;
|
||||
if (isParticipating(refElement, owner)) return null;
|
||||
return new PreviewFeatureUsage(feature, refElement, owner, annotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Participating source code means that such code can access preview feature api in the same package without warnings.
|
||||
*
|
||||
|
||||
@@ -128,7 +128,7 @@ public final class IdeaTestUtil {
|
||||
|
||||
private static Sdk createMockJdkFromRepository(String name, int version) {
|
||||
List<RemoteRepositoryDescription> repos = MavenDependencyUtil.getRemoteRepositoryDescriptions();
|
||||
String coordinates = "org.jetbrains.mockjdk:"+ MOCK_JDK_GROUP_ID + ":" + version + ".0";
|
||||
String coordinates = "org.jetbrains.mockjdk:" + MOCK_JDK_GROUP_ID + ":" + version + ".0";
|
||||
RepositoryLibraryProperties libraryProperties = new RepositoryLibraryProperties(coordinates, false);
|
||||
Collection<OrderRoot> roots =
|
||||
JarRepositoryManager.loadDependenciesModal(ProjectManager.getInstance().getDefaultProject(), libraryProperties, false, false, null,
|
||||
|
||||
@@ -220,6 +220,7 @@ jvm.inspections.string.touppercase.tolowercase.without.locale.description=<code>
|
||||
jvm.inspections.api.display.name=Usages of API which isn't available at the configured language level
|
||||
assertequals.between.inconvertible.types.display.name='assertEquals()' between objects of inconvertible types
|
||||
jvm.inspections.1.5.problem.descriptor=Usage of API documented as @since {0}+
|
||||
jvm.inspections.1.5.problem.descriptor.preview=Usage of preview API documented as @since {0}+
|
||||
jvm.inspections.1.7.problem.descriptor=Usage of generified after 1.6 API which would cause compilation problems with JDK {0}
|
||||
jvm.inspections.1.8.problem.descriptor=Default {0, choice, 0#|1#method is|2# methods are} not overridden. It would cause compilation problems with JDK {1}
|
||||
jvm.inspections.1.8.problem.single.descriptor=Default method ''{0}'' is not overridden. It would cause compilation problems with JDK {1}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.codeInspection.options.OptPane
|
||||
import com.intellij.codeInspection.options.OptPane.*
|
||||
import com.intellij.codeInspection.options.OptionController
|
||||
import com.intellij.java.JavaBundle
|
||||
import com.intellij.java.codeserver.core.JavaPreviewFeatureUtil
|
||||
import com.intellij.lang.Language
|
||||
import com.intellij.openapi.module.JdkApiCompatibilityService
|
||||
import com.intellij.openapi.module.LanguageLevelUtil
|
||||
@@ -191,7 +192,7 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
languageLevel = sourcePsi.containingFile.getUserData(PsiUtil.FILE_LANGUAGE_LEVEL_KEY)
|
||||
}
|
||||
if (languageLevel == null) return
|
||||
val firstCompatibleLanguageLevel = JdkApiCompatibilityService.getInstance().firstCompatibleLanguageLevel(target, languageLevel)
|
||||
var firstCompatibleLanguageLevel = JdkApiCompatibilityService.getInstance().firstCompatibleLanguageLevel(target, languageLevel)
|
||||
if (firstCompatibleLanguageLevel != null) {
|
||||
val psiClass = if (qualifier != null) {
|
||||
PsiUtil.resolveClassInType(qualifier.getExpressionType())
|
||||
@@ -205,6 +206,15 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
if (isIgnored(superClass)) return
|
||||
}
|
||||
}
|
||||
|
||||
if (sourcePsi is PsiJavaCodeReferenceElement) {
|
||||
val previewFeatureUsage = JavaPreviewFeatureUtil.getPreviewFeatureUsage(sourcePsi, target)
|
||||
val previewLevel = firstCompatibleLanguageLevel.getPreviewLevel()
|
||||
if (previewFeatureUsage != null && previewLevel != null) {
|
||||
firstCompatibleLanguageLevel = previewLevel
|
||||
}
|
||||
}
|
||||
|
||||
registerError(sourcePsi, firstCompatibleLanguageLevel, holder, isOnTheFly)
|
||||
}
|
||||
else if (target is PsiClass && !languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
|
||||
@@ -240,9 +250,13 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
|
||||
private fun registerError(reference: PsiElement, sinceLanguageLevel: LanguageLevel, holder: ProblemsHolder, isOnTheFly: Boolean) {
|
||||
if (reference.getUastParentOfType<UComment>() != null) return
|
||||
val message = JvmAnalysisBundle.message(
|
||||
"jvm.inspections.1.5.problem.descriptor", sinceLanguageLevel.toJavaVersion().toFeatureString()
|
||||
)
|
||||
val message = if (sinceLanguageLevel.isPreview) {
|
||||
JvmAnalysisBundle.message("jvm.inspections.1.5.problem.descriptor.preview", sinceLanguageLevel.toJavaVersion().toFeatureString())
|
||||
}
|
||||
else {
|
||||
JvmAnalysisBundle.message("jvm.inspections.1.5.problem.descriptor", sinceLanguageLevel.toJavaVersion().toFeatureString())
|
||||
}
|
||||
|
||||
val fix = if (isOnTheFly) {
|
||||
QuickFixFactory.getInstance().createIncreaseLanguageLevelFix(sinceLanguageLevel) as LocalQuickFix
|
||||
}
|
||||
@@ -253,4 +267,4 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() {
|
||||
private fun getEffectiveLanguageLevel(module: Module): LanguageLevel {
|
||||
return effectiveLanguageLevel ?: LanguageLevelUtil.getEffectiveLanguageLevel(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+68
-5
@@ -2,14 +2,16 @@ package com.intellij.codeInspection.tests.java
|
||||
|
||||
import com.intellij.jvm.analysis.JavaJvmAnalysisTestUtil
|
||||
import com.intellij.jvm.analysis.internal.testFramework.JavaApiUsageInspectionTestBase
|
||||
import com.intellij.jvm.analysis.testFramework.JvmLanguage
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ContentEntry
|
||||
import com.intellij.openapi.roots.LanguageLevelProjectExtension
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import org.junit.Ignore
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.BlockJUnit4ClassRunner
|
||||
|
||||
/**
|
||||
* This is a base test case for test cases that highlight all the use of APIs
|
||||
@@ -21,8 +23,6 @@ import org.junit.runners.BlockJUnit4ClassRunner
|
||||
* - Set `JAVA_HOME` to JDK 1.8. In this case it's possible to redefine JDK's own classes like `String` or `Class`
|
||||
* - Invoke `./compile.sh`. The new class(es) will appear in `./classes`
|
||||
*/
|
||||
@Ignore
|
||||
@RunWith(BlockJUnit4ClassRunner::class) // disabled because there are currently no tests for a JDK higher than the highest mock JDK
|
||||
class JavaJavaApiUsageInspectionWithCustomMockJdkTest : JavaApiUsageInspectionTestBase() {
|
||||
override fun getBasePath(): String = JavaJvmAnalysisTestUtil.TEST_DATA_PROJECT_RELATIVE_BASE_PATH
|
||||
|
||||
@@ -33,4 +33,67 @@ class JavaJavaApiUsageInspectionWithCustomMockJdkTest : JavaApiUsageInspectionTe
|
||||
PsiTestUtil.newLibrary("JDKMock").classesRoot("$dataDir/classes").addTo(model)
|
||||
}
|
||||
}
|
||||
|
||||
fun `test language level 24 preview with JDK 25`() {
|
||||
myFixture.addClass("""
|
||||
package jdk.internal.javac;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.METHOD,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.FIELD,
|
||||
ElementType.PACKAGE,
|
||||
ElementType.TYPE})
|
||||
// CLASS retention will hopefully be sufficient for the purposes at hand
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
// *Not* @Documented
|
||||
public @interface PreviewFeature {
|
||||
/**
|
||||
* Name of the preview feature the annotated API is associated
|
||||
* with.
|
||||
*/
|
||||
public Feature feature();
|
||||
|
||||
public enum Feature {
|
||||
PEM_API,
|
||||
STABLE_VALUES
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.addClass("""
|
||||
package java.lang;
|
||||
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
||||
/**
|
||||
* @since 25
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES)
|
||||
public class StableValue<T> {
|
||||
private StableValue(T value) {}
|
||||
public static <X> StableValue<X> of(X value) {
|
||||
return new StableValue<>(value);
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
myFixture.setLanguageLevel(LanguageLevel.JDK_24)
|
||||
println("DBG: language level is: " + LanguageLevelProjectExtension.getInstance(project).getLanguageLevel())
|
||||
println("DBGL: location: " + JavaPsiFacade.getInstance(project).findClass("java.lang.String", GlobalSearchScope.allScope(project))?.containingFile?.virtualFile?.path)
|
||||
myFixture.testHighlighting(JvmLanguage.JAVA, """
|
||||
import java.util.concurrent.StructuredTaskScope;
|
||||
|
||||
class Main {
|
||||
static void main() {
|
||||
StructuredTaskScope a; // JEP 505
|
||||
<error descr="Usage of preview API documented as @since 25+"><error descr="java.lang.StableValue is a preview API and is disabled by default">StableValue<String></error></error> b = <error descr="Usage of preview API documented as @since 25+"><error descr="java.lang.StableValue is a preview API and is disabled by default">StableValue</error></error>.<caret>of("foo");
|
||||
}
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
val intention = myFixture.getAvailableIntention("Set language level to 25 (Preview) - Primitive Types in Patterns, etc.")
|
||||
myFixture.launchAction(intention!!)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user