From afd09dde1cf844e5bfb6e4c5e0704ffa92764dc3 Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Thu, 11 Aug 2022 14:21:36 +0200 Subject: [PATCH] [jvm] Code and documentation improvements to Java API inspection GitOrigin-RevId: 752f3ac5b9bea956323585f75327149582dfbf3f --- .../openapi/module/LanguageLevelUtil.java | 7 ++++- .../codeInspection/JavaApiUsageInspection.kt | 29 +++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java b/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java index df8baf155965..34ef1b1e15ac 100644 --- a/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java +++ b/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java @@ -88,6 +88,10 @@ public class LanguageLevelUtil { return ourPresentableShortMessage.get(languageLevel); } + /** + * For performance reasons the forbidden API is pre-generated. + * @see com.intellij.codeInspection.tests.JavaApiUsageGenerator + */ @Nullable private static Set getForbiddenApi(@NotNull LanguageLevel languageLevel) { if (!ourPresentableShortMessage.containsKey(languageLevel)) return null; @@ -110,7 +114,8 @@ public class LanguageLevelUtil { /** * @param member The {@link PsiMember} to get the language level from * @param languageLevel The effective language level - * @return The last compatible language level for a {@link PsiMember} as annotated by the @since javadoc + * @return The last incompatible language level for a {@link PsiMember} as annotated by the @since javadoc or null if it is unknown. + * For example, if a method is annotated as @since 9 this method will return {@link LanguageLevel#JDK_1_8}. */ public static @Nullable LanguageLevel getLastIncompatibleLanguageLevel(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { if (member instanceof PsiAnonymousClass) return null; diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt index dc6973ed592f..528ff3e3fe23 100644 --- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt +++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt @@ -161,13 +161,12 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { val sourcePsi = method.sourcePsi ?: return val module = ModuleUtilCore.findModuleForPsiElement(sourcePsi) ?: return val languageLevel = getEffectiveLanguageLevel(module) - val sinceLanguageLevel = overriddenMethods.mapNotNull { overriddenMethod -> + val lastIncompatibleLevel = overriddenMethods.mapNotNull { overriddenMethod -> LanguageLevelUtil.getLastIncompatibleLanguageLevel(overriddenMethod, languageLevel) }.minOrNull() ?: return val toHighlight = overrideAnnotation?.uastAnchor?.sourcePsi ?: method.uastAnchor?.sourcePsi ?: return - val jdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(sourcePsi) ?: return - if (checkSdkLevel(sinceLanguageLevel, jdkVersion)) return - registerError(toHighlight, sinceLanguageLevel, holder, isOnTheFly) + if (shouldReportSinceLevelForElement(lastIncompatibleLevel, sourcePsi) == true) return + registerError(toHighlight, lastIncompatibleLevel, holder, isOnTheFly) } } @@ -179,10 +178,9 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { val sourcePsi = sourceNode.sourcePsi ?: return val module = ModuleUtilCore.findModuleForPsiElement(sourcePsi) ?: return val languageLevel = getEffectiveLanguageLevel(module) - val sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(constructor, languageLevel) ?: return - val jdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(sourcePsi) ?: return - if (checkSdkLevel(sinceLanguageLevel, jdkVersion)) return - registerError(sourcePsi, sinceLanguageLevel, holder, isOnTheFly) + val lastIncompatibleLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(constructor, languageLevel) ?: return + if (shouldReportSinceLevelForElement(lastIncompatibleLevel, sourcePsi) == true) return + registerError(sourcePsi, lastIncompatibleLevel, holder, isOnTheFly) } override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) { @@ -190,10 +188,9 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { if (target !is PsiMember) return val module = ModuleUtilCore.findModuleForPsiElement(sourcePsi) ?: return val languageLevel = getEffectiveLanguageLevel(module) - val sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(target, languageLevel) - if (sinceLanguageLevel != null) { - val jdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(sourcePsi) ?: return - if (checkSdkLevel(sinceLanguageLevel, jdkVersion)) return + val lastIncompatibleLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(target, languageLevel) + if (lastIncompatibleLevel != null) { + if (shouldReportSinceLevelForElement(lastIncompatibleLevel, sourcePsi) == true) return val psiClass = if (qualifier != null) { PsiUtil.resolveClassInType(qualifier.getExpressionType()) } @@ -206,7 +203,7 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { if (isIgnored(superClass)) return } } - registerError(sourcePsi, sinceLanguageLevel, holder, isOnTheFly) + registerError(sourcePsi, lastIncompatibleLevel, holder, isOnTheFly) } else if (target is PsiClass && !languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) { for (generifiedClass in generifiedClasses) { @@ -240,8 +237,10 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { } /** Only runs in production because tests have incorrect SDKs when no mock SDK is available. */ - private fun checkSdkLevel(sinceLanguageLevel: LanguageLevel, jdkVersion: JavaSdkVersion) = - sinceLanguageLevel.isAtLeast(jdkVersion.maxLanguageLevel) && !ApplicationManager.getApplication().isUnitTestMode + private fun shouldReportSinceLevelForElement(lastIncompatibleLevel: LanguageLevel, context: PsiElement): Boolean? { + val jdkVersion = JavaVersionService.getInstance().getJavaSdkVersion(context) ?: return null + return lastIncompatibleLevel.isAtLeast(jdkVersion.maxLanguageLevel) && !ApplicationManager.getApplication().isUnitTestMode + } private fun registerError(reference: PsiElement, sinceLanguageLevel: LanguageLevel, holder: ProblemsHolder, isOnTheFly: Boolean) { val targetLanguageLevel = LanguageLevelUtil.getNextLanguageLevel(sinceLanguageLevel) ?: run {