Skip to content

Commit 50e9f60

Browse files
4.x: Improve annotation processing diagnostics (helidon-io#11281)
* Improve annotation processing diagnostics - Add originatingElement to io.helidon.common.types.Annotation - Update CodegenExceptiont to take multiple originating elements using a varag (backward compatible) - Update AptProcessor to delegate the logging of CodegenException to AptLogger - Update AptLogger to unwrap all originating elements correctly * Review feedback
1 parent ed510f2 commit 50e9f60

8 files changed

Lines changed: 168 additions & 57 deletions

File tree

builder/tests/common-types/src/main/java/io/helidon/common/types/AnnotationBlueprint.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ interface AnnotationBlueprint {
9999
@Option.Singular
100100
List<Annotation> metaAnnotations();
101101

102+
/**
103+
* The element used to create this instance.
104+
* The type of the object depends on the environment.
105+
*
106+
* @return originating element
107+
*/
108+
@Option.Redundant
109+
Optional<Object> originatingElement();
110+
102111
/**
103112
* The value property.
104113
*

codegen/apt/src/main/java/io/helidon/codegen/apt/AptAnnotationFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -87,6 +87,7 @@ private static Optional<Annotation> createAnnotation(Elements elements, Annotati
8787

8888
return Optional.of(builder
8989
.typeName(val)
90+
.originatingElement(am)
9091
.values(extractAnnotationValues(am, elements))
9192
.build());
9293
}

codegen/apt/src/main/java/io/helidon/codegen/apt/AptLogger.java

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,13 +23,15 @@
2323
import javax.lang.model.element.AnnotationMirror;
2424
import javax.lang.model.element.AnnotationValue;
2525
import javax.lang.model.element.Element;
26-
import javax.lang.model.element.TypeElement;
2726
import javax.tools.Diagnostic;
2827

2928
import io.helidon.codegen.CodegenEvent;
3029
import io.helidon.codegen.CodegenLogger;
3130
import io.helidon.codegen.CodegenOptions;
31+
import io.helidon.common.types.Annotation;
32+
import io.helidon.common.types.TypeInfo;
3233
import io.helidon.common.types.TypeName;
34+
import io.helidon.common.types.TypedElementInfo;
3335

3436
class AptLogger implements CodegenLogger {
3537
private final System.Logger logger;
@@ -47,15 +49,12 @@ public void log(CodegenEvent event) {
4749
// we always log to system logger if info or below
4850
// we only log to messager if info or above
4951
switch (event.level()) {
50-
case TRACE, DEBUG -> logSystem(event);
51-
case INFO -> {
52-
logSystem(event);
53-
logApt(event);
54-
}
55-
case WARNING, ERROR -> {
56-
logApt(event);
57-
}
58-
default -> logSystem(event);
52+
case INFO -> {
53+
logSystem(event);
54+
logApt(event);
55+
}
56+
case WARNING, ERROR -> logApt(event);
57+
default -> logSystem(event);
5958
}
6059
}
6160

@@ -87,23 +86,40 @@ private AnnotationMirror findAnnotation(List<Object> objects) {
8786
for (Object object : objects) {
8887
if (object instanceof AnnotationMirror mirror) {
8988
return mirror;
89+
} else if (object instanceof Annotation annot) {
90+
var o = annot.originatingElement().orElse(null);
91+
if (o instanceof AnnotationMirror mirror) {
92+
return mirror;
93+
}
9094
}
9195
}
9296
return null;
9397
}
9498

9599
private Element findElement(List<Object> objects) {
96-
for (Object object : objects) {
97-
if (object instanceof Element e) {
98-
return e;
100+
for (var object : objects) {
101+
var element = findElement(object);
102+
if (element != null) {
103+
return element;
99104
}
100105
}
101-
for (Object object : objects) {
102-
if (object instanceof TypeName t) {
103-
TypeElement element = env.getElementUtils().getTypeElement(t.declaredName());
104-
if (element != null) {
105-
return element;
106-
}
106+
return null;
107+
}
108+
109+
private Element findElement(Object orig) {
110+
if (orig instanceof Element element) {
111+
return element;
112+
} else if (orig instanceof TypeName typeName) {
113+
return env.getElementUtils().getTypeElement(typeName.declaredName());
114+
} else if (orig instanceof TypeInfo typeInfo) {
115+
var o = typeInfo.originatingElement().orElse(null);
116+
if (o instanceof Element element) {
117+
return element;
118+
}
119+
} else if (orig instanceof TypedElementInfo elementInfo) {
120+
var o = elementInfo.originatingElement().orElse(null);
121+
if (o instanceof Element element) {
122+
return element;
107123
}
108124
}
109125
return null;

codegen/apt/src/main/java/io/helidon/codegen/apt/AptProcessor.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2025 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,11 @@
5050

5151
/**
5252
* Annotation processor that maps APT types to Helidon types, and invokes {@link io.helidon.codegen.Codegen}.
53+
* <p>
54+
* <b>This class is NOT part of any supported API.
55+
* If you write code that depends on this, you do so at your own risk.
56+
* This code and its internal interfaces are subject to change or deletion without notice.</b>
57+
* </p>
5358
*/
5459
@SuppressWarnings("removal")
5560
public final class AptProcessor extends AbstractProcessor {
@@ -61,9 +66,7 @@ public final class AptProcessor extends AbstractProcessor {
6166
/**
6267
* Only for {@link java.util.ServiceLoader}, to be loaded by compiler.
6368
*/
64-
@Deprecated
6569
public AptProcessor() {
66-
super();
6770
}
6871

6972
@Override
@@ -106,18 +109,8 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
106109
try {
107110
return doProcess(annotations, roundEnv);
108111
} catch (CodegenException e) {
109-
Object originatingElement = e.originatingElement()
110-
.orElse(null);
111-
if (originatingElement instanceof Element element) {
112-
processingEnv.getMessager().printError(e.getMessage(), element);
113-
} else if (originatingElement instanceof TypeName typeName) {
114-
processingEnv.getMessager().printError(e.getMessage() + ", source: " + typeName.fqName());
115-
} else {
116-
if (originatingElement != null) {
117-
processingEnv.getMessager().printError(e.getMessage() + ", source: " + originatingElement);
118-
}
119-
}
120-
throw e;
112+
ctx.logger().log(e.toEvent(System.Logger.Level.ERROR));
113+
return true;
121114
} finally {
122115
thread.setContextClassLoader(previousClassloader);
123116
}

codegen/codegen/src/main/java/io/helidon/codegen/CodegenException.java

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package io.helidon.codegen;
1818

19+
import java.util.List;
1920
import java.util.Optional;
2021

2122
/**
@@ -27,7 +28,7 @@ public class CodegenException extends RuntimeException {
2728
* Originating element, depends on which codegen implementation is used.
2829
* For annotation processor, this could be the Element that caused this exception.
2930
*/
30-
private final Object originatingElement;
31+
private final List<Object> originatingElement;
3132

3233
/**
3334
* Constructor with a message.
@@ -36,7 +37,7 @@ public class CodegenException extends RuntimeException {
3637
*/
3738
public CodegenException(String message) {
3839
super(message);
39-
this.originatingElement = null;
40+
this.originatingElement = List.of();
4041
}
4142

4243
/**
@@ -47,30 +48,29 @@ public CodegenException(String message) {
4748
*/
4849
public CodegenException(String message, Throwable cause) {
4950
super(message, cause);
50-
this.originatingElement = null;
51+
this.originatingElement = List.of();
5152
}
5253

5354
/**
5455
* Constructor with a message and an originating element.
5556
*
56-
* @param message descriptive message
57-
* @param originatingElement element that caused this exception
57+
* @param message descriptive message
58+
* @param originatingElements elements that caused this exception
5859
*/
59-
public CodegenException(String message, Object originatingElement) {
60-
super(message);
61-
this.originatingElement = originatingElement;
60+
public CodegenException(String message, Object... originatingElements) {
61+
this(message, null, originatingElements);
6262
}
6363

6464
/**
6565
* Constructor with a message, cause, and an originating element.
6666
*
67-
* @param message descriptive message
68-
* @param cause throwable triggering this exception
69-
* @param originatingElement element that caused this exception
67+
* @param message descriptive message
68+
* @param cause throwable triggering this exception
69+
* @param originatingElements element that caused this exception
7070
*/
71-
public CodegenException(String message, Throwable cause, Object originatingElement) {
71+
public CodegenException(String message, Throwable cause, Object... originatingElements) {
7272
super(message, cause);
73-
this.originatingElement = originatingElement;
73+
this.originatingElement = List.of(originatingElements);
7474
}
7575

7676
/**
@@ -82,7 +82,16 @@ public CodegenException(String message, Throwable cause, Object originatingEleme
8282
* @return originating element of this exception
8383
*/
8484
public Optional<Object> originatingElement() {
85-
return Optional.ofNullable(originatingElement);
85+
return originatingElement.stream().findFirst();
86+
}
87+
88+
/**
89+
* Originating elements.
90+
*
91+
* @return originating elements of this exception
92+
*/
93+
public List<Object> originatingElements() {
94+
return originatingElement;
8695
}
8796

8897
/**
@@ -112,7 +121,7 @@ public CodegenEvent toEvent(System.Logger.Level level) {
112121
.level(level)
113122
.message(getMessage())
114123
.throwable(this)
115-
.update(it -> originatingElement().ifPresent(it::addObject))
124+
.update(it -> it.addObjects(originatingElements()))
116125
.build();
117126
}
118127
}

codegen/codegen/src/main/java/io/helidon/codegen/CodegenLogger.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2026 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package io.helidon.codegen;
1818

19+
import java.util.List;
20+
1921
/**
2022
* An abstraction for logging code processing and generation events.
2123
*/
@@ -44,11 +46,13 @@ static CodegenLogger create(System.Logger logger) {
4446
*
4547
* @param level log level to use
4648
* @param message message to log
49+
* @param objects origin elements
4750
*/
48-
default void log(System.Logger.Level level, String message) {
51+
default void log(System.Logger.Level level, String message, Object... objects) {
4952
log(CodegenEvent.builder()
50-
.level(level)
51-
.message(message)
52-
.build());
53+
.level(level)
54+
.message(message)
55+
.objects(List.of(objects))
56+
.build());
5357
}
5458
}

0 commit comments

Comments
 (0)