Skip to content

Commit 88e5f1f

Browse files
committed
Disallow bean definitions for enums. Fixes #1252
1 parent 7f58f2e commit 88e5f1f

File tree

4 files changed

+118
-67
lines changed

4 files changed

+118
-67
lines changed

inject-java/src/main/java/io/micronaut/annotation/processing/BeanDefinitionInjectProcessor.java

Lines changed: 84 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616

1717
package io.micronaut.annotation.processing;
1818

19-
import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
20-
import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
21-
import static javax.lang.model.element.ElementKind.FIELD;
19+
import static javax.lang.model.element.ElementKind.*;
2220
import static javax.lang.model.type.TypeKind.ARRAY;
2321

2422
import io.micronaut.aop.Adapter;
@@ -129,7 +127,7 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
129127
annotations = annotations
130128
.stream()
131129
.filter(ann -> !ann.getQualifiedName().toString().equals(AnnotationUtil.KOTLIN_METADATA))
132-
.filter(ann -> annotationUtils.hasStereotype(ann, ANNOTATION_STEREOTYPES) || AbstractAnnotationMetadataBuilder.isAnnotationMapped(((TypeElement) ann).getQualifiedName().toString()))
130+
.filter(ann -> annotationUtils.hasStereotype(ann, ANNOTATION_STEREOTYPES) || AbstractAnnotationMetadataBuilder.isAnnotationMapped(ann.getQualifiedName().toString()))
133131
.collect(Collectors.toSet());
134132

135133
if (!annotations.isEmpty()) {
@@ -143,8 +141,12 @@ public final boolean process(Set<? extends TypeElement> annotations, RoundEnviro
143141
.forEach(element -> {
144142
TypeElement typeElement = modelUtils.classElementFor(element);
145143

144+
if (element.getKind() == ENUM) {
145+
error(element, "Enum types cannot be defined as beans");
146+
return;
147+
}
146148
// skip Groovy code, handled by InjectTransform. Required for GroovyEclipse compiler
147-
if (groovyObjectType != null && typeUtils.isAssignable(typeElement.asType(), groovyObjectType)) {
149+
if (typeElement == null || (groovyObjectType != null && typeUtils.isAssignable(typeElement.asType(), groovyObjectType))) {
148150
return;
149151
}
150152

@@ -536,7 +538,7 @@ public Object visitExecutable(ExecutableElement method, Object o) {
536538
}
537539

538540
TypeElement declaringClassElement = modelUtils.classElementFor(method);
539-
if (modelUtils.isObjectClass(declaringClassElement)) {
541+
if (declaringClassElement == null || modelUtils.isObjectClass(declaringClassElement)) {
540542
return null;
541543
}
542544

@@ -570,63 +572,67 @@ private void visitConfigurationPropertySetter(ExecutableElement method) {
570572

571573
TypeElement declaringClass = modelUtils.classElementFor(method);
572574

573-
AnnotationMetadata methodAnnotationMetadata = annotationUtils.getAnnotationMetadata(method);
574-
if (methodAnnotationMetadata.hasStereotype(ConfigurationBuilder.class)) {
575-
writer.visitConfigBuilderMethod(
576-
fieldType,
577-
NameUtils.getterNameFor(parameter.getSimpleName().toString()),
578-
methodAnnotationMetadata,
579-
metadataBuilder);
580-
try {
581-
visitConfigurationBuilder(method, valueType, writer);
582-
} finally {
583-
writer.visitConfigBuilderEnd();
584-
}
585-
} else {
586-
String docComment = elementUtils.getDocComment(method);
587-
String setterName = method.getSimpleName().toString();
588-
PropertyMetadata propertyMetadata = metadataBuilder.visitProperty(
589-
concreteClass,
590-
declaringClass,
591-
getPropertyMetadataTypeReference(valueType),
592-
NameUtils.getPropertyNameForSetter(setterName),
593-
docComment,
594-
null
595-
);
575+
if (declaringClass != null) {
596576

597-
AnnotationMetadata annotationMetadata = DefaultAnnotationMetadata.mutateMember(
598-
AnnotationMetadata.EMPTY_METADATA,
599-
PropertySource.class.getName(),
600-
AnnotationMetadata.VALUE_MEMBER,
601-
Collections.singletonList(
602-
new io.micronaut.core.annotation.AnnotationValue(
603-
Property.class.getName(),
604-
Collections.singletonMap(
605-
"name",
606-
propertyMetadata.getPath()
607-
)
608-
)
609-
)
610-
);
577+
AnnotationMetadata methodAnnotationMetadata = annotationUtils.getAnnotationMetadata(method);
578+
if (methodAnnotationMetadata.hasStereotype(ConfigurationBuilder.class)) {
579+
writer.visitConfigBuilderMethod(
580+
fieldType,
581+
NameUtils.getterNameFor(parameter.getSimpleName().toString()),
582+
methodAnnotationMetadata,
583+
metadataBuilder);
584+
try {
585+
visitConfigurationBuilder(method, valueType, writer);
586+
} finally {
587+
writer.visitConfigBuilderEnd();
588+
}
589+
} else {
590+
String docComment = elementUtils.getDocComment(method);
591+
String setterName = method.getSimpleName().toString();
592+
PropertyMetadata propertyMetadata = metadataBuilder.visitProperty(
593+
concreteClass,
594+
declaringClass,
595+
getPropertyMetadataTypeReference(valueType),
596+
NameUtils.getPropertyNameForSetter(setterName),
597+
docComment,
598+
null
599+
);
611600

612-
boolean requiresReflection = modelUtils.isPrivate(method);
601+
AnnotationMetadata annotationMetadata = DefaultAnnotationMetadata.mutateMember(
602+
AnnotationMetadata.EMPTY_METADATA,
603+
PropertySource.class.getName(),
604+
AnnotationMetadata.VALUE_MEMBER,
605+
Collections.singletonList(
606+
new io.micronaut.core.annotation.AnnotationValue(
607+
Property.class.getName(),
608+
Collections.singletonMap(
609+
"name",
610+
propertyMetadata.getPath()
611+
)
612+
)
613+
)
614+
);
613615

614-
if (!requiresReflection && modelUtils.isProtected(method)) {
615-
PackageElement declaringPackage = elementUtils.getPackageOf(declaringClass);
616-
PackageElement concretePackage = elementUtils.getPackageOf(this.concreteClass);
617-
requiresReflection = !declaringPackage.getQualifiedName().equals(concretePackage.getQualifiedName());
618-
}
616+
boolean requiresReflection = modelUtils.isPrivate(method);
619617

620-
writer.visitSetterValue(
621-
modelUtils.resolveTypeReference(declaringClass),
622-
annotationMetadata,
623-
requiresReflection,
624-
fieldType,
625-
setterName,
626-
genericTypes,
627-
annotationUtils.getAnnotationMetadata(method.getParameters().get(0)),
628-
true);
618+
if (!requiresReflection && modelUtils.isProtected(method)) {
619+
PackageElement declaringPackage = elementUtils.getPackageOf(declaringClass);
620+
PackageElement concretePackage = elementUtils.getPackageOf(this.concreteClass);
621+
requiresReflection = !declaringPackage.getQualifiedName().equals(concretePackage.getQualifiedName());
622+
}
623+
624+
writer.visitSetterValue(
625+
modelUtils.resolveTypeReference(declaringClass),
626+
annotationMetadata,
627+
requiresReflection,
628+
fieldType,
629+
setterName,
630+
genericTypes,
631+
annotationUtils.getAnnotationMetadata(method.getParameters().get(0)),
632+
true);
633+
}
629634
}
635+
630636
}
631637

632638
/**
@@ -640,7 +646,13 @@ void visitBeanFactoryMethod(ExecutableElement beanMethod) {
640646
TypeMirror returnType = beanMethod.getReturnType();
641647
ExecutableElementParamInfo beanMethodParams = populateParameterData(beanMethod);
642648

643-
BeanDefinitionWriter beanMethodWriter = createFactoryBeanMethodWriterFor(beanMethod, returnType);
649+
TypeElement producedElement = modelUtils.classElementFor(typeUtils.asElement(returnType));
650+
651+
if (producedElement == null) {
652+
return;
653+
}
654+
655+
BeanDefinitionWriter beanMethodWriter = createFactoryBeanMethodWriterFor(beanMethod, returnType, producedElement);
644656

645657
if (returnType instanceof DeclaredType) {
646658
DeclaredType dt = (DeclaredType) returnType;
@@ -1180,6 +1192,10 @@ void visitAnnotatedMethod(ExecutableElement method, Object o) {
11801192
TypeMirror returnType = method.getReturnType();
11811193
TypeElement declaringClass = modelUtils.classElementFor(method);
11821194

1195+
if (declaringClass == null) {
1196+
return;
1197+
}
1198+
11831199
boolean isParent = !declaringClass.getQualifiedName().equals(this.concreteClass.getQualifiedName());
11841200
ExecutableElement overridingMethod = modelUtils.overridingOrHidingMethod(method, this.concreteClass).orElse(method);
11851201
TypeElement overridingClass = modelUtils.classElementFor(overridingMethod);
@@ -1278,6 +1294,10 @@ public Object visitVariable(VariableElement variable, Object o) {
12781294

12791295
TypeElement declaringClass = modelUtils.classElementFor(variable);
12801296

1297+
if (declaringClass == null) {
1298+
return null;
1299+
}
1300+
12811301
boolean isPrivate = modelUtils.isPrivate(variable);
12821302
boolean requiresReflection = isPrivate
12831303
|| modelUtils.isInheritedAndNotPublic(this.concreteClass, declaringClass, variable);
@@ -1336,6 +1356,10 @@ public Object visitConfigurationProperty(VariableElement field, AnnotationMetada
13361356

13371357
TypeElement declaringClass = modelUtils.classElementFor(field);
13381358

1359+
if (declaringClass == null) {
1360+
return null;
1361+
}
1362+
13391363
String fieldName = field.getSimpleName().toString();
13401364
if (fieldAnnotationMetadata.hasStereotype(ConfigurationBuilder.class)) {
13411365
writer.visitConfigBuilderField(fieldType, fieldName, fieldAnnotationMetadata, metadataBuilder);
@@ -1712,11 +1736,8 @@ private void tryAddAnnotationValue(Set<TypeElement> additionalInterfaces, Annota
17121736
}
17131737
}
17141738

1715-
private BeanDefinitionWriter createFactoryBeanMethodWriterFor(ExecutableElement method, TypeMirror producedType) {
1739+
private BeanDefinitionWriter createFactoryBeanMethodWriterFor(ExecutableElement method, TypeMirror producedType, TypeElement producedElement) {
17161740
AnnotationMetadata annotationMetadata = annotationUtils.getAnnotationMetadata(method);
1717-
Element element = typeUtils.asElement(producedType);
1718-
TypeElement producedElement = modelUtils.classElementFor(element);
1719-
17201741
PackageElement producedPackageElement = elementUtils.getPackageOf(producedElement);
17211742
PackageElement definingPackageElement = elementUtils.getPackageOf(concreteClass);
17221743

inject-java/src/main/java/io/micronaut/annotation/processing/ModelUtils.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,14 @@ public Types getTypeUtils() {
8484
* @param element The element
8585
* @return The {@link TypeElement}
8686
*/
87-
final TypeElement classElementFor(Element element) {
88-
while (!(JavaModelUtils.isClass(element) || JavaModelUtils.isInterface(element))) {
87+
@Nullable final TypeElement classElementFor(Element element) {
88+
while (element != null && !(JavaModelUtils.isClass(element) || JavaModelUtils.isInterface(element))) {
8989
element = element.getEnclosingElement();
9090
}
91-
return (TypeElement) element;
91+
if (element instanceof TypeElement) {
92+
return (TypeElement) element;
93+
}
94+
return null;
9295
}
9396

9497
/**

inject-java/src/main/java/io/micronaut/annotation/processing/TypeElementVisitorProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
116116
.stream()
117117
.filter(JavaModelUtils::isClassOrInterface)
118118
.map(modelUtils::classElementFor)
119-
.filter(typeElement -> groovyObjectType == null || !typeUtils.isAssignable(typeElement.asType(), groovyObjectType))
119+
.filter(typeElement -> typeElement == null || (groovyObjectType == null || !typeUtils.isAssignable(typeElement.asType(), groovyObjectType)))
120120
.forEach((typeElement) -> {
121121
String className = typeElement.getQualifiedName().toString();
122122
List<LoadedVisitor> matchedVisitors = loadedVisitors.values().stream().filter((v) -> v.matches(typeElement)).collect(Collectors.toList());
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.micronaut.inject.errors
2+
3+
import io.micronaut.inject.AbstractTypeElementSpec
4+
import io.micronaut.inject.BeanDefinition
5+
import io.micronaut.inject.aliasfor.TestAnnotation
6+
7+
import javax.inject.Named
8+
import javax.inject.Qualifier
9+
10+
class SingletonOnEnumSpec extends AbstractTypeElementSpec {
11+
12+
void "test that using @Singleton on an enum results in a compilation error"() {
13+
when:
14+
buildBeanDefinition('test.Test','''\
15+
package test;
16+
17+
@javax.inject.Singleton
18+
enum Test {
19+
}
20+
21+
''')
22+
then:
23+
def e = thrown(RuntimeException)
24+
e.message.contains('Enum types cannot be defined as beans')
25+
}
26+
27+
}

0 commit comments

Comments
 (0)