[java-highlighting] More class-related stuff and implicit class stuff -> ClassChecker

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

GitOrigin-RevId: aa4e9711952175cbd0677b50d7486efaa19113fc
This commit is contained in:
Tagir Valeev
2025-01-10 16:29:57 +01:00
committed by intellij-monorepo-bot
parent c6e3aee5d6
commit 9450c1bf37
11 changed files with 169 additions and 252 deletions

View File

@@ -76,6 +76,16 @@ class.sealed.incomplete.permits=Sealed class permits clause must contain all sub
class.sealed.inheritor.expected.modifiers.can.be.final=Modifier 'sealed', 'non-sealed' or 'final' expected
class.sealed.inheritor.expected.modifiers=Modifier 'sealed' or 'non-sealed' expected
class.implicit.no.main.method=Implicitly declared class contains no 'main' method
class.implicit.invalid.file.name=Implicitly declared class's file name is not a valid identifier
class.implicit.initializer=Initializers are not allowed in implicitly declared classes
class.implicit.package.statement=Package statement is not allowed for implicitly declared class
interface.constructor=Constructor is not allowed in interface
interface.class.initializer=Class initializer is not allowed in interface
record.instance.initializer=Instance initializer is not allowed in record
record.instance.field=Instance field is not allowed in record
record.no.header=Record has no header declared
record.header.regular.class=Record header declared for non-record

View File

@@ -241,7 +241,22 @@ final class ClassChecker {
checkDuplicateClasses(aClass, classes);
}
void checkDuplicateClasses(@NotNull PsiClass aClass, @NotNull PsiClass @NotNull[] classes) {
void checkDuplicateClassesWithImplicit(@NotNull PsiJavaFile file) {
if (!myVisitor.isApplicable(JavaFeature.IMPLICIT_CLASSES)) return;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return;
Module module = ModuleUtilCore.findModuleForPsiElement(file);
if (module == null) return;
GlobalSearchScope scope = GlobalSearchScope.moduleScope(module).intersectWith(implicitClass.getResolveScope());
String qualifiedName = implicitClass.getQualifiedName();
if (qualifiedName == null) return;
PsiClass[] classes = JavaPsiFacade.getInstance(implicitClass.getProject()).findClasses(qualifiedName, scope);
checkDuplicateClasses(implicitClass, classes);
}
private void checkDuplicateClasses(@NotNull PsiClass aClass, @NotNull PsiClass @NotNull [] classes) {
PsiManager manager = aClass.getManager();
Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
if (module == null) return;
@@ -380,6 +395,69 @@ final class ClassChecker {
}
}
void checkImplicitClassWellFormed(@NotNull PsiJavaFile file) {
if (!myVisitor.isApplicable(JavaFeature.IMPLICIT_CLASSES)) return;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return;
String name = implicitClass.getQualifiedName();
if (!PsiNameHelper.getInstance(file.getProject()).isIdentifier(name)) {
myVisitor.report(JavaErrorKinds.CLASS_IMPLICIT_INVALID_FILE_NAME.create(file, implicitClass));
return;
}
PsiMethod[] methods = implicitClass.getMethods();
boolean hasMainMethod = ContainerUtil.exists(methods, method -> "main".equals(method.getName()) && PsiMethodUtil.isMainMethod(method));
if (!hasMainMethod) {
myVisitor.report(JavaErrorKinds.CLASS_IMPLICIT_NO_MAIN_METHOD.create(file, implicitClass));
}
}
void checkImplicitClassMember(@NotNull PsiMember member) {
if (!(member.getContainingClass() instanceof PsiImplicitClass)) return;
PsiElement anchor = member;
if (member instanceof PsiNameIdentifierOwner owner) {
PsiElement nameIdentifier = owner.getNameIdentifier();
if (nameIdentifier != null) {
anchor = nameIdentifier;
}
}
myVisitor.checkFeature(anchor, JavaFeature.IMPLICIT_CLASSES);
}
void checkIllegalInstanceMemberInRecord(@NotNull PsiMember member) {
if (!member.hasModifierProperty(PsiModifier.STATIC)) {
PsiClass aClass = member.getContainingClass();
if (aClass != null && aClass.isRecord()) {
var error = member instanceof PsiClassInitializer initializer ?
JavaErrorKinds.RECORD_INSTANCE_INITIALIZER.create(initializer) :
JavaErrorKinds.RECORD_INSTANCE_FIELD.create((PsiField)member);
myVisitor.report(error);
}
}
}
void checkThingNotAllowedInInterface(@NotNull PsiMember member) {
PsiClass aClass = member.getContainingClass();
if (aClass == null || !aClass.isInterface()) return;
if (member instanceof PsiMethod method && method.isConstructor()) {
myVisitor.report(JavaErrorKinds.INTERFACE_CONSTRUCTOR.create(method));
} else if (member instanceof PsiClassInitializer initializer) {
myVisitor.report(JavaErrorKinds.INTERFACE_CLASS_INITIALIZER.create(initializer));
}
}
void checkInitializersInImplicitClass(@NotNull PsiClassInitializer initializer) {
if (initializer.getContainingClass() instanceof PsiImplicitClass && myVisitor.isApplicable(JavaFeature.IMPLICIT_CLASSES)) {
myVisitor.report(JavaErrorKinds.CLASS_IMPLICIT_INITIALIZER.create(initializer));
}
}
void checkPackageNotAllowedInImplicitClass(@NotNull PsiPackageStatement statement) {
if (myVisitor.isApplicable(JavaFeature.IMPLICIT_CLASSES) && JavaImplicitClassUtil.isFileWithImplicitClass(myVisitor.file())) {
myVisitor.report(JavaErrorKinds.CLASS_IMPLICIT_PACKAGE.create(statement));
}
}
private static @Unmodifiable @NotNull Map<PsiJavaCodeReferenceElement, PsiClass> getPermittedClassesRefs(@NotNull PsiClass psiClass) {
PsiReferenceList permitsList = psiClass.getPermitsList();
if (permitsList == null) return Collections.emptyMap();

View File

@@ -76,7 +76,8 @@ final class JavaErrorVisitor extends JavaElementVisitor {
@Override
public void visitPackageStatement(@NotNull PsiPackageStatement statement) {
super.visitPackageStatement(statement);
myAnnotationChecker.checkPackageAnnotationContainingFile(statement);
if (!hasErrorResults()) myAnnotationChecker.checkPackageAnnotationContainingFile(statement);
if (!hasErrorResults()) myClassChecker.checkPackageNotAllowedInImplicitClass(statement);
}
@Override
@@ -162,6 +163,41 @@ final class JavaErrorVisitor extends JavaElementVisitor {
}
}
@Override
public void visitJavaFile(@NotNull PsiJavaFile file) {
super.visitJavaFile(file);
if (!hasErrorResults()) myClassChecker.checkImplicitClassWellFormed(file);
if (!hasErrorResults()) myClassChecker.checkDuplicateClassesWithImplicit(file);
}
@Override
public void visitClassInitializer(@NotNull PsiClassInitializer initializer) {
super.visitClassInitializer(initializer);
if (!hasErrorResults()) myClassChecker.checkImplicitClassMember(initializer);
if (!hasErrorResults()) myClassChecker.checkIllegalInstanceMemberInRecord(initializer);
if (!hasErrorResults()) myClassChecker.checkThingNotAllowedInInterface(initializer);
if (!hasErrorResults()) myClassChecker.checkInitializersInImplicitClass(initializer);
}
@Override
public void visitField(@NotNull PsiField field) {
super.visitField(field);
if (!hasErrorResults()) myClassChecker.checkIllegalInstanceMemberInRecord(field);
}
@Override
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
PsiElement parent = identifier.getParent();
if (parent instanceof PsiVariable variable) {
if (variable instanceof PsiField field) {
myClassChecker.checkImplicitClassMember(field);
}
}
else if (parent instanceof PsiMethod method) {
myClassChecker.checkImplicitClassMember(method);
}
}
@Override
public void visitClass(@NotNull PsiClass aClass) {
super.visitClass(aClass);
@@ -252,6 +288,9 @@ final class JavaErrorVisitor extends JavaElementVisitor {
if (!hasErrorResults() && aClass != null) {
myMethodChecker.checkDuplicateMethod(aClass, method);
}
if (!hasErrorResults() && method.isConstructor()) {
myClassChecker.checkThingNotAllowedInInterface(method);
}
}
@Override

View File

@@ -261,6 +261,20 @@ public final class JavaErrorKinds {
public static final Simple<PsiClass> RECORD_NO_HEADER = error(PsiClass.class, "record.no.header")
.withAnchor(PsiClass::getNameIdentifier);
public static final Simple<PsiRecordHeader> RECORD_HEADER_REGULAR_CLASS = error("record.header.regular.class");
public static final Simple<PsiClassInitializer> RECORD_INSTANCE_INITIALIZER = error("record.instance.initializer");
public static final Simple<PsiField> RECORD_INSTANCE_FIELD = error("record.instance.field");
public static final Simple<PsiClassInitializer> INTERFACE_CLASS_INITIALIZER = error("interface.class.initializer");
public static final Simple<PsiMethod> INTERFACE_CONSTRUCTOR = error("interface.constructor");
public static final Parameterized<PsiJavaFile, PsiImplicitClass> CLASS_IMPLICIT_NO_MAIN_METHOD =
error(PsiJavaFile.class, "class.implicit.no.main.method")
.withHighlightType(psi -> JavaErrorHighlightType.FILE_LEVEL_ERROR).parameterized();
public static final Parameterized<PsiJavaFile, PsiImplicitClass> CLASS_IMPLICIT_INVALID_FILE_NAME =
error(PsiJavaFile.class, "class.implicit.invalid.file.name")
.withHighlightType(psi -> JavaErrorHighlightType.FILE_LEVEL_ERROR).parameterized();
public static final Simple<PsiClassInitializer> CLASS_IMPLICIT_INITIALIZER = error("class.implicit.initializer");
public static final Simple<PsiPackageStatement> CLASS_IMPLICIT_PACKAGE = error("class.implicit.package.statement");
private static @NotNull <Psi extends PsiElement> Simple<Psi> error(@NotNull String key) {
return new Simple<>(key);

View File

@@ -6,7 +6,6 @@ import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveMembersIntoClassFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
@@ -15,15 +14,10 @@ import com.intellij.lang.jvm.JvmModifier;
import com.intellij.lang.jvm.actions.ChangeModifierRequest;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleFileIndex;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.JavaFeature;
@@ -36,7 +30,6 @@ import com.intellij.util.JavaPsiConstructorUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.PropertyKey;
import java.util.*;
import java.util.function.Consumer;
@@ -47,36 +40,6 @@ import java.util.function.Consumer;
*/
public final class HighlightClassUtil {
static @Nullable HighlightInfo.Builder checkDuplicateClasses(@NotNull PsiClass aClass, @NotNull PsiClass @NotNull[] classes) {
PsiManager manager = aClass.getManager();
Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
if (module == null) return null;
ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex();
VirtualFile virtualFile = PsiUtilCore.getVirtualFile(aClass);
if (virtualFile == null) return null;
boolean isTestSourceRoot = fileIndex.isInTestSourceContent(virtualFile);
String dupFileName = null;
PsiClass dupClass = null;
for (PsiClass dupClassCandidate : classes) {
// do not use equals
if (dupClassCandidate != aClass) {
VirtualFile file = dupClassCandidate.getContainingFile().getVirtualFile();
if (file != null && manager.isInProject(dupClassCandidate) && fileIndex.isInTestSourceContent(file) == isTestSourceRoot) {
dupClass = dupClassCandidate;
dupFileName = FileUtil.toSystemDependentName(file.getPath());
break;
}
}
}
if (dupFileName == null) return null;
HighlightInfo.Builder info = createInfoAndRegisterRenameFix(aClass, dupFileName, "duplicate.class.in.other.file");
IntentionAction action = QuickFixFactory.getInstance().createNavigateToDuplicateElementFix(dupClass);
if (info != null) {
info.registerFix(action, null, null, null, null);
}
return info;
}
static HighlightInfo.Builder checkClassRestrictedKeyword(@NotNull LanguageLevel level, @NotNull PsiIdentifier identifier) {
String className = identifier.getText();
if (isRestrictedIdentifier(className, level)) {
@@ -101,36 +64,6 @@ public final class HighlightClassUtil {
PsiKeyword.VALUE.equals(typeName) && JavaFeature.VALHALLA_VALUE_CLASSES.isSufficient(level);
}
private static @Nullable HighlightInfo.Builder createInfoAndRegisterRenameFix(@NotNull PsiClass aClass,
@NotNull String name,
@NotNull @PropertyKey(resourceBundle = JavaErrorBundle.BUNDLE) String key) {
String message = JavaErrorBundle.message(key, name);
PsiIdentifier identifier = aClass.getNameIdentifier();
HighlightInfo.Builder info;
if (aClass instanceof PsiImplicitClass) {
info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(aClass)
.fileLevelAnnotation()
.description(message);
IntentionAction action = QuickFixFactory.getInstance().createRenameFix(aClass);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
else {
if (identifier == null) return null;
TextRange textRange = identifier.getTextRange();
info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(textRange)
.descriptionAndTooltip(message);
IntentionAction action = QuickFixFactory.getInstance().createRenameFix(identifier);
if (action != null) {
info.registerFix(action, null, null, null, null);
}
}
return info;
}
private static HighlightInfo.Builder checkStaticFieldDeclarationInInnerClass(@NotNull PsiKeyword keyword) {
if (getEnclosingStaticClass(keyword, PsiField.class) == null) {
return null;
@@ -471,17 +404,6 @@ public final class HighlightClassUtil {
return null;
}
static HighlightInfo.Builder checkThingNotAllowedInInterface(@NotNull PsiElement element, @Nullable PsiClass aClass) {
if (aClass == null || !aClass.isInterface()) return null;
String description = JavaErrorBundle.message("not.allowed.in.interface");
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(element).descriptionAndTooltip(description);
IntentionAction action1 = QuickFixFactory.getInstance().createDeleteFix(element);
info.registerFix(action1, null, null, null, null);
IntentionAction action = QuickFixFactory.getInstance().createConvertInterfaceToClassFix(aClass);
info.registerFix(action, null, null, null, null);
return info;
}
static HighlightInfo.Builder checkQualifiedNew(@NotNull PsiNewExpression expression, @Nullable PsiType type, @Nullable PsiClass aClass) {
PsiExpression qualifier = expression.getQualifier();
if (qualifier == null) return null;
@@ -644,21 +566,6 @@ public final class HighlightClassUtil {
return null;
}
static HighlightInfo.Builder checkIllegalInstanceMemberInRecord(@NotNull PsiMember member) {
if (!member.hasModifierProperty(PsiModifier.STATIC)) {
PsiClass aClass = member.getContainingClass();
if (aClass != null && aClass.isRecord()) {
HighlightInfo.Builder info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(member)
.descriptionAndTooltip(JavaErrorBundle.message(member instanceof PsiClassInitializer ?
"record.instance.initializer" : "record.instance.field"));
IntentionAction action = QuickFixFactory.getInstance().createModifierListFix(member, PsiModifier.STATIC, true, false);
info.registerFix(action, null, null, null, null);
return info;
}
}
return null;
}
static HighlightInfo.Builder checkValueClassExtends(@NotNull PsiClass superClass,
@NotNull PsiClass psiClass,
@NotNull PsiElement elementToHighlight) {
@@ -915,31 +822,4 @@ public final class HighlightClassUtil {
}
return null;
}
static HighlightInfo.Builder checkImplicitClassMember(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel,
@NotNull PsiFile psiFile) {
if (!(member.getContainingClass() instanceof PsiImplicitClass implicitClass)) {
return null;
}
PsiElement anchor = member;
if (member instanceof PsiNameIdentifierOwner owner) {
PsiElement nameIdentifier = owner.getNameIdentifier();
if (nameIdentifier != null) {
anchor = nameIdentifier;
}
}
HighlightInfo.Builder builder = HighlightUtil.checkFeature(anchor, JavaFeature.IMPLICIT_CLASSES, languageLevel, psiFile);
if (builder == null) return null;
if (!(member instanceof PsiClass)) {
boolean hasClassToRelocate = PsiTreeUtil.findChildOfType(implicitClass, PsiClass.class) != null;
if (hasClassToRelocate) {
MoveMembersIntoClassFix fix = new MoveMembersIntoClassFix(implicitClass);
builder.registerFix(fix, null, null, null, null);
}
}
return builder;
}
}

View File

@@ -1,92 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.JavaImplicitClassUtil;
import com.intellij.psi.util.PsiMethodUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Checks and reports errors for implicitly declared classes {@link PsiImplicitClass}.
*/
public final class HighlightImplicitClassUtil {
static HighlightInfo.@Nullable Builder checkImplicitClassHasMainMethod(@NotNull PsiJavaFile file) {
if (!PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, file)) return null;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return null;
PsiMethod[] methods = implicitClass.getMethods();
boolean hasMainMethod = ContainerUtil.exists(methods, method -> "main".equals(method.getName()) && PsiMethodUtil.isMainMethod(method));
if (!hasMainMethod) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(file)
.fileLevelAnnotation()
.registerFix(QuickFixFactory.getInstance().createAddMainMethodFix(implicitClass), null, null, null, null)
.description(JavaErrorBundle.message("error.implicit.class.contains.no.main.method"));
}
return null;
}
static HighlightInfo.@Nullable Builder checkImplicitClassFileIsValidIdentifier(@NotNull PsiJavaFile file) {
if (!PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, file)) return null;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return null;
String name = implicitClass.getQualifiedName();
if (!PsiNameHelper.getInstance(file.getProject()).isIdentifier(name)) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(file)
.fileLevelAnnotation()
.description(JavaErrorBundle.message("error.implicit.class.has.invalid.file.name"));
}
return null;
}
static HighlightInfo.@Nullable Builder checkInitializersInImplicitClass(@NotNull PsiClassInitializer initializer) {
if (initializer.getContainingClass() instanceof PsiImplicitClass && PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, initializer)) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(initializer)
.descriptionAndTooltip(JavaErrorBundle.message("error.initializers.are.not.allowed.in.implicit.classes"))
.registerFix(QuickFixFactory.getInstance().createDeleteFix(initializer), null, null, null, null);
}
return null;
}
static HighlightInfo.@Nullable Builder checkPackageNotAllowedInImplicitClass(@NotNull PsiPackageStatement statement,
@NotNull PsiFile file) {
if (PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, file) && JavaImplicitClassUtil.isFileWithImplicitClass(file)) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
.range(statement)
.descriptionAndTooltip(JavaErrorBundle.message("error.package.statement.not.allowed.for.implicit.class"))
.registerFix(QuickFixFactory.getInstance().createDeleteFix(statement), null, null, null, null);
}
return null;
}
static @Nullable HighlightInfo.Builder checkDuplicateClasses(@NotNull PsiJavaFile file) {
if (!PsiUtil.isAvailable(JavaFeature.IMPLICIT_CLASSES, file)) return null;
PsiImplicitClass implicitClass = JavaImplicitClassUtil.getImplicitClassFor(file);
if (implicitClass == null) return null;
Module module = ModuleUtilCore.findModuleForPsiElement(file);
if (module == null) return null;
GlobalSearchScope scope = GlobalSearchScope.moduleScope(module).intersectWith(implicitClass.getResolveScope());
String qualifiedName = implicitClass.getQualifiedName();
if (qualifiedName == null) {
return null;
}
PsiClass[] classes = JavaPsiFacade.getInstance(implicitClass.getProject()).findClasses(qualifiedName, scope);
return HighlightClassUtil.checkDuplicateClasses(implicitClass, classes);
}
}

View File

@@ -252,14 +252,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
return false;
}
@Override
public void visitJavaFile(@NotNull PsiJavaFile file) {
super.visitJavaFile(file);
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkImplicitClassHasMainMethod(file));
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkImplicitClassFileIsValidIdentifier(file));
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkDuplicateClasses(file));
}
@Override
public void visitArrayInitializerExpression(@NotNull PsiArrayInitializerExpression expression) {
super.visitArrayInitializerExpression(expression);
@@ -429,14 +421,8 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitClassInitializer(@NotNull PsiClassInitializer initializer) {
super.visitClassInitializer(initializer);
if (!hasErrorResults()) add(HighlightClassUtil.checkImplicitClassMember(initializer, myLanguageLevel, myFile));
if (!hasErrorResults()) add(HighlightClassUtil.checkIllegalInstanceMemberInRecord(initializer));
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkInitializerCompleteNormally(initializer));
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkUnreachableStatement(initializer.getBody()));
if (!hasErrorResults()) {
add(HighlightClassUtil.checkThingNotAllowedInInterface(initializer, initializer.getContainingClass()));
}
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkInitializersInImplicitClass(initializer));
}
@Override
@@ -591,7 +577,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
@Override
public void visitField(@NotNull PsiField field) {
super.visitField(field);
if (!hasErrorResults()) add(HighlightClassUtil.checkIllegalInstanceMemberInRecord(field));
if (!hasErrorResults()) add(HighlightControlFlowUtil.checkFinalFieldInitialized(field));
}
@@ -623,9 +608,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
public void visitIdentifier(@NotNull PsiIdentifier identifier) {
PsiElement parent = identifier.getParent();
if (parent instanceof PsiVariable variable) {
if (variable instanceof PsiField field) {
add(HighlightClassUtil.checkImplicitClassMember(field, myLanguageLevel, myFile));
}
add(HighlightUtil.checkVariableAlreadyDefined(variable));
if (variable.isUnnamed()) {
HighlightInfo.Builder notAvailable = checkFeature(variable, JavaFeature.UNNAMED_PATTERNS_AND_VARIABLES);
@@ -635,11 +617,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
add(HighlightUtil.checkUnnamedVariableDeclaration(variable));
}
}
if (variable.getInitializer() == null) {
PsiElement child = variable.getLastChild();
if (child instanceof PsiErrorElement && child.getPrevSibling() == identifier) return;
}
}
else if (parent instanceof PsiClass aClass) {
if (aClass.isAnnotationType()) {
@@ -659,7 +636,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
}
else if (parent instanceof PsiMethod method) {
add(HighlightClassUtil.checkImplicitClassMember(method, myLanguageLevel, myFile));
if (method.isConstructor()) {
HighlightInfo.Builder info = HighlightMethodUtil.checkConstructorName(method);
if (info != null) {
@@ -859,11 +835,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (!hasErrorResults()) add(HighlightMethodUtil.checkConstructorHandleSuperClassExceptions(method));
if (!hasErrorResults()) add(HighlightMethodUtil.checkRecordAccessorDeclaration(method));
if (!hasErrorResults()) HighlightMethodUtil.checkRecordConstructorDeclaration(method, myErrorSink);
PsiClass aClass = method.getContainingClass();
if (!hasErrorResults() && method.isConstructor()) {
add(HighlightClassUtil.checkThingNotAllowedInInterface(method, aClass));
}
}
@Override
@@ -977,7 +948,6 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
if (JavaFeature.MODULES.isSufficient(myLanguageLevel)) {
if (!hasErrorResults()) add(ModuleHighlightUtil.checkPackageStatement(statement, myFile, myJavaModule));
}
if (!hasErrorResults()) add(HighlightImplicitClassUtil.checkPackageNotAllowedInImplicitClass(statement, myFile));
}
@Override

View File

@@ -4,6 +4,7 @@ package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.codeInsight.ClassUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationOnStaticMemberQualifyingTypeFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveAnnotationToPackageInfoFileFix;
import com.intellij.codeInsight.daemon.impl.quickfix.MoveMembersIntoClassFix;
import com.intellij.codeInsight.daemon.impl.quickfix.ReplaceVarWithExplicitTypeFix;
import com.intellij.codeInsight.intention.CommonIntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
@@ -17,6 +18,7 @@ import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequestsKt;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.Service;
import com.intellij.pom.java.JavaFeature;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
@@ -68,11 +70,13 @@ final class JavaErrorFixProvider {
JavaFixProvider<PsiElement, Object> genericRemover = error -> myFactory.createDeleteFix(error.psi());
for (JavaErrorKind<?, ?> kind : List.of(ANNOTATION_MEMBER_THROWS_NOT_ALLOWED, ANNOTATION_ATTRIBUTE_DUPLICATE,
ANNOTATION_NOT_ALLOWED_EXTENDS, RECEIVER_STATIC_CONTEXT, RECEIVER_WRONG_POSITION,
RECORD_HEADER_REGULAR_CLASS)) {
RECORD_HEADER_REGULAR_CLASS, INTERFACE_CLASS_INITIALIZER, INTERFACE_CONSTRUCTOR,
CLASS_IMPLICIT_INITIALIZER, CLASS_IMPLICIT_PACKAGE)) {
fix(kind, genericRemover);
}
createClassFixes();
createRecordFixes();
createAnnotationFixes();
createReceiverParameterFixes();
}
@@ -152,7 +156,6 @@ final class JavaErrorFixProvider {
}
return registrar;
});
fix(RECORD_NO_HEADER, error -> myFactory.createAddEmptyRecordHeaderFix(error.psi()));
fix(CLASS_SEALED_INCOMPLETE_PERMITS, error -> myFactory.createFillPermitsListFix(requireNonNull(error.psi().getNameIdentifier())));
multi(CLASS_SEALED_INHERITOR_EXPECTED_MODIFIERS_CAN_BE_FINAL, error -> List.of(
addModifierFix(error.psi(), PsiModifier.FINAL),
@@ -161,6 +164,23 @@ final class JavaErrorFixProvider {
multi(CLASS_SEALED_INHERITOR_EXPECTED_MODIFIERS, error -> List.of(
addModifierFix(error.psi(), PsiModifier.SEALED),
addModifierFix(error.psi(), PsiModifier.NON_SEALED)));
fix(CLASS_IMPLICIT_NO_MAIN_METHOD, error -> myFactory.createAddMainMethodFix(error.context()));
fix(UNSUPPORTED_FEATURE, error -> {
if (error.context() != JavaFeature.IMPLICIT_CLASSES) return null;
PsiMember member = PsiTreeUtil.getNonStrictParentOfType(error.psi(), PsiMember.class);
if (member == null || member instanceof PsiClass) return null;
if (!(member.getContainingClass() instanceof PsiImplicitClass implicitClass)) return null;
boolean hasClassToRelocate = PsiTreeUtil.findChildOfType(implicitClass, PsiClass.class) != null;
return hasClassToRelocate ? new MoveMembersIntoClassFix(implicitClass) : null;
});
fix(INTERFACE_CONSTRUCTOR, error -> myFactory.createConvertInterfaceToClassFix(requireNonNull(error.psi().getContainingClass())));
fix(INTERFACE_CLASS_INITIALIZER, error -> myFactory.createConvertInterfaceToClassFix(requireNonNull(error.psi().getContainingClass())));
}
private void createRecordFixes() {
fix(RECORD_NO_HEADER, error -> myFactory.createAddEmptyRecordHeaderFix(error.psi()));
fix(RECORD_INSTANCE_FIELD, error -> addModifierFix(error.psi(), PsiModifier.STATIC));
fix(RECORD_INSTANCE_INITIALIZER, error -> addModifierFix(error.psi(), PsiModifier.STATIC));
}
private void createReceiverParameterFixes() {

View File

@@ -546,9 +546,7 @@ too.many.array.dimensions=Too many array dimensions
error.cannot.infer.pattern.type=Cannot infer pattern type: {0}
error.extra.semicolons.between.import.statements.not.allowed=Extra semicolons between import statements are not allowed
error.guard.allowed.after.patterns.only=Guard is allowed after patterns only
error.implicit.class.contains.no.main.method=Implicitly declared class contains no 'main' method
error.implicit.class.has.invalid.file.name=Implicitly declared class's file name is not a valid identifier
error.package.statement.not.allowed.for.implicit.class=Package statement is not allowed for implicitly declared class
error.initializers.are.not.allowed.in.implicit.classes=Initializers are not allowed in implicitly declared classes
remove.unused.imports.quickfix.text=Remove unused imports
incomplete.project.state.pending.reference=Not resolved until the project is fully loaded

View File

@@ -2,8 +2,8 @@
interface A {
<error descr="Not allowed in interface">A();</error>
<error descr="Not allowed in interface">static {}</error>
<error descr="Not allowed in interface">{}</error>
<error descr="Constructor is not allowed in interface">A();</error>
<error descr="Class initializer is not allowed in interface">static {}</error>
<error descr="Class initializer is not allowed in interface">{}</error>
}

View File

@@ -50,7 +50,7 @@ class D {
}
interface IllegalMods {
void m1()<error descr="'{' or ';' expected"> </error>default<error descr="Identifier or type expected">;</error> <error descr="Not allowed in interface">{ }</error>
void m1()<error descr="'{' or ';' expected"> </error>default<error descr="Identifier or type expected">;</error> <error descr="Class initializer is not allowed in interface">{ }</error>
<error descr="Static methods in interfaces should have a body">static void m2()</error>;
<error descr="Illegal combination of modifiers 'static' and 'default'">static</error> <error descr="Illegal combination of modifiers 'default' and 'static'">default</error> void m3() { }