Skip to content

Commit

Permalink
Skip classes from introspection generation that are postponed (#11155)
Browse files Browse the repository at this point in the history
Currently if a class is postponed the introspection is still generated. This means that in the next round the introspection is recreated and an exception is thrown because there was an attempt to rewrite the some class.

Without this change it is impossible to use @wither and @introspected together.

Unclear how to create a test for it here. Will have to upgrade SourceGen and add a test there.

---------

Co-authored-by: Denis Stepanov <[email protected]>
  • Loading branch information
graemerocher and dstepanov authored Sep 6, 2024
1 parent 2ca4ab4 commit 4640ece
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import io.micronaut.inject.ast.MethodElement;
import io.micronaut.inject.ast.PropertyElement;
import io.micronaut.inject.ast.PropertyElementQuery;
import io.micronaut.inject.visitor.ElementPostponedToNextRoundException;
import io.micronaut.inject.visitor.TypeElementVisitor;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.inject.writer.ClassGenerationException;
Expand Down Expand Up @@ -256,13 +257,16 @@ public VisitorKind getVisitorKind() {
public void finish(VisitorContext visitorContext) {
try {
if (!writers.isEmpty()) {
for (BeanIntrospectionWriter writer : writers.values()) {
writers.forEach((className, writer) -> {
try {
writer.accept(visitorContext);
} catch (ElementPostponedToNextRoundException ignore) {
// Ignore, next round will redo
} catch (IOException e) {
throw new ClassGenerationException("I/O error occurred during class generation: " + e.getMessage(), e);
}
}
});

}
} finally {
writers.clear();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2017-2020 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.inject.visitor;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.inject.ast.Element;

/**
* Exception is thrown when the visitor is attempted to create a new file but the originated element is postponed to the next round.
*
* @author Denis Stepanov
* @since 4.7
*/
@Internal
public final class ElementPostponedToNextRoundException extends RuntimeException {

private final Element originatingElement;

/**
* @param originatingElement The originating element
*/
public ElementPostponedToNextRoundException(@NonNull Element originatingElement) {
super("Original element: " + originatingElement.getName() + " is postponed to the next round!");
this.originatingElement = originatingElement;
}

@NonNull
public Element getOriginatingElement() {
return originatingElement;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

Expand Down Expand Up @@ -77,6 +78,7 @@ abstract class AbstractInjectAnnotationProcessor extends AbstractProcessor {
private final Set<String> supportedAnnotationTypes = new HashSet<>(5);
private final Map<String, Boolean> isProcessedCache = new HashMap<>(30);
private Set<String> processedTypes;
protected Set<String> postponedTypes = new LinkedHashSet<>();

@Override
public SourceVersion getSupportedSourceVersion() {
Expand Down Expand Up @@ -216,7 +218,8 @@ protected JavaVisitorContext newVisitorContext(@NonNull ProcessingEnvironment pr
modelUtils,
filer,
visitorAttributes,
getVisitorKind()
getVisitorKind(),
postponedTypes
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.Set;

/**
* Utility methods for annotations.
Expand Down Expand Up @@ -143,7 +144,8 @@ public JavaVisitorContext newVisitorContext() {
modelUtils,
filer,
visitorAttributes,
TypeElementVisitor.VisitorKind.ISOLATING
TypeElementVisitor.VisitorKind.ISOLATING,
Set.of()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ public class TypeElementVisitorProcessor extends AbstractInjectAnnotationProcess

private List<LoadedVisitor> loadedVisitors;
private Collection<? extends TypeElementVisitor<?, ?>> typeElementVisitors;
private final Set<String> pendingTypes = new LinkedHashSet<>();

/**
* The visited annotation names.
Expand Down Expand Up @@ -241,8 +240,8 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
roundEnv.getRootElements()
).filter(notGroovyObject).forEach(elements::add);

pendingTypes.stream().map(elementUtils::getTypeElement).filter(Objects::nonNull).forEach(elements::add);
pendingTypes.clear();
postponedTypes.stream().map(elementUtils::getTypeElement).filter(Objects::nonNull).forEach(elements::add);
postponedTypes.clear();

if (!elements.isEmpty()) {

Expand Down Expand Up @@ -272,7 +271,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
}
error(originatingElement.element(), e.getMessage());
} catch (PostponeToNextRoundException e) {
pendingTypes.add(javaClassElement.getName());
postponedTypes.add(javaClassElement.getName());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import java.lang.annotation.Annotation;
Expand Down Expand Up @@ -265,11 +266,15 @@ public boolean isPrimitive() {
@Override
public Collection<ClassElement> getInterfaces() {
if (resolvedInterfaces == null) {
resolvedInterfaces = classElement.getInterfaces().stream().map(mirror -> newClassElement(mirror, getTypeArguments())).toList();
resolvedInterfaces = classElement.getInterfaces().stream().filter(this::onlyAvailable).map(mirror -> newClassElement(mirror, getTypeArguments())).toList();
}
return resolvedInterfaces;
}

private boolean onlyAvailable(TypeMirror mirror) {
return !(mirror instanceof DeclaredType declaredType) || declaredType.getKind() != TypeKind.ERROR;
}

@Override
public Optional<ClassElement> getSuperType() {
if (resolvedSuperType == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.micronaut.annotation.processing.AnnotationProcessingOutputVisitor;
import io.micronaut.annotation.processing.AnnotationUtils;
import io.micronaut.inject.visitor.ElementPostponedToNextRoundException;
import io.micronaut.annotation.processing.GenericUtils;
import io.micronaut.annotation.processing.JavaAnnotationMetadataBuilder;
import io.micronaut.annotation.processing.JavaElementAnnotationMetadataFactory;
Expand Down Expand Up @@ -66,6 +67,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -100,6 +102,7 @@ public final class JavaVisitorContext implements VisitorContext, BeanElementVisi
private final JavaElementAnnotationMetadataFactory elementAnnotationMetadataFactory;
private final JavaNativeElementsHelper nativeElementsHelper;
private final Filer filer;
private final Set<String> postponedTypes;

/**
* The default constructor.
Expand Down Expand Up @@ -128,7 +131,7 @@ public JavaVisitorContext(
Filer filer,
MutableConvertibleValues<Object> visitorAttributes,
TypeElementVisitor.VisitorKind visitorKind) {
this(processingEnv, messager, elements, types, modelUtils, filer, visitorAttributes, visitorKind);
this(processingEnv, messager, elements, types, modelUtils, filer, visitorAttributes, visitorKind, new HashSet<>());
}

/**
Expand All @@ -142,7 +145,9 @@ public JavaVisitorContext(
* @param filer The filer
* @param visitorAttributes The attributes
* @param visitorKind The visitor kind
* @deprecated No longer needed
*/
@Deprecated(forRemoval = true, since = "4.7.0")
public JavaVisitorContext(
ProcessingEnvironment processingEnv,
Messager messager,
Expand All @@ -152,6 +157,32 @@ public JavaVisitorContext(
Filer filer,
MutableConvertibleValues<Object> visitorAttributes,
TypeElementVisitor.VisitorKind visitorKind) {
this(processingEnv, messager, elements, types, modelUtils, filer, visitorAttributes, visitorKind, Set.of());
}

/**
* The default constructor.
*
* @param processingEnv The processing environment
* @param messager The messager
* @param elements The elements
* @param types Type types
* @param modelUtils The model utils
* @param filer The filer
* @param visitorAttributes The attributes
* @param visitorKind The visitor kind
* @param postponedTypes The postponed types
*/
public JavaVisitorContext(
ProcessingEnvironment processingEnv,
Messager messager,
Elements elements,
Types types,
ModelUtils modelUtils,
Filer filer,
MutableConvertibleValues<Object> visitorAttributes,
TypeElementVisitor.VisitorKind visitorKind,
Set<String> postponedTypes) {
this.messager = messager;
this.elements = elements;
this.types = types;
Expand All @@ -166,6 +197,7 @@ public JavaVisitorContext(
this.elementAnnotationMetadataFactory = new JavaElementAnnotationMetadataFactory(false, this.annotationMetadataBuilder);
this.expressionCompilationContextFactory = new DefaultExpressionCompilationContextFactory(this);
this.filer = filer;
this.postponedTypes = postponedTypes;
}

@Override
Expand Down Expand Up @@ -321,13 +353,29 @@ private void printMessage(String message, Diagnostic.Kind kind, @Nullable io.mic
}
}

private void checkForPostponedOriginalElement(io.micronaut.inject.ast.Element originatingElement) {
if (originatingElement != null && postponedTypes.contains(originatingElement.getName())) {
throw new ElementPostponedToNextRoundException(originatingElement);
}
}

private void checkForPostponedOriginalElements(io.micronaut.inject.ast.Element[] originatingElements) {
if (originatingElements != null) {
for (io.micronaut.inject.ast.Element originatingElement : originatingElements) {
checkForPostponedOriginalElement(originatingElement);
}
}
}

@Override
public OutputStream visitClass(String classname, @Nullable io.micronaut.inject.ast.Element originatingElement) throws IOException {
checkForPostponedOriginalElement(originatingElement);
return outputVisitor.visitClass(classname, new io.micronaut.inject.ast.Element[] {originatingElement});
}

@Override
public OutputStream visitClass(String classname, io.micronaut.inject.ast.Element... originatingElements) throws IOException {
checkForPostponedOriginalElements(originatingElements);
return outputVisitor.visitClass(classname, originatingElements);
}

Expand All @@ -338,11 +386,13 @@ public void visitServiceDescriptor(String type, String classname) {

@Override
public void visitServiceDescriptor(String type, String classname, io.micronaut.inject.ast.Element originatingElement) {
checkForPostponedOriginalElement(originatingElement);
outputVisitor.visitServiceDescriptor(type, classname, originatingElement);
}

@Override
public Optional<GeneratedFile> visitMetaInfFile(String path, io.micronaut.inject.ast.Element... originatingElements) {
checkForPostponedOriginalElements(originatingElements);
return outputVisitor.visitMetaInfFile(path, originatingElements);
}

Expand All @@ -353,11 +403,13 @@ public Optional<GeneratedFile> visitGeneratedFile(String path) {

@Override
public Optional<GeneratedFile> visitGeneratedFile(String path, io.micronaut.inject.ast.Element... originatingElements) {
checkForPostponedOriginalElements(originatingElements);
return outputVisitor.visitGeneratedFile(path, originatingElements);
}

@Override
public Optional<GeneratedFile> visitGeneratedSourceFile(String packageName, String fileNameWithoutExtension, io.micronaut.inject.ast.Element... originatingElements) {
checkForPostponedOriginalElements(originatingElements);
return outputVisitor.visitGeneratedSourceFile(packageName, fileNameWithoutExtension, originatingElements);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.micronaut.visitors


import io.micronaut.annotation.processing.test.AbstractTypeElementSpec

class PostponedVisitorsSpec extends AbstractTypeElementSpec {

void 'test'() {
when:
def definition = buildBeanIntrospection('test.Walrus', '''
package test;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.visitors.Wither;
@Introspected
@Wither
public record Walrus (
@NonNull
String name,
int age,
byte[] chipInfo
) implements WalrusWither {
}
''')
then:
definition
}
}
8 changes: 8 additions & 0 deletions inject-java/src/test/groovy/io/micronaut/visitors/Wither.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.micronaut.visitors;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Wither {
}
Loading

0 comments on commit 4640ece

Please sign in to comment.