in src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java [1156:1362]
public ClassNode visitClassDeclaration(final ClassDeclarationContext ctx) {
String packageName = Optional.ofNullable(this.moduleNode.getPackageName()).orElse("");
String className = this.visitIdentifier(ctx.identifier());
if ("var".equals(className)) {
throw createParsingFailedException("var cannot be used for type declarations", ctx.identifier());
}
boolean isAnnotation = asBoolean(ctx.AT());
if (isAnnotation) {
if (asBoolean(ctx.typeParameters())) {
throw createParsingFailedException("annotation declaration cannot have type parameters", ctx.typeParameters());
}
if (asBoolean(ctx.EXTENDS())) {
throw createParsingFailedException("No extends clause allowed for annotation declaration", ctx.EXTENDS());
}
if (asBoolean(ctx.IMPLEMENTS())) {
throw createParsingFailedException("No implements clause allowed for annotation declaration", ctx.IMPLEMENTS());
}
}
boolean isEnum = asBoolean(ctx.ENUM());
if (isEnum) {
if (asBoolean(ctx.typeParameters())) {
throw createParsingFailedException("enum declaration cannot have type parameters", ctx.typeParameters());
}
if (asBoolean(ctx.EXTENDS())) {
throw createParsingFailedException("No extends clause allowed for enum declaration", ctx.EXTENDS());
}
}
boolean isInterface = (asBoolean(ctx.INTERFACE()) && !isAnnotation);
if (isInterface) {
if (asBoolean(ctx.IMPLEMENTS())) {
throw createParsingFailedException("No implements clause allowed for interface declaration", ctx.IMPLEMENTS());
}
}
ModifierManager modifierManager = new ModifierManager(this, ctx.getNodeMetaData(TYPE_DECLARATION_MODIFIERS));
Optional<ModifierNode> finalModifier = modifierManager.get(FINAL);
Optional<ModifierNode> sealedModifier = modifierManager.get(SEALED);
Optional<ModifierNode> nonSealedModifier = modifierManager.get(NON_SEALED);
boolean isFinal = finalModifier.isPresent();
boolean isSealed = sealedModifier.isPresent();
boolean isNonSealed = nonSealedModifier.isPresent();
boolean isRecord = asBoolean(ctx.RECORD());
boolean hasRecordHeader = asBoolean(ctx.formalParameters());
if (isRecord) {
if (!hasRecordHeader) {
throw createParsingFailedException("header declaration of record is expected", ctx.identifier());
}
if (asBoolean(ctx.EXTENDS())) {
throw createParsingFailedException("No extends clause allowed for record declaration", ctx.EXTENDS());
}
if (isSealed) {
throw createParsingFailedException("`sealed` is not allowed for record declaration", sealedModifier.get());
}
if (isNonSealed) {
throw createParsingFailedException("`non-sealed` is not allowed for record declaration", nonSealedModifier.get());
}
} else {
if (hasRecordHeader) {
throw createParsingFailedException("header declaration is only allowed for record declaration", ctx.formalParameters());
}
}
if (isSealed && isNonSealed) {
throw createParsingFailedException("type cannot be defined with both `sealed` and `non-sealed`", nonSealedModifier.get());
}
if (isFinal && (isSealed || isNonSealed)) {
throw createParsingFailedException("type cannot be defined with both " + (isSealed ? "`sealed`" : "`non-sealed`") + " and `final`", finalModifier.get());
}
if ((isAnnotation || isEnum) && (isSealed || isNonSealed)) {
ModifierNode mn = isSealed ? sealedModifier.get() : nonSealedModifier.get();
throw createParsingFailedException("modifier `" + mn.getText() + "` is not allowed for " + (isEnum ? "enum" : "annotation definition"), mn);
}
boolean hasPermits = asBoolean(ctx.PERMITS());
if (!isSealed && hasPermits) {
throw createParsingFailedException("only sealed type declarations should have `permits` clause", ctx);
}
int modifiers = modifierManager.getClassModifiersOpValue();
boolean syntheticPublic = ((modifiers & Opcodes.ACC_SYNTHETIC) != 0);
modifiers &= ~Opcodes.ACC_SYNTHETIC;
ClassNode classNode, outerClass = this.classNodeStack.peek();
if (isEnum) {
classNode = EnumHelper.makeEnumNode(
asBoolean(outerClass) ? className : packageName + className,
modifiers,
null,
outerClass
);
} else if (outerClass != null) {
classNode = new InnerClassNode(
outerClass,
outerClass.getName() + "$" + className,
modifiers,
ClassHelper.OBJECT_TYPE.getPlainNodeReference()
);
} else {
classNode = new ClassNode(
packageName + className,
modifiers,
ClassHelper.OBJECT_TYPE.getPlainNodeReference()
);
}
configureAST(classNode, ctx);
classNode.setSyntheticPublic(syntheticPublic);
classNode.setGenericsTypes(this.visitTypeParameters(ctx.typeParameters()));
boolean isInterfaceWithDefaultMethods = (isInterface && this.containsDefaultOrPrivateMethods(ctx));
if (isSealed) {
AnnotationNode sealedAnnotationNode = makeAnnotationNode(Sealed.class);
if (asBoolean(ctx.ps)) {
ListExpression permittedSubclassesListExpression =
listX(Arrays.stream(this.visitTypeList(ctx.ps))
.map(ClassExpression::new)
.collect(Collectors.toList()));
sealedAnnotationNode.setMember("permittedSubclasses", permittedSubclassesListExpression);
configureAST(sealedAnnotationNode, ctx.PERMITS());
sealedAnnotationNode.setNodeMetaData("permits", Boolean.TRUE);
}
classNode.addAnnotation(sealedAnnotationNode);
} else if (isNonSealed) {
classNode.addAnnotation(makeAnnotationNode(NonSealed.class));
}
if (asBoolean(ctx.TRAIT())) {
classNode.addAnnotation(makeAnnotationNode(Trait.class));
}
classNode.addAnnotations(modifierManager.getAnnotations());
if (isRecord && classNode.getAnnotations().stream().noneMatch(a ->
a.getClassNode().getName().equals(RECORD_TYPE_NAME))) {
classNode.addAnnotation(new AnnotationNode(ClassHelper.makeWithoutCaching(RECORD_TYPE_NAME))); // TODO: makeAnnotationNode(RecordType.class)
}
if (isInterfaceWithDefaultMethods) {
classNode.putNodeMetaData(IS_INTERFACE_WITH_DEFAULT_METHODS, Boolean.TRUE);
}
classNode.putNodeMetaData(CLASS_NAME, className);
if (asBoolean(ctx.CLASS()) || asBoolean(ctx.TRAIT())) {
if (asBoolean(ctx.scs)) {
ClassNode[] scs = this.visitTypeList(ctx.scs);
if (scs.length > 1) {
throw createParsingFailedException("Cannot extend multiple classes", ctx.EXTENDS());
}
classNode.setSuperClass(scs[0]);
}
classNode.setInterfaces(this.visitTypeList(ctx.is));
this.checkUsingGenerics(classNode);
} else if (isInterface) {
classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT | (outerClass != null ? Opcodes.ACC_STATIC : 0));
classNode.setInterfaces(this.visitTypeList(ctx.scs));
this.checkUsingGenerics(classNode);
this.hackMixins(classNode);
} else if (isEnum || isRecord) {
if (isRecord) this.transformRecordHeaderToProperties(ctx, classNode);
classNode.setInterfaces(this.visitTypeList(ctx.is));
this.checkUsingGenerics(classNode);
} else if (isAnnotation) {
classNode.setModifiers(classNode.getModifiers() | Opcodes.ACC_ANNOTATION | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT | (outerClass != null ? Opcodes.ACC_STATIC : 0));
classNode.addInterface(ClassHelper.Annotation_TYPE);
this.hackMixins(classNode);
} else {
throw createParsingFailedException("Unsupported class declaration: " + ctx.getText(), ctx);
}
this.classNodeStack.push(classNode);
ctx.classBody().putNodeMetaData(CLASS_DECLARATION_CLASS_NODE, classNode);
this.visitClassBody(ctx.classBody());
if (isRecord) {
classNode.getFields().stream().filter(f -> !isTrue(f, IS_RECORD_GENERATED) && !f.isStatic()).findFirst()
.ifPresent(fn -> {
throw this.createParsingFailedException("Instance field is not allowed in `record`", fn);
});
final List<Statement> objectInitializerStatements = classNode.getObjectInitializerStatements();
if (asBoolean(objectInitializerStatements)) {
throw this.createParsingFailedException("Instance initializer is not allowed in `record`", objectInitializerStatements.get(0));
}
}
this.classNodeStack.pop();
// The first element in classNodeList determines what GCL#parseClass for
// example will return. So we have to ensure it won't be an inner class.
if (outerClass == null) {
this.addToClassNodeList(classNode);
}
this.groovydocManager.handle(classNode, ctx);
return classNode;
}