diff --git a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties index a0e1336ab624..e33e2cbda309 100644 --- a/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties +++ b/java/java-analysis-api/resources/messages/JavaAnalysisBundle.properties @@ -106,6 +106,7 @@ feature.static.interface.calls=Static interface method calls feature.try.with.resources.refs=Resource references feature.modules=Modules feature.lvti=Local variable type inference +feature.var.lambda.parameter='var' in lambda parameters feature.text.blocks=Text block literals feature.enhanced.switch=Enhanced 'switch' blocks feature.switch.expressions='switch' expressions diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index 341eb088ce95..ec985ff4a6e9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -7,6 +7,7 @@ import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.JavaModuleSystemEx; import com.intellij.codeInsight.JavaModuleSystemEx.ErrorWithFixes; import com.intellij.codeInsight.daemon.JavaErrorBundle; +import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.HighlightInfoType; import com.intellij.codeInsight.daemon.impl.quickfix.*; @@ -3239,14 +3240,21 @@ public class HighlightUtil { if (file.getManager().isInProject(file) && !feature.isSufficient(level)) { String message = getUnsupportedFeatureMessage(element, feature, level, file); HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(message).create(); - QuickFixAction.registerQuickFixAction(info, getFixFactory().createIncreaseLanguageLevelFix(getApplicableLevel(file, feature))); - QuickFixAction.registerQuickFixAction(info, getFixFactory().createShowModulePropertiesFix(element)); + registerIncreaseLanguageLevelFixes(new QuickFixActionRegistrarImpl(info), element, feature); return info; } return null; } + public static void registerIncreaseLanguageLevelFixes(@NotNull QuickFixActionRegistrar registrar, + @NotNull PsiElement element, + @NotNull HighlightingFeature feature) { + if (feature.isAvailable(element)) return; + registrar.register(getFixFactory().createIncreaseLanguageLevelFix(getApplicableLevel(element.getContainingFile(), feature))); + registrar.register(getFixFactory().createShowModulePropertiesFix(element)); + } + private static @NotNull String getUnsupportedFeatureMessage(@NotNull PsiElement element, @NotNull HighlightingFeature feature, @NotNull LanguageLevel level, diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java index 9c5cff732be1..6b7e57815c76 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java @@ -31,6 +31,7 @@ public enum HighlightingFeature { REFS_AS_RESOURCE(LanguageLevel.JDK_1_9, "feature.try.with.resources.refs"), MODULES(LanguageLevel.JDK_1_9, "feature.modules"), LVTI(LanguageLevel.JDK_10, "feature.lvti"), + VAR_LAMBDA_PARAMETER(LanguageLevel.JDK_11, "feature.var.lambda.parameter"), ENHANCED_SWITCH(LanguageLevel.JDK_13_PREVIEW, "feature.enhanced.switch"){ @Override boolean isSufficient(@NotNull LanguageLevel useSiteLevel) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorQuickFixProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorQuickFixProvider.java index ae7146918df9..6c184043f0a2 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorQuickFixProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorQuickFixProvider.java @@ -5,12 +5,10 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.quickfix.AddExceptionToCatchFix; import com.intellij.codeInsight.daemon.impl.quickfix.AddFinallyFix; import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction; +import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.core.JavaPsiBundle; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiErrorElement; -import com.intellij.psi.PsiSwitchLabeledRuleStatement; -import com.intellij.psi.PsiTryStatement; +import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; public class JavaErrorQuickFixProvider implements ErrorQuickFixProvider { @@ -29,5 +27,13 @@ public class JavaErrorQuickFixProvider implements ErrorQuickFixProvider { QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createWrapSwitchRuleStatementsIntoBlockFix((PsiSwitchLabeledRuleStatement)parent)); } + if (parent instanceof PsiJavaFile && errorElement.getErrorDescription().equals( + JavaPsiBundle.message("expected.class.or.interface"))) { + PsiElement child = errorElement.getFirstChild(); + if (child instanceof PsiIdentifier && child.textMatches(PsiKeyword.RECORD)) { + HighlightUtil.registerIncreaseLanguageLevelFixes( + new QuickFixActionRegistrarImpl(highlightInfo), errorElement, HighlightingFeature.RECORDS); + } + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaFutureKeywordUseFixProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaFutureKeywordUseFixProvider.java new file mode 100644 index 000000000000..ca5e08ac8cbc --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaFutureKeywordUseFixProvider.java @@ -0,0 +1,57 @@ +// 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. +package com.intellij.codeInsight.daemon.impl.analysis; + +import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; +import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; +import com.intellij.psi.*; +import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.NotNull; + +public class JavaFutureKeywordUseFixProvider extends UnresolvedReferenceQuickFixProvider { + @Override + public void registerFixes(@NotNull PsiJavaCodeReferenceElement ref, @NotNull QuickFixActionRegistrar registrar) { + PsiTypeElement typeElement = ObjectUtils.tryCast(ref.getParent(), PsiTypeElement.class); + if (typeElement == null || typeElement.getFirstChild() != typeElement.getLastChild()) return; + PsiElement parent = typeElement.getParent(); + if (PsiKeyword.VAR.equals(ref.getReferenceName())) { + registerVarLanguageLevelFix(ref, parent, registrar); + } + if (PsiKeyword.RECORD.equals(ref.getReferenceName())) { + registerRecordLanguageLevelFix(ref, parent, registrar); + } + } + + private static void registerRecordLanguageLevelFix(@NotNull PsiJavaCodeReferenceElement ref, + PsiElement parent, + @NotNull QuickFixActionRegistrar registrar) { + if ((parent instanceof PsiMethod || parent instanceof PsiField) && parent.getParent() instanceof PsiClass) { + // record R() {} is parsed as method if records aren't supported + // record R incomplete declaration is also possible + HighlightUtil.registerIncreaseLanguageLevelFixes(registrar, ref, HighlightingFeature.RECORDS); + } + if (parent instanceof PsiLocalVariable && parent.getParent() instanceof PsiDeclarationStatement + && ((PsiDeclarationStatement)parent.getParent()).getDeclaredElements().length == 1) { + // record R() declaration inside method + HighlightUtil.registerIncreaseLanguageLevelFixes(registrar, ref, HighlightingFeature.RECORDS); + } + } + + private static void registerVarLanguageLevelFix(@NotNull PsiJavaCodeReferenceElement ref, + PsiElement parent, + @NotNull QuickFixActionRegistrar registrar) { + HighlightingFeature feature; + if (parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiLambdaExpression) { + feature = HighlightingFeature.VAR_LAMBDA_PARAMETER; + } + else { + feature = HighlightingFeature.LVTI; + } + HighlightUtil.registerIncreaseLanguageLevelFixes(registrar, ref, feature); + } + + @NotNull + @Override + public Class getReferenceClass() { + return PsiJavaCodeReferenceElement.class; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LocalVariableTypeInferenceUnresolvedFixProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LocalVariableTypeInferenceUnresolvedFixProvider.java deleted file mode 100644 index c7b2c02cc27c..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/LocalVariableTypeInferenceUnresolvedFixProvider.java +++ /dev/null @@ -1,53 +0,0 @@ -// 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. -package com.intellij.codeInsight.daemon.impl.analysis; - -import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; -import com.intellij.codeInsight.intention.QuickFixFactory; -import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; -import com.intellij.pom.java.AcceptedLanguageLevelsSettings; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; -import org.jetbrains.annotations.NotNull; - -public class LocalVariableTypeInferenceUnresolvedFixProvider extends UnresolvedReferenceQuickFixProvider { - @Override - public void registerFixes(@NotNull PsiJavaCodeReferenceElement ref, @NotNull QuickFixActionRegistrar registrar) { - PsiElement typeElement = ref.getParent(); - PsiElement parent = typeElement instanceof PsiTypeElement ? typeElement.getParent() : null; - boolean increaseLanguageLevel = true; - LanguageLevel targetLanguageLevel; - if (parent instanceof PsiParameter && ((PsiParameter)parent).getDeclarationScope() instanceof PsiLambdaExpression) { - //early-draft specification support to be enabled after release - if (LanguageLevel.HIGHEST.isAtLeast(LanguageLevel.JDK_11)) { - targetLanguageLevel = LanguageLevel.JDK_11; - } - else if (AcceptedLanguageLevelsSettings.isLanguageLevelAccepted(LanguageLevel.JDK_11)) { - targetLanguageLevel = LanguageLevel.JDK_11; - - //show module options with ability to explicitly agree with legal notice - increaseLanguageLevel = false; - } - else { - return; - } - } - else { - targetLanguageLevel = LanguageLevel.JDK_10; - } - - if (PsiUtil.getLanguageLevel(ref).isAtLeast(targetLanguageLevel)) return; - if (!PsiKeyword.VAR.equals(ref.getReferenceName())) return; - - if (increaseLanguageLevel) { - registrar.register(QuickFixFactory.getInstance().createIncreaseLanguageLevelFix(targetLanguageLevel)); - } - registrar.register(QuickFixFactory.getInstance().createShowModulePropertiesFix(ref)); - } - - @NotNull - @Override - public Class getReferenceClass() { - return PsiJavaCodeReferenceElement.class; - } -} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInClass.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInClass.java new file mode 100644 index 000000000000..7ef0f11bae5d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInClass.java @@ -0,0 +1,3 @@ +class X { + record R() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInMethod.java new file mode 100644 index 000000000000..12410fb05868 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordInMethod.java @@ -0,0 +1,5 @@ +class X { + void test() { + record R() {} + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordTopLevel.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordTopLevel.java new file mode 100644 index 000000000000..a39a5ae71dfc --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/RecordTopLevel.java @@ -0,0 +1 @@ +record R() {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLambda.java new file mode 100644 index 000000000000..c4d159021090 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLambda.java @@ -0,0 +1,9 @@ +class X { + interface Fn { + void test(String s); + } + + void test() { + Fn fn = (var s) -> System.out.println(s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLocal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLocal.java new file mode 100644 index 000000000000..f098c98827d1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/VarLocal.java @@ -0,0 +1,5 @@ +class X { + void test() { + var x = 5; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/IncreaseLanguageLevelFixTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/IncreaseLanguageLevelFixTest.java new file mode 100644 index 000000000000..993fbdff1c32 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/IncreaseLanguageLevelFixTest.java @@ -0,0 +1,72 @@ +// 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. +package com.intellij.java.codeInsight.daemon; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.java.JavaBundle; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; +import com.intellij.util.lang.JavaVersion; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Stream; + +public class IncreaseLanguageLevelFixTest extends LightDaemonAnalyzerTestCase { + @Override + protected @NotNull String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/increaseLanguageLevel/"; + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk(JavaVersion.compose(14)); + } + + @Override + protected LanguageLevel getLanguageLevel() { + return LanguageLevel.JDK_1_8; + } + + public void testVarLocal() { + doTest(LanguageLevel.JDK_10); + } + + public void testVarLambda() { + doTest(LanguageLevel.JDK_11); + } + + public void testRecordTopLevel() { + doTest(LanguageLevel.JDK_14_PREVIEW); + } + + public void testRecordInClass() { + doTest(LanguageLevel.JDK_14_PREVIEW); + } + + public void testRecordInMethod() { + doTest(LanguageLevel.JDK_14_PREVIEW); + } + + private void doTest(LanguageLevel level) { + configureByFile(getTestName(false) + ".java"); + doHighlighting(); + List actions = CodeInsightTestFixtureImpl.getAvailableIntentions(getEditor(), getFile()); + String message = JavaBundle.message("set.language.level.to.0", level.getPresentableText()); + boolean found = actions.stream().anyMatch(act -> act.getText().equals(message)); + if (!found) { + LanguageLevel foundLevel = Stream.of(LanguageLevel.values()) + .filter(l -> actions.stream().anyMatch( + act -> act.getText().equals(JavaBundle.message("set.language.level.to.0", l.getPresentableText())))) + .findFirst().orElse(null); + if (foundLevel != null) { + fail("Expected level: "+level+"; actual: "+foundLevel); + } else { + fail("Action " + message + " not found"); + } + } + } +} diff --git a/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml index 5bbab2d07c10..31799d9f615d 100644 --- a/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/src/META-INF/InspectionGadgets.xml @@ -2808,7 +2808,7 @@ bundle="messages.InspectionGadgetsBundle" key="inspection.constant.expression.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.code.style.issues" implementationClass="com.siyeh.ig.style.ConstantExpressionInspection"/> - +