From e48a6f2573105a07ba787e0f87bf6f52791ee5c9 Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Tue, 23 Nov 2021 15:31:24 +0100 Subject: [PATCH] [java] Move 'forbiddenApi' API to LanguageLevelUtil Decouples Java15APIUsageInspection from other inspections and completion GitOrigin-RevId: ae37f8f01514e2e557acac9757b11cba11bcdbb5 --- .../openapi/module/LanguageLevelUtil.java | 152 ++++++++++++++++- .../com/intellij/openapi/module}/api1.4.txt | 0 .../com/intellij/openapi/module}/api1.5.txt | 0 .../com/intellij/openapi/module}/api1.6.txt | 0 .../com/intellij/openapi/module}/api1.7.txt | 0 .../com/intellij/openapi/module}/api1.8.txt | 0 .../com/intellij/openapi/module}/api10.txt | 0 .../com/intellij/openapi/module}/api11.txt | 0 .../com/intellij/openapi/module}/api12.txt | 0 .../com/intellij/openapi/module}/api13.txt | 0 .../com/intellij/openapi/module}/api14.txt | 0 .../com/intellij/openapi/module}/api15.txt | 0 .../com/intellij/openapi/module}/api16.txt | 0 .../com/intellij/openapi/module}/api17.txt | 0 .../src/com/intellij/openapi/module/api9.txt} | 0 .../CharsetObjectCanBeUsedInspection.java | 6 +- .../FunctionalInterfaceSuggester.java | 6 +- .../completion/JavaCompletionUtil.java | 4 +- .../java15api/Java15APIUsageInspection.java | 154 ++---------------- .../MissingOverrideAnnotationInspection.java | 5 +- ...ImplicitDefaultCharsetUsageInspection.java | 6 +- .../inspections/Java15FormInspection.java | 7 +- 22 files changed, 179 insertions(+), 161 deletions(-) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api1.4.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api1.5.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api1.6.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api1.7.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api1.8.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api10.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api11.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api12.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api13.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api14.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api15.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api16.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api => java-analysis-api/src/com/intellij/openapi/module}/api17.txt (100%) rename java/{java-impl/src/com/intellij/codeInspection/java15api/api1.9.txt => java-analysis-api/src/com/intellij/openapi/module/api9.txt} (100%) 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 5d6402554791..df6e72fb6dff 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 @@ -1,15 +1,27 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.module; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.LanguageLevelModuleExtension; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.ref.Reference; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.*; + public class LanguageLevelUtil { /** * Returns explicitly specified custom language level for {@code module}, or {@code null} if the module uses 'Project default' language level @@ -26,4 +38,142 @@ public class LanguageLevelUtil { if (level != null) return level; return LanguageLevelProjectExtension.getInstance(module.getProject()).getLanguageLevel(); } + + private static final Map>> ourForbiddenAPI = new EnumMap<>(LanguageLevel.class); + + public static String getJdkName(LanguageLevel languageLevel) { + final String presentableText = languageLevel.getPresentableText(); + return presentableText.substring(0, presentableText.indexOf(' ')); + } + + /** + * @param api The language level to get the next from. + * @return Next {@link LanguageLevel} that is not in preview. + */ + public static @Nullable LanguageLevel getNextLanguageLevel(@NotNull LanguageLevel api) { + final LanguageLevel[] levels = LanguageLevel.values(); + final int currentLevelId = api.ordinal(); + for (int i = currentLevelId + 1; i < levels.length; i++) { + final LanguageLevel level = levels[i]; + if (!level.isPreview()) return level; + } + return null; + } + + private static final Map ourPresentableShortMessage = new EnumMap<>(LanguageLevel.class); + + static { + ourPresentableShortMessage.put(LanguageLevel.JDK_1_3, "1.4"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_4, "1.5"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_5, "1.6"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_6, "1.7"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_7, "1.8"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_8, "9"); + ourPresentableShortMessage.put(LanguageLevel.JDK_1_9, "10"); + ourPresentableShortMessage.put(LanguageLevel.JDK_10, "11"); + ourPresentableShortMessage.put(LanguageLevel.JDK_11, "12"); + ourPresentableShortMessage.put(LanguageLevel.JDK_12, "13"); + ourPresentableShortMessage.put(LanguageLevel.JDK_13, "14"); + ourPresentableShortMessage.put(LanguageLevel.JDK_14, "15"); + ourPresentableShortMessage.put(LanguageLevel.JDK_15, "16"); + ourPresentableShortMessage.put(LanguageLevel.JDK_16, "17"); + ourPresentableShortMessage.put(LanguageLevel.JDK_16_PREVIEW, "17"); + } + + @Nullable + public static String getShortMessage(@NotNull LanguageLevel languageLevel) { + return ourPresentableShortMessage.get(languageLevel); + } + + @Nullable + private static Set getForbiddenApi(@NotNull LanguageLevel languageLevel) { + if (!ourPresentableShortMessage.containsKey(languageLevel)) return null; + Reference> ref = ourForbiddenAPI.get(languageLevel); + Set result = SoftReference.dereference(ref); + if (result == null) { + result = loadSignatureList(LanguageLevelUtil.class.getResource("api" + getShortMessage(languageLevel) + ".txt")); + ourForbiddenAPI.put(languageLevel, new SoftReference<>(result)); + } + return result; + } + + /** + * @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 + */ + public static LanguageLevel getLastIncompatibleLanguageLevel(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { + if (member instanceof PsiAnonymousClass) return null; + PsiClass containingClass = member.getContainingClass(); + if (containingClass instanceof PsiAnonymousClass) return null; + if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return null; + + Set forbiddenApi = getForbiddenApi(languageLevel); + if (forbiddenApi == null) return null; + String signature = getSignature(member); + if (signature == null) return null; + LanguageLevel lastIncompatibleLanguageLevel = getLastIncompatibleLanguageLevelForSignature(signature, languageLevel, forbiddenApi); + if (lastIncompatibleLanguageLevel != null) return lastIncompatibleLanguageLevel; + return null; + } + + private static LanguageLevel getLastIncompatibleLanguageLevelForSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) { + if (forbiddenApi.contains(signature)) { + return languageLevel; + } + if (languageLevel.compareTo(LanguageLevel.HIGHEST) == 0) { + return null; + } + LanguageLevel nextLanguageLevel = LanguageLevel.values()[languageLevel.ordinal() + 1]; + Set nextForbiddenApi = getForbiddenApi(nextLanguageLevel); + return nextForbiddenApi != null ? getLastIncompatibleLanguageLevelForSignature(signature, nextLanguageLevel, nextForbiddenApi) : null; + } + + public static Set loadSignatureList(URL resource) { + if (resource == null) { + Logger.getInstance(LanguageLevelUtil.class).warn("not found: " + resource.getFile()); + return Collections.emptySet(); + } + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { + return new HashSet<>(FileUtil.loadLines(reader)); + } + catch (IOException ex) { + Logger.getInstance(LanguageLevelUtil.class).warn("cannot load: " + resource.getFile(), ex); + return Collections.emptySet(); + } + } + + /** + * For serialization of forbidden api. + */ + @Nullable + public static String getSignature(@Nullable PsiMember member) { + if (member instanceof PsiClass) { + return ((PsiClass)member).getQualifiedName(); + } + if (member instanceof PsiField) { + String containingClass = getSignature(member.getContainingClass()); + return containingClass == null ? null : containingClass + "#" + member.getName(); + } + if (member instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)member; + String containingClass = getSignature(member.getContainingClass()); + if (containingClass == null) return null; + + StringBuilder buf = new StringBuilder(); + buf.append(containingClass); + buf.append('#'); + buf.append(method.getName()); + buf.append('('); + for (PsiType type : method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes()) { + buf.append(type.getCanonicalText()); + buf.append(";"); + } + buf.append(')'); + return buf.toString(); + } + return null; + } + } diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.4.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api1.4.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.4.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api1.4.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.5.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api1.5.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.5.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api1.5.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.6.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api1.6.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.6.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api1.6.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.7.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api1.7.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.7.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api1.7.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.8.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api1.8.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.8.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api1.8.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api10.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api10.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api10.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api10.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api11.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api11.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api11.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api11.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api12.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api12.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api12.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api12.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api13.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api13.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api13.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api13.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api14.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api14.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api14.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api14.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api15.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api15.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api15.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api15.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api16.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api16.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api16.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api16.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api17.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api17.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api17.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api17.txt diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/api1.9.txt b/java/java-analysis-api/src/com/intellij/openapi/module/api9.txt similarity index 100% rename from java/java-impl/src/com/intellij/codeInspection/java15api/api1.9.txt rename to java/java-analysis-api/src/com/intellij/openapi/module/api9.txt diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/CharsetObjectCanBeUsedInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/CharsetObjectCanBeUsedInspection.java index 570e1fc4968c..28dd29570b20 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/CharsetObjectCanBeUsedInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/CharsetObjectCanBeUsedInspection.java @@ -1,11 +1,11 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInspection; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.impl.quickfix.DeleteCatchFix; import com.intellij.codeInsight.daemon.impl.quickfix.DeleteMultiCatchFix; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; import com.intellij.java.JavaBundle; +import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; @@ -187,7 +187,7 @@ public class CharsetObjectCanBeUsedInspection extends AbstractBaseJavaLocalInspe PsiMethod[] candidates = method.isConstructor() ? aClass.getConstructors() : aClass.findMethodsByName(method.getName(), false); PsiMethod charsetMethod = Arrays.stream(candidates) .filter(psiMethod -> checkMethod(psiMethod, "java.nio.charset.Charset")) - .filter(psiMethod -> Java15APIUsageInspection.getLastIncompatibleLanguageLevel(psiMethod, languageLevel) == null) + .filter(psiMethod -> LanguageLevelUtil.getLastIncompatibleLanguageLevel(psiMethod, languageLevel) == null) .findFirst().orElse(null); if (charsetMethod == null) return null; return new CharsetMatch(argument, method, charsetMethod); diff --git a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java index aa8583440a60..988d9e997b1f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java +++ b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java @@ -1,11 +1,11 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInsight; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; import com.intellij.java.JavaBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -151,7 +151,7 @@ public final class FunctionalInterfaceSuggester { @Override public boolean processInReadAction(PsiMember member) { if (member instanceof PsiClass && - Java15APIUsageInspection.getLastIncompatibleLanguageLevel(member, PsiUtil.getLanguageLevel(element)) == null) { + LanguageLevelUtil.getLastIncompatibleLanguageLevel(member, PsiUtil.getLanguageLevel(element)) == null) { if (!JavaPsiFacade.getInstance(project).getResolveHelper().isAccessible(member, element, null)) { return true; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 001a5aff8aca..607faffdabc9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -11,13 +11,13 @@ import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil; import com.intellij.codeInsight.editorActions.TabOutScopesTracker; import com.intellij.codeInsight.guess.GuessManager; import com.intellij.codeInsight.lookup.*; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; @@ -503,7 +503,7 @@ public final class JavaCompletionUtil { private boolean shouldMarkRed(@NotNull Object object) { if (!(object instanceof PsiMember)) return false; - if (Java15APIUsageInspection.getLastIncompatibleLanguageLevel((PsiMember)object, myLanguageLevel) != null) { + if (LanguageLevelUtil.getLastIncompatibleLanguageLevel((PsiMember)object, myLanguageLevel) != null) { return true; } diff --git a/java/java-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspection.java b/java/java-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspection.java index 5fedd79b8e51..3bef3be12a0c 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspection.java @@ -19,49 +19,25 @@ import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.reference.SoftReference; import com.intellij.ui.SimpleListCellRenderer; import com.intellij.util.ObjectUtils; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.lang.ref.Reference; -import java.net.URL; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.*; -/** - * In order to add the support for new API in the most recent JDK execute: - *
    - *
  1. Generate apiXXX.txt by uncommenting the body of {@link com.intellij.java.codeInspection.JavaAPIUsagesInspectionTest#testCollectSinceApiUsages} and run it;
  2. - *
  3. Add two new entries to the {@link Java15APIUsageInspection#ourHighestKnownLanguage}: - *
      - *
    • The First entry: The key is the most recent language level, the value is the second to the most recent language level.
    • - *
    • The Second entry: The key is the most recent preview language level, the value is the second to the most recent language level.
    • - *
    - *
  4. - *
  5. Reassign the value of {@link Java15APIUsageInspection#ourHighestKnownLanguage} to the most recent language level
  6. - *
- * To - * @author max - */ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionTool { private static final Logger LOGGER = Logger.getInstance(Java15APIUsageInspection.class.getName()); @@ -69,12 +45,11 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo private static final String EFFECTIVE_LL = "effectiveLL"; - private static final Map>> ourForbiddenAPI = new EnumMap<>(LanguageLevel.class); - private static final Set ourIgnored16ClassesAPI = loadForbiddenApi("ignore16List.txt"); + private static final Set ourIgnored16ClassesAPI = LanguageLevelUtil.loadSignatureList( + Java15APIUsageInspection.class.getResource("ignore6List.txt") + ); private static final Map ourPresentableShortMessage = new EnumMap<>(LanguageLevel.class); - private static final LanguageLevel ourHighestKnownLanguage = LanguageLevel.JDK_17; - static { ourPresentableShortMessage.put(LanguageLevel.JDK_1_3, "1.4"); ourPresentableShortMessage.put(LanguageLevel.JDK_1_4, "1.5"); @@ -154,34 +129,6 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo return panel; } - @Nullable - private static Set getForbiddenApi(@NotNull LanguageLevel languageLevel) { - if (!ourPresentableShortMessage.containsKey(languageLevel)) return null; - Reference> ref = ourForbiddenAPI.get(languageLevel); - Set result = SoftReference.dereference(ref); - if (result == null) { - result = loadForbiddenApi("api" + getShortName(languageLevel) + ".txt"); - ourForbiddenAPI.put(languageLevel, new SoftReference<>(result)); - } - return result; - } - - private static Set loadForbiddenApi(String fileName) { - URL resource = Java15APIUsageInspection.class.getResource(fileName); - if (resource == null) { - Logger.getInstance(Java15APIUsageInspection.class).warn("not found: " + fileName); - return Collections.emptySet(); - } - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream(), StandardCharsets.UTF_8))) { - return new HashSet<>(FileUtil.loadLines(reader)); - } - catch (IOException ex) { - Logger.getInstance(Java15APIUsageInspection.class).warn("cannot load: " + fileName, ex); - return Collections.emptySet(); - } - } - @Override @NotNull public String getGroupDisplayName() { @@ -256,7 +203,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo final List methods = new ArrayList<>(); for (HierarchicalMethodSignature methodSignature : aClass.getVisibleSignatures()) { final PsiMethod method = methodSignature.getMethod(); - if (ourDefaultMethods.contains(getSignature(method))) { + if (ourDefaultMethods.contains(LanguageLevelUtil.getSignature(method))) { methods.add(method); } } @@ -267,8 +214,8 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo element2Highlight = aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassReference() : aClass; } myHolder.registerProblem(element2Highlight, - methods.size() == 1 ? JavaBundle.message("inspection.1.8.problem.single.descriptor", methods.get(0).getName(), getJdkName(effectiveLanguageLevel)) - : JavaBundle.message("inspection.1.8.problem.descriptor", methods.size(), getJdkName(effectiveLanguageLevel)), + methods.size() == 1 ? JavaBundle.message("inspection.1.8.problem.single.descriptor", methods.get(0).getName(), LanguageLevelUtil.getJdkName(effectiveLanguageLevel)) + : JavaBundle.message("inspection.1.8.problem.descriptor", methods.size(), LanguageLevelUtil.getJdkName(effectiveLanguageLevel)), QuickFixFactory.getInstance().createImplementMethodsFix(aClass)); } } @@ -290,7 +237,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo final Module module = ModuleUtilCore.findModuleForPsiElement(pair); if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); - LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel((PsiMember)resolve, languageLevel); + LanguageLevel sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel((PsiMember)resolve, languageLevel); if (sinceLanguageLevel != null) { registerError(ObjectUtils.notNull(pair.getNameIdentifier(), pair), sinceLanguageLevel); } @@ -307,7 +254,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo final Module module = ModuleUtilCore.findModuleForPsiElement(reference.getElement()); if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); - LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel((PsiMember)resolved, languageLevel); + LanguageLevel sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel((PsiMember)resolved, languageLevel); if (sinceLanguageLevel != null) { PsiClass psiClass = null; final PsiElement qualifier = reference.getQualifier(); @@ -332,7 +279,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo for (String generifiedClass : ourGenerifiedClasses) { if (InheritanceUtil.isInheritor((PsiClass)resolved, generifiedClass) && !isRawInheritance(generifiedClass, (PsiClass)resolved, new HashSet<>())) { - String message = JavaBundle.message("inspection.1.7.problem.descriptor", getJdkName(languageLevel)); + String message = JavaBundle.message("inspection.1.7.problem.descriptor", LanguageLevelUtil.getJdkName(languageLevel)); myHolder.registerProblem(reference, message); break; } @@ -371,7 +318,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); if (constructor instanceof PsiCompiledElement) { - LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel(constructor, languageLevel); + LanguageLevel sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(constructor, languageLevel); if (sinceLanguageLevel != null) { registerError(expression.getClassReference(), sinceLanguageLevel); } @@ -391,7 +338,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo final PsiMethod[] methods = method.findSuperMethods(); for (PsiMethod superMethod : methods) { if (superMethod instanceof PsiCompiledElement) { - sinceLanguageLevel = getLastIncompatibleLanguageLevel(superMethod, languageLevel); + sinceLanguageLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(superMethod, languageLevel); if (sinceLanguageLevel == null) { return; } @@ -415,7 +362,7 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo private void registerError(PsiElement reference, LanguageLevel api) { if (reference == null || !isInProject(reference)) return; - final LanguageLevel targetLanguageLevel = getNextLanguageLevel(api); + final LanguageLevel targetLanguageLevel = LanguageLevelUtil.getNextLanguageLevel(api); if (targetLanguageLevel == null) { LOGGER.error("Unable to get the next language level for " + api); @@ -436,81 +383,4 @@ public class Java15APIUsageInspection extends AbstractBaseJavaLocalInspectionToo } } - - private static @Nullable LanguageLevel getNextLanguageLevel(@NotNull LanguageLevel api) { - final LanguageLevel[] levels = LanguageLevel.values(); - final int currentLevelId = api.ordinal(); - for (int i = currentLevelId + 1; i < levels.length; i++) { - final LanguageLevel level = levels[i]; - if (!level.isPreview()) { - return level; - } - } - return null; - } - - private static String getJdkName(LanguageLevel languageLevel) { - final String presentableText = languageLevel.getPresentableText(); - return presentableText.substring(0, presentableText.indexOf(' ')); - } - - public static LanguageLevel getLastIncompatibleLanguageLevel(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { - if (member instanceof PsiAnonymousClass) return null; - PsiClass containingClass = member.getContainingClass(); - if (containingClass instanceof PsiAnonymousClass) return null; - if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return null; - - Set forbiddenApi = getForbiddenApi(languageLevel); - String signature = getSignature(member); - if (forbiddenApi != null && signature != null) { - LanguageLevel lastIncompatibleLanguageLevel = getLastIncompatibleLanguageLevelForSignature(signature, languageLevel, forbiddenApi); - if (lastIncompatibleLanguageLevel != null) return lastIncompatibleLanguageLevel; - } - return containingClass != null ? getLastIncompatibleLanguageLevel(containingClass, languageLevel) : null; - - } - - private static LanguageLevel getLastIncompatibleLanguageLevelForSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) { - if (forbiddenApi.contains(signature)) { - return languageLevel; - } - if (languageLevel.compareTo(ourHighestKnownLanguage) == 0) { - return null; - } - LanguageLevel nextLanguageLevel = LanguageLevel.values()[languageLevel.ordinal() + 1]; - Set nextForbiddenApi = getForbiddenApi(nextLanguageLevel); - return nextForbiddenApi != null ? getLastIncompatibleLanguageLevelForSignature(signature, nextLanguageLevel, nextForbiddenApi) : null; - } - - /** - * please leave public for JavaAPIUsagesInspectionTest#testCollectSinceApiUsages - */ - @Nullable - public static String getSignature(@Nullable PsiMember member) { - if (member instanceof PsiClass) { - return ((PsiClass)member).getQualifiedName(); - } - if (member instanceof PsiField) { - String containingClass = getSignature(member.getContainingClass()); - return containingClass == null ? null : containingClass + "#" + member.getName(); - } - if (member instanceof PsiMethod) { - final PsiMethod method = (PsiMethod)member; - String containingClass = getSignature(member.getContainingClass()); - if (containingClass == null) return null; - - StringBuilder buf = new StringBuilder(); - buf.append(containingClass); - buf.append('#'); - buf.append(method.getName()); - buf.append('('); - for (PsiType type : method.getSignature(PsiSubstitutor.EMPTY).getParameterTypes()) { - buf.append(type.getCanonicalText()); - buf.append(";"); - } - buf.append(')'); - return buf.toString(); - } - return null; - } } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/inheritance/MissingOverrideAnnotationInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/inheritance/MissingOverrideAnnotationInspection.java index ebeffc5f5750..df405dc745e5 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/inheritance/MissingOverrideAnnotationInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/inheritance/MissingOverrideAnnotationInspection.java @@ -1,9 +1,8 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.siyeh.ig.inheritance; import com.intellij.codeInsight.intention.AddAnnotationPsiFix; import com.intellij.codeInspection.*; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.module.Module; @@ -235,7 +234,7 @@ public class MissingOverrideAnnotationInspection extends AbstractBaseJavaLocalIn @Contract("_, _, _,null -> true") private boolean ignoreSuperMethod(PsiMethod method, PsiClass methodClass, PsiMethod superMethod, PsiClass superClass) { return !InheritanceUtil.isInheritorOrSelf(methodClass, superClass, true) || - Java15APIUsageInspection.getLastIncompatibleLanguageLevel(superMethod, PsiUtil.getLanguageLevel(method)) != null; + LanguageLevelUtil.getLastIncompatibleLanguageLevel(superMethod, PsiUtil.getLanguageLevel(method)) != null; } }; } diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/internationalization/ImplicitDefaultCharsetUsageInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/internationalization/ImplicitDefaultCharsetUsageInspection.java index 674156b16b00..f8e139382901 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/internationalization/ImplicitDefaultCharsetUsageInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/internationalization/ImplicitDefaultCharsetUsageInspection.java @@ -1,8 +1,8 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.siyeh.ig.internationalization; import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; +import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.pom.java.LanguageLevel; @@ -59,7 +59,7 @@ public class ImplicitDefaultCharsetUsageInspection extends BaseInspection { } InspectionGadgetsFix createFix(LanguageLevel level) { - return myMethod == null || Java15APIUsageInspection.getLastIncompatibleLanguageLevel(myMethod, level) != null + return myMethod == null || LanguageLevelUtil.getLastIncompatibleLanguageLevel(myMethod, level) != null ? null : new AddUtf8CharsetFix(this); } diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/inspections/Java15FormInspection.java b/plugins/ui-designer/src/com/intellij/uiDesigner/inspections/Java15FormInspection.java index 4443721cd7fd..920c0e58bc26 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/inspections/Java15FormInspection.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/inspections/Java15FormInspection.java @@ -1,7 +1,6 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.uiDesigner.inspections; -import com.intellij.codeInspection.java15api.Java15APIUsageInspection; import com.intellij.java.JavaBundle; import com.intellij.openapi.module.LanguageLevelUtil; import com.intellij.openapi.module.Module; @@ -45,8 +44,8 @@ public class Java15FormInspection extends BaseFormInspection { final PsiMethod getter = PropertyUtilBase.findPropertyGetter(aClass, prop.getName(), false, true); if (getter == null) continue; final LanguageLevel languageLevel = LanguageLevelUtil.getEffectiveLanguageLevel(module); - if (Java15APIUsageInspection.getLastIncompatibleLanguageLevel(getter, languageLevel) != null) { - registerError(component, collector, prop, "@since " + Java15APIUsageInspection.getShortName(languageLevel)); + if (LanguageLevelUtil.getLastIncompatibleLanguageLevel(getter, languageLevel) != null) { + registerError(component, collector, prop, "@since " + LanguageLevelUtil.getJdkName(languageLevel)); } } }