From bbfc04bc96357e4f39db7cd77758e9cf9cff3c24 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 13 Jan 2025 09:40:08 +0100 Subject: [PATCH] [java-highlighting] checkStaticDeclarationInInnerClass -> ClassChecker Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only) GitOrigin-RevId: 24c404c0e96e7c6b4127c76bb411814dcaef83e9 --- .../codeserver/highlighting/ClassChecker.java | 47 +++++++ .../highlighting/JavaErrorVisitor.java | 6 + .../impl/analysis/HighlightClassUtil.java | 122 +----------------- .../impl/analysis/HighlightVisitorImpl.java | 1 - .../impl/analysis/JavaErrorFixProvider.java | 14 ++ .../advHighlighting/StaticInInner.java | 6 +- .../genericsHighlighting/Enum.java | 4 +- .../genericsHighlighting8/Enum.java | 4 +- .../UnnecessaryParenthesesInspection.java | 4 +- 9 files changed, 80 insertions(+), 128 deletions(-) diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java index d26a6c8617b9..4f14a622ef77 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/ClassChecker.java @@ -465,6 +465,53 @@ final class ClassChecker { } } + void checkStaticDeclarationInInnerClass(@NotNull PsiKeyword keyword) { + if (!myVisitor.hasErrorResults()) checkStaticClassDeclarationInInnerClass(keyword); + if (!myVisitor.hasErrorResults()) checkStaticMemberInInnerClass(keyword); + } + + private void checkStaticClassDeclarationInInnerClass(@NotNull PsiKeyword keyword) { + // keyword points to 'class' or 'interface' or 'enum'; interface and enum are implicitly static + if (!(keyword.getParent() instanceof PsiClass curClass)) return; + if (!curClass.hasModifierProperty(PsiModifier.STATIC) || PsiUtilCore.hasErrorElementChild(curClass)) return; + if (!(curClass.getParent() instanceof PsiClass parentClass)) return; + if (parentClass.hasModifierProperty(PsiModifier.STATIC)) return; + PsiElement parent = parentClass.getParent(); + if (!(parent instanceof PsiClass) && !(parent instanceof PsiDeclarationStatement) && + !(parent instanceof PsiNewExpression) && !(parent instanceof PsiEnumConstant)) { + return; + } + // highlight 'static' keyword if any, or class or interface if not + PsiElement context = keyword; + PsiModifierList modifierList = curClass.getModifierList(); + if (modifierList != null) { + for (PsiElement element = modifierList.getFirstChild(); element != null; element = element.getNextSibling()) { + if (Objects.equals(element.getText(), PsiModifier.STATIC)) { + context = element; + break; + } + } + } + myVisitor.checkFeature(context, JavaFeature.INNER_STATICS); + } + + private void checkStaticMemberInInnerClass(@NotNull PsiKeyword keyword) { + if (!keyword.getTokenType().equals(JavaTokenType.STATIC_KEYWORD)) return; + if (!(keyword.getParent() instanceof PsiModifierList modifierList)) return; + if (!(modifierList.getParent() instanceof PsiMember member)) return; + if (member instanceof PsiClass) return; // checked separately + if (PsiUtilCore.hasErrorElementChild(member)) return; + if (member instanceof PsiField field && PsiUtil.isCompileTimeConstant(field)) return; + if (!(member.getParent() instanceof PsiClass psiClass)) return; + if (psiClass.hasModifierProperty(PsiModifier.STATIC)) return; + PsiElement classParent = psiClass.getParent(); + if (!(classParent instanceof PsiClass) && !(classParent instanceof PsiDeclarationStatement) && + !(classParent instanceof PsiNewExpression) && !(classParent instanceof PsiEnumConstant)) { + return; + } + myVisitor.checkFeature(keyword, JavaFeature.INNER_STATICS); + } + private static @Unmodifiable @NotNull Map getPermittedClassesRefs(@NotNull PsiClass psiClass) { PsiReferenceList permitsList = psiClass.getPermitsList(); if (permitsList == null) return Collections.emptyMap(); diff --git a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java index c60b14d6d8f8..f76cbc9eb7c3 100644 --- a/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java +++ b/java/codeserver/highlighting/src/com/intellij/java/codeserver/highlighting/JavaErrorVisitor.java @@ -210,6 +210,12 @@ final class JavaErrorVisitor extends JavaElementVisitor { } } + @Override + public void visitKeyword(@NotNull PsiKeyword keyword) { + super.visitKeyword(keyword); + if (!hasErrorResults()) myClassChecker.checkStaticDeclarationInInnerClass(keyword); + } + @Override public void visitClass(@NotNull PsiClass aClass) { super.visitClass(aClass); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index bf0b71c49ae4..cd9b2623479b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -31,7 +31,10 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.function.Consumer; /** @@ -48,28 +51,6 @@ public final class HighlightClassUtil { return PsiTypesUtil.isRestrictedIdentifier(typeName, level); } - private static HighlightInfo.Builder checkStaticFieldDeclarationInInnerClass(@NotNull PsiKeyword keyword) { - if (getEnclosingStaticClass(keyword, PsiField.class) == null) { - return null; - } - - PsiField field = (PsiField)keyword.getParent().getParent(); - if (PsiUtilCore.hasErrorElementChild(field) || PsiUtil.isCompileTimeConstant(field)) { - return null; - } - - HighlightInfo.Builder result = HighlightUtil.checkFeature(keyword, JavaFeature.INNER_STATICS, - PsiUtil.getLanguageLevel(field), field.getContainingFile()); - - IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(field, PsiModifier.STATIC, false, false); - if (result != null) { - result.registerFix(action, null, null, null, null); - } - registerMakeInnerClassStatic(field.getContainingClass(), result); - - return result; - } - private static void registerMakeInnerClassStatic(@Nullable PsiClass aClass, @Nullable HighlightInfo.Builder result) { if (aClass != null && aClass.getContainingClass() != null) { IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(aClass, PsiModifier.STATIC, true, false); @@ -79,101 +60,6 @@ public final class HighlightClassUtil { } } - private static HighlightInfo.Builder checkStaticMethodDeclarationInInnerClass(@NotNull PsiKeyword keyword) { - if (getEnclosingStaticClass(keyword, PsiMethod.class) == null) { - return null; - } - PsiMethod method = (PsiMethod)keyword.getParent().getParent(); - if (PsiUtilCore.hasErrorElementChild(method)) return null; - HighlightInfo.Builder result = HighlightUtil.checkFeature(keyword, JavaFeature.INNER_STATICS, - PsiUtil.getLanguageLevel(method), method.getContainingFile()); - IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(method, PsiModifier.STATIC, false, false); - if (result != null) { - result.registerFix(action, null, null, null, null); - } - registerMakeInnerClassStatic((PsiClass)method.getParent(), result); - return result; - } - - private static HighlightInfo.Builder checkStaticInitializerDeclarationInInnerClass(@NotNull PsiKeyword keyword) { - if (getEnclosingStaticClass(keyword, PsiClassInitializer.class) == null) { - return null; - } - PsiClassInitializer initializer = (PsiClassInitializer)keyword.getParent().getParent(); - if (PsiUtilCore.hasErrorElementChild(initializer)) return null; - HighlightInfo.Builder result = HighlightUtil.checkFeature(keyword, JavaFeature.INNER_STATICS, - PsiUtil.getLanguageLevel(initializer), initializer.getContainingFile()); - IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(initializer, PsiModifier.STATIC, false, false); - if (result != null) { - result.registerFix(action, null, null, null, null); - } - registerMakeInnerClassStatic((PsiClass)keyword.getParent().getParent().getParent(), result); - return result; - } - - private static PsiElement getEnclosingStaticClass(@NotNull PsiKeyword keyword, @NotNull Class parentClass) { - return new PsiMatcherImpl(keyword) - .dot(PsiMatchers.hasText(PsiModifier.STATIC)) - .parent(PsiMatchers.hasClass(PsiModifierList.class)) - .parent(PsiMatchers.hasClass(parentClass)) - .parent(PsiMatchers.hasClass(PsiClass.class)) - .dot(JavaMatchers.hasModifier(PsiModifier.STATIC, false)) - .parent(PsiMatchers.hasClass(PsiClass.class, PsiDeclarationStatement.class, PsiNewExpression.class, PsiEnumConstant.class)) - .getElement(); - } - - private static HighlightInfo.Builder checkStaticClassDeclarationInInnerClass(@NotNull PsiKeyword keyword) { - // keyword points to 'class' or 'interface' or 'enum' - if (new PsiMatcherImpl(keyword) - .parent(PsiMatchers.hasClass(PsiClass.class)) - .dot(JavaMatchers.hasModifier(PsiModifier.STATIC, true)) - .parent(PsiMatchers.hasClass(PsiClass.class)) - .dot(JavaMatchers.hasModifier(PsiModifier.STATIC, false)) - .parent(PsiMatchers.hasClass(PsiClass.class, PsiDeclarationStatement.class, PsiNewExpression.class, PsiEnumConstant.class)) - .getElement() == null) { - return null; - } - - PsiClass aClass = (PsiClass)keyword.getParent(); - if (PsiUtilCore.hasErrorElementChild(aClass)) { - return null; - } - - // highlight 'static' keyword if any, or class or interface if not - PsiElement context = null; - PsiModifierList modifierList = aClass.getModifierList(); - if (modifierList != null) { - for (PsiElement element = modifierList.getFirstChild(); element != null; element = element.getNextSibling()) { - if (Objects.equals(element.getText(), PsiModifier.STATIC)) { - context = element; - break; - } - } - } - - TextRange range = context == null ? HighlightNamesUtil.getClassDeclarationTextRange(aClass) : context.getTextRange(); - HighlightInfo.Builder info = HighlightUtil.checkFeature(range, JavaFeature.INNER_STATICS, - PsiUtil.getLanguageLevel(aClass), aClass.getContainingFile()); - if (context != keyword) { - QuickFixAction.registerQuickFixActions(info, null, JvmElementActionFactories - .createModifierActions(aClass, MemberRequestsKt.modifierRequest(JvmModifier.STATIC, false))); - } - PsiClass containingClass = aClass.getContainingClass(); - registerMakeInnerClassStatic(containingClass, info); - return info; - } - - static HighlightInfo.Builder checkStaticDeclarationInInnerClass(@NotNull PsiKeyword keyword) { - HighlightInfo.Builder errorResult = checkStaticFieldDeclarationInInnerClass(keyword); - if (errorResult != null) return errorResult; - errorResult = checkStaticMethodDeclarationInInnerClass(keyword); - if (errorResult != null) return errorResult; - errorResult = checkStaticClassDeclarationInInnerClass(keyword); - if (errorResult != null) return errorResult; - errorResult = checkStaticInitializerDeclarationInInnerClass(keyword); - return errorResult; - } - static HighlightInfo.Builder checkExtendsAllowed(@NotNull PsiReferenceList list) { if (list.getParent() instanceof PsiClass aClass && (aClass.isEnum() || aClass.isRecord())) { boolean isExtends = list.equals(aClass.getExtendsList()); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index c128319145f5..261a3d86e18e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -764,7 +764,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(keyword).descriptionAndTooltip(description)); } } - if (!hasErrorResults()) add(HighlightClassUtil.checkStaticDeclarationInInnerClass(keyword)); if (!hasErrorResults()) add(HighlightUtil.checkIllegalVoidType(keyword)); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java index f5f767208d3e..3e655288483a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaErrorFixProvider.java @@ -172,6 +172,20 @@ final class JavaErrorFixProvider { boolean hasClassToRelocate = PsiTreeUtil.findChildOfType(implicitClass, PsiClass.class) != null; return hasClassToRelocate ? new MoveMembersIntoClassFix(implicitClass) : null; }); + multi(UNSUPPORTED_FEATURE, error -> { + if (error.context() != JavaFeature.INNER_STATICS) return List.of(); + PsiMember member = PsiTreeUtil.getParentOfType(error.psi(), PsiMember.class); + if (member == null) return List.of(); + List registrar = new ArrayList<>(); + if (PsiUtil.isJavaToken(error.psi(), JavaTokenType.STATIC_KEYWORD)) { + registrar.add(myFactory.createModifierListFix(member, PsiModifier.STATIC, false, false)); + } + PsiClass containingClass = member.getContainingClass(); + if (containingClass != null && containingClass.getContainingClass() != null) { + registrar.add(addModifierFix(containingClass, PsiModifier.STATIC)); + } + return registrar; + }); fix(INTERFACE_CONSTRUCTOR, error -> myFactory.createConvertInterfaceToClassFix(requireNonNull(error.psi().getContainingClass()))); fix(INTERFACE_CLASS_INITIALIZER, error -> myFactory.createConvertInterfaceToClassFix(requireNonNull(error.psi().getContainingClass()))); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticInInner.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticInInner.java index f78ce95d8445..d66585c6cd99 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticInInner.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/StaticInInner.java @@ -17,7 +17,7 @@ public class a { static class a_ic_c {} - interface a_ic_i {} + interface a_ic_i {} static interface a_ic_i2 {} static @@ -93,12 +93,12 @@ public class a { } void ff() { class inn { - interface i {} + interface i {} } } Object o = new Runnable() { - interface i {} + interface i {} public void run() {} }; } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/Enum.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/Enum.java index 6f028e4c9514..de512baa3976 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/Enum.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/Enum.java @@ -207,7 +207,7 @@ class NestedEnums { enum E1 { } class C2 { - enum E2 { } + enum E2 { } } static class C3 { @@ -216,7 +216,7 @@ class NestedEnums { { new C3() { - enum E2 { } + enum E2 { } }; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/Enum.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/Enum.java index 5ba567ba83eb..180c7bc01077 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/Enum.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting8/Enum.java @@ -190,7 +190,7 @@ class NestedEnums { enum E1 { } class C2 { - enum E2 { } + enum E2 { } } static class C3 { @@ -199,7 +199,7 @@ class NestedEnums { { new C3() { - enum E2 { } + enum E2 { } }; } } diff --git a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_parentheses/UnnecessaryParenthesesInspection.java b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_parentheses/UnnecessaryParenthesesInspection.java index 18ac7f1cd2dd..7b86d6b9675c 100644 --- a/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_parentheses/UnnecessaryParenthesesInspection.java +++ b/java/java-tests/testData/ig/com/siyeh/igtest/style/unnecessary_parentheses/UnnecessaryParenthesesInspection.java @@ -101,10 +101,10 @@ public class UnnecessaryParenthesesInspection } class ParenthesesAroundLambda { - interface I { + interface I { void foo(int x, int y); } - interface J { + interface J { void foo(int x); }