mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
[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:
committed by
intellij-monorepo-bot
parent
4d8680e81c
commit
bbfc04bc96
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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())));
|
||||
}
|
||||
|
||||
@@ -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() {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 { }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 { }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user