mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Records error highlighting: well-formedness; components (part of IDEA-228460)
GitOrigin-RevId: f72bba51738ed7d89dbbebf43311a049c23d317f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2c22ee32a7
commit
3ff5ba7461
+26
-3
@@ -404,11 +404,14 @@ public class HighlightClassUtil {
|
||||
static HighlightInfo checkExtendsAllowed(@NotNull PsiReferenceList list) {
|
||||
if (list.getParent() instanceof PsiClass) {
|
||||
PsiClass aClass = (PsiClass)list.getParent();
|
||||
if (aClass.isEnum()) {
|
||||
if (aClass.isEnum() || aClass.isRecord()) {
|
||||
boolean isExtends = list.equals(aClass.getExtendsList());
|
||||
if (isExtends) {
|
||||
String description = JavaErrorMessages.message("extends.after.enum");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create();
|
||||
String description = JavaErrorMessages.message(aClass.isRecord() ? "record.extends" : "extends.after.enum");
|
||||
HighlightInfo info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(list).descriptionAndTooltip(description).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteFix(list));
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -873,4 +876,24 @@ public class HighlightClassUtil {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HighlightInfo checkWellFormedRecord(PsiClass psiClass) {
|
||||
PsiRecordHeader header = psiClass.getRecordHeader();
|
||||
if (!psiClass.isRecord()) {
|
||||
if (header != null) {
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(header)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("record.header.regular.class")).create();
|
||||
QuickFixAction.registerQuickFixAction(info, QUICK_FIX_FACTORY.createDeleteFix(header));
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
PsiIdentifier identifier = psiClass.getNameIdentifier();
|
||||
if (identifier == null) return null;
|
||||
if (header == null) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("record.no.header")).create();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+28
-1
@@ -86,6 +86,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
ContainerUtil.newTroveSet(PsiModifier.ABSTRACT, PsiModifier.STATIC, PsiModifier.NATIVE, PsiModifier.FINAL, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED);
|
||||
|
||||
private static final String SERIAL_PERSISTENT_FIELDS_FIELD_NAME = "serialPersistentFields";
|
||||
private static final Set<String> RESTRICTED_RECORD_COMPONENT_NAMES = ContainerUtil.immutableSet(
|
||||
"clone", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait");
|
||||
|
||||
static {
|
||||
ourClassIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet(PsiModifier.FINAL));
|
||||
@@ -915,6 +917,10 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
isAllowed &= !(PsiModifier.FINAL.equals(modifier) || PsiModifier.ABSTRACT.equals(modifier));
|
||||
}
|
||||
|
||||
if (aClass.isRecord()) {
|
||||
isAllowed &= !PsiModifier.ABSTRACT.equals(modifier);
|
||||
}
|
||||
|
||||
if (aClass.getContainingClass() instanceof PsiAnonymousClass) {
|
||||
isAllowed &= !privateOrProtected;
|
||||
}
|
||||
@@ -958,7 +964,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
else if (modifierOwner instanceof PsiClassInitializer) {
|
||||
isAllowed = PsiModifier.STATIC.equals(modifier);
|
||||
}
|
||||
else if (modifierOwner instanceof PsiLocalVariable || modifierOwner instanceof PsiParameter) {
|
||||
else if (modifierOwner instanceof PsiLocalVariable || modifierOwner instanceof PsiParameter ||
|
||||
modifierOwner instanceof PsiRecordComponent) {
|
||||
isAllowed = PsiModifier.FINAL.equals(modifier);
|
||||
}
|
||||
else if (modifierOwner instanceof PsiReceiverParameter) {
|
||||
@@ -1464,6 +1471,26 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
return infos;
|
||||
}
|
||||
|
||||
static HighlightInfo checkRecordComponentName(PsiRecordComponent component) {
|
||||
PsiIdentifier identifier = component.getNameIdentifier();
|
||||
if (identifier != null) {
|
||||
String name = identifier.getText();
|
||||
if (RESTRICTED_RECORD_COMPONENT_NAMES.contains(name)) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(identifier)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("record.component.restricted.name", name)).create();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static HighlightInfo checkRecordComponentVarArg(PsiRecordComponent recordComponent) {
|
||||
if (recordComponent.isVarArgs() && PsiTreeUtil.getNextSiblingOfType(recordComponent, PsiRecordComponent.class) != null) {
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(recordComponent)
|
||||
.descriptionAndTooltip(JavaErrorMessages.message("record.component.vararg.not.last")).create();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum SelectorKind { INT, ENUM, STRING }
|
||||
|
||||
private static SelectorKind getSwitchSelectorKind(@NotNull PsiType type) {
|
||||
|
||||
+8
@@ -469,6 +469,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkImplicitThisReferenceBeforeSuper(aClass, myJavaSdkVersion));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkClassAndPackageConflict(aClass));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkPublicClassInRightFile(aClass));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightClassUtil.checkWellFormedRecord(aClass));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(GenericsHighlightUtil.checkTypeParameterOverrideEquivalentMethods(aClass, myLanguageLevel));
|
||||
}
|
||||
|
||||
@@ -1062,6 +1063,13 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRecordComponent(PsiRecordComponent recordComponent) {
|
||||
super.visitRecordComponent(recordComponent);
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkRecordComponentVarArg(recordComponent));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkRecordComponentName(recordComponent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParameter(PsiParameter parameter) {
|
||||
super.visitParameter(parameter);
|
||||
|
||||
@@ -33,6 +33,7 @@ public class AnnotationTargetUtil {
|
||||
private static final TargetType[] CONSTRUCTOR_TARGETS = {TargetType.CONSTRUCTOR, TargetType.TYPE_USE};
|
||||
private static final TargetType[] METHOD_TARGETS = {TargetType.METHOD, TargetType.TYPE_USE};
|
||||
private static final TargetType[] FIELD_TARGETS = {TargetType.FIELD, TargetType.TYPE_USE};
|
||||
private static final TargetType[] RECORD_COMPONENT_TARGETS = {TargetType.RECORD_COMPONENT, TargetType.FIELD, TargetType.METHOD, TargetType.TYPE_USE};
|
||||
private static final TargetType[] PARAMETER_TARGETS = {TargetType.PARAMETER, TargetType.TYPE_USE};
|
||||
private static final TargetType[] LOCAL_VARIABLE_TARGETS = {TargetType.LOCAL_VARIABLE, TargetType.TYPE_USE};
|
||||
private static final TargetType[] MODULE_TARGETS = {TargetType.MODULE};
|
||||
@@ -67,6 +68,9 @@ public class AnnotationTargetUtil {
|
||||
return TYPE_TARGETS;
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiRecordComponent) {
|
||||
return RECORD_COMPONENT_TARGETS;
|
||||
}
|
||||
if (element instanceof PsiMethod) {
|
||||
if (((PsiMethod)element).isConstructor()) {
|
||||
return CONSTRUCTOR_TARGETS;
|
||||
|
||||
@@ -32,7 +32,7 @@ public interface PsiAnnotation extends PsiAnnotationMemberValue, JvmAnnotation {
|
||||
*/
|
||||
enum TargetType {
|
||||
// see java.lang.annotation.ElementType
|
||||
TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_USE, TYPE_PARAMETER, MODULE,
|
||||
TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_USE, TYPE_PARAMETER, MODULE, RECORD_COMPONENT,
|
||||
// auxiliary value, used when it's impossible to determine annotation's targets
|
||||
UNKNOWN;
|
||||
|
||||
|
||||
@@ -3,4 +3,13 @@ package com.intellij.psi;
|
||||
|
||||
public interface PsiRecordComponent extends PsiMember, PsiVariable {
|
||||
PsiRecordComponent[] EMPTY_ARRAY = new PsiRecordComponent[]{};
|
||||
|
||||
/**
|
||||
* Checks if the record component accepts a variable number of arguments in canonical constructor.
|
||||
*
|
||||
* @return true if the record component is a vararg, false otherwise
|
||||
*/
|
||||
default boolean isVarArgs() {
|
||||
return getType() instanceof PsiEllipsisType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,12 @@ public class PsiModifierListImpl extends JavaStubPsiElement<PsiModifierListStub>
|
||||
implicitModifiers.add(STATIC);
|
||||
}
|
||||
}
|
||||
if (((PsiClass)parent).isRecord()) {
|
||||
if (!(grandParent instanceof PsiFile)) {
|
||||
implicitModifiers.add(STATIC);
|
||||
}
|
||||
implicitModifiers.add(FINAL);
|
||||
}
|
||||
if (((PsiClass)parent).isEnum()) {
|
||||
if (!(grandParent instanceof PsiFile)) {
|
||||
implicitModifiers.add(STATIC);
|
||||
@@ -155,6 +161,9 @@ public class PsiModifierListImpl extends JavaStubPsiElement<PsiModifierListStub>
|
||||
implicitModifiers.add(PRIVATE);
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiRecordComponent) {
|
||||
implicitModifiers.add(FINAL);
|
||||
}
|
||||
else if (parent instanceof PsiField) {
|
||||
if (parent instanceof PsiEnumConstant) {
|
||||
Collections.addAll(implicitModifiers, PUBLIC, STATIC, FINAL);
|
||||
|
||||
@@ -59,6 +59,8 @@ annotation.target.LOCAL_VARIABLE=local variable
|
||||
annotation.target.PACKAGE=package
|
||||
# suppress inspection "UnusedProperty"
|
||||
annotation.target.MODULE=module
|
||||
# suppress inspection "UnusedProperty"
|
||||
annotation.target.RECORD_COMPONENT=record component
|
||||
|
||||
# generics related messages
|
||||
generics.holder.type=Type
|
||||
@@ -465,6 +467,12 @@ lvti.null=Cannot infer type: variable initializer is 'null'
|
||||
lvti.void=Cannot infer type: variable initializer is 'void'
|
||||
lvti.selfReferenced=Cannot infer type: variable initializer is self-referencing
|
||||
|
||||
record.no.header=Record has no header declared
|
||||
record.header.regular.class=Record header declared for non-record
|
||||
record.extends=No extends clause allowed for record
|
||||
record.component.vararg.not.last=Vararg record component must be the last in the list
|
||||
record.component.restricted.name=Illegal record component name ''{0}''
|
||||
|
||||
feature.generics=Generics
|
||||
feature.annotations=Annotations
|
||||
feature.static.imports=Static imports
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import java.lang.annotation.*;
|
||||
|
||||
record <error descr="Record has no header declared">NoComponentList</error> {}
|
||||
record NoComponents() {}
|
||||
class ClassWithComponents<error descr="Record header declared for non-record">(int x)</error> {}
|
||||
class ClassWithComponents2<error descr="Record header declared for non-record">(int x, int y)</error> {}
|
||||
<error descr="Modifier 'abstract' not allowed here">abstract</error> record AbstractRecord() {}
|
||||
record ExtendsObject() <error descr="No extends clause allowed for record">extends Object</error> {}
|
||||
class ExtendsRecord extends <error descr="Cannot inherit from final 'NoComponents'">NoComponents</error> {}
|
||||
|
||||
record ComponentModifiers(
|
||||
<error descr="Modifier 'public' not allowed here">public</error> int x,
|
||||
<error descr="Modifier 'static' not allowed here">static</error> int y,
|
||||
final int z) {}
|
||||
record ComponentDuplicateName(int <error descr="Variable 'x' is already defined in the scope">x</error>, int <error descr="Variable 'x' is already defined in the scope">x</error>) {}
|
||||
record VarArgOk(int... x) {}
|
||||
record VarArgOk2(int x, int... y) {}
|
||||
record VarArgNotOk(<error descr="Vararg record component must be the last in the list">int... x</error>, int y) {}
|
||||
record IllegalComponentName(
|
||||
int <error descr="Illegal record component name 'clone'">clone</error>,
|
||||
int <error descr="Illegal record component name 'finalize'">finalize</error>,
|
||||
int <error descr="Illegal record component name 'getClass'">getClass</error>,
|
||||
int <error descr="Illegal record component name 'hashCode'">hashCode</error>,
|
||||
int <error descr="Illegal record component name 'notify'">notify</error>,
|
||||
int <error descr="Illegal record component name 'notifyAll'">notifyAll</error>,
|
||||
int <error descr="Illegal record component name 'toString'">toString</error>,
|
||||
int <error descr="Illegal record component name 'wait'">wait</error>) {}
|
||||
|
||||
@interface SimpleAnno {}
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
@interface ConstructorAnno {}
|
||||
@Target(ElementType.METHOD)
|
||||
@interface MethodAnno {}
|
||||
record AnnotatedComponents(
|
||||
@SimpleAnno int x,
|
||||
<error descr="'@ConstructorAnno' not applicable to record component">@ConstructorAnno</error> int y,
|
||||
@MethodAnno int z) {}
|
||||
Reference in New Issue
Block a user