[java-highlighting] checkStaticDeclarationInInnerClass -> ClassChecker

Part of IDEA-365344 Create a new Java error highlighter with minimal dependencies (PSI only)

GitOrigin-RevId: 24c404c0e96e7c6b4127c76bb411814dcaef83e9
This commit is contained in:
Tagir Valeev
2025-01-13 09:40:08 +01:00
committed by intellij-monorepo-bot
parent 4d8680e81c
commit bbfc04bc96
9 changed files with 80 additions and 128 deletions

View File

@@ -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<PsiJavaCodeReferenceElement, PsiClass> getPermittedClassesRefs(@NotNull PsiClass psiClass) {
PsiReferenceList permitsList = psiClass.getPermitsList();
if (permitsList == null) return Collections.emptyMap();

View File

@@ -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);

View File

@@ -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());

View File

@@ -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));
}

View File

@@ -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<CommonIntentionAction> 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())));
}

View File

@@ -17,7 +17,7 @@ public class a {
<error descr="Static declarations in inner classes are not supported at language level '1.4'">static</error>
class a_ic_c {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface a_ic_i</error> {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface</error> a_ic_i {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">static</error> interface a_ic_i2 {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">static</error>
@@ -93,12 +93,12 @@ public class a {
}
void ff() {
class inn {
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface i</error> {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface</error> i {}
}
}
Object o = new Runnable() {
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface i</error> {}
<error descr="Static declarations in inner classes are not supported at language level '1.4'">interface</error> i {}
public void run() {}
};
}

View File

@@ -207,7 +207,7 @@ class NestedEnums {
enum E1 { }
class C2 {
<error descr="Static declarations in inner classes are not supported at language level '5'">enum E2</error> { }
<error descr="Static declarations in inner classes are not supported at language level '5'">enum</error> E2 { }
}
static class C3 {
@@ -216,7 +216,7 @@ class NestedEnums {
{
new C3() {
<error descr="Static declarations in inner classes are not supported at language level '5'">enum E2</error> { }
<error descr="Static declarations in inner classes are not supported at language level '5'">enum</error> E2 { }
};
}
}

View File

@@ -190,7 +190,7 @@ class NestedEnums {
enum E1 { }
class C2 {
<error descr="Static declarations in inner classes are not supported at language level '8'">enum E2</error> { }
<error descr="Static declarations in inner classes are not supported at language level '8'">enum</error> E2 { }
}
static class C3 {
@@ -199,7 +199,7 @@ class NestedEnums {
{
new C3() {
<error descr="Static declarations in inner classes are not supported at language level '8'">enum E2</error> { }
<error descr="Static declarations in inner classes are not supported at language level '8'">enum</error> E2 { }
};
}
}

View File

@@ -101,10 +101,10 @@ public class UnnecessaryParenthesesInspection
}
class ParenthesesAroundLambda {
<error descr="Static declarations in inner classes are not supported at language level '15'">interface I</error> {
<error descr="Static declarations in inner classes are not supported at language level '15'">interface</error> I {
void foo(int x, int y);
}
<error descr="Static declarations in inner classes are not supported at language level '15'">interface J</error> {
<error descr="Static declarations in inner classes are not supported at language level '15'">interface</error> J {
void foo(int x);
}