Repository: quarkusio/quarkus
Issue: Indexed `provided` annotation breaks `build` goal (or app startup) in 2.14

### Describe the bug

While trying to upgrade from 2.13.3 to 2.14.0/1 I was greeted with a strange `java.lang.ClassNotFoundException: lombok.NonNull` very early in the boot process of the app.

This is in a multi-module project that is working just fine with Quarkus 2.13.3 (and the "old" Jandex 1.2.3).

In a non-app module, a class is using `lombok.NonNull` on a field alongside `lombok.RequiredArgsConstructor`.
`jandex-maven-plugin` is executed in the module to provide an index.
The lombok jar is a `provided` dependency (as recommended generally).
See the reproducer further down.

### Expected behavior

Should work as in 2.13

### Actual behavior

Fails at app startup (with ECJ) or already when building the app via `build` goal (with standard compiler).

### How to Reproduce?

[q_jandex_nonnull.zip](https://github.com/quarkusio/quarkus/files/10071585/q_jandex_nonnull.zip)

`mvn clean verify`:

```
[INFO] --- quarkus-maven-plugin:2.14.1.Final:build (default) @ code-with-quarkus-app ---
[WARNING] [io.quarkus.arc.processor.BeanArchives] Failed to index lombok.NonNull: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD@65f470f8
[INFO] [io.quarkus.arc.processor.IndexClassLookupUtils] Class for name: lombok.NonNull was not found in Jandex index. Please ensure the class is part of the index.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary for code-with-quarkus 1.0.0-SNAPSHOT:
[INFO] 
[INFO] code-with-quarkus .................................. SUCCESS [  0.027 s]
[INFO] code-with-quarkus-lib .............................. SUCCESS [  0.924 s]
[INFO] code-with-quarkus-app .............................. FAILURE [  1.511 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.968 s
[INFO] Finished at: 2022-11-22T23:13:14+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:2.14.1.Final:build (default) on project code-with-quarkus-app: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR] 	[error]: Build step io.quarkus.arc.deployment.ArcProcessor#generateResources threw an exception: java.lang.IllegalStateException: java.util.concurrent.ExecutionException: java.lang.NullPointerException: Annotation class not available: @NonNull
[ERROR] 	at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:918)
[ERROR] 	at io.quarkus.builder.BuildContext.run(BuildContext.java:281)
[ERROR] 	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
[ERROR] 	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
[ERROR] 	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
[ERROR] 	at java.base/java.lang.Thread.run(Thread.java:833)
[ERROR] 	at org.jboss.threads.JBossThread.run(JBossThread.java:501)
[ERROR] Caused by: java.util.concurrent.ExecutionException: java.lang.NullPointerException: Annotation class not available: @NonNull
[ERROR] 	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
[ERROR] 	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
[ERROR] 	at io.quarkus.arc.processor.BeanProcessor.generateResources(BeanProcessor.java:319)
[ERROR] 	at io.quarkus.arc.deployment.ArcProcessor.generateResources(ArcProcessor.java:570)
[ERROR] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[ERROR] 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR] 	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[ERROR] 	at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:909)
[ERROR] 	... 6 more
[ERROR] Caused by: java.lang.NullPointerException: Annotation class not available: @NonNull
[ERROR] 	at java.base/java.util.Objects.requireNonNull(Objects.java:233)
[ERROR] 	at io.quarkus.arc.processor.AnnotationLiteralProcessor.create(AnnotationLiteralProcessor.java:87)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.collectInjectionPointAnnotations(BeanGenerator.java:1937)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.wrapCurrentInjectionPoint(BeanGenerator.java:1837)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.initConstructor(BeanGenerator.java:704)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.createConstructor(BeanGenerator.java:614)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.generateClassBean(BeanGenerator.java:355)
[ERROR] 	at io.quarkus.arc.processor.BeanGenerator.generate(BeanGenerator.java:122)
[ERROR] 	at io.quarkus.arc.processor.BeanProcessor$4.call(BeanProcessor.java:258)
[ERROR] 	at io.quarkus.arc.processor.BeanProcessor$4.call(BeanProcessor.java:254)
[ERROR] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
[ERROR] 	... 5 more
```

`mvn clean verify -Pquarkus-2.13.4` is fine though (switches from Quarkus 2.14.1 to 2.13.4 and jandex-plugin to the old coordinates and version) 

Please note that this reproducer is not using ECJ (unlike my actual project), hence the error at build time, not boot time.
If required I can provide a ECJ setup like in https://github.com/smallrye/jandex/issues/268.
I actually started with an ECJ setup, stripped it down and was then surprised by the different behavior.

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

17.0.5

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.14.1

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

I _think_ it's an issue in Jandex (cc @Ladicek), but I'm not entirely sure. Might also be related to Arc (cc @mkouba @manovotn )?

If I had to guess (wildly), I'd suppose that Jandex 3 is more strict about missing annotations.

Please note that there have been discussions about why `lombok.NonNull` has `CLASS` retention in the first place: https://github.com/projectlombok/lombok/issues/895
I do think the maintainer has a point though: https://github.com/projectlombok/lombok/issues/895#issuecomment-145653045

Changed files:
independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/resource/ResourceInjectionTest.java
independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/AnnotationLiteralProcessorTest.java

diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
index 3446f86ad3e..88e8e83a893 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/AnnotationLiteralProcessor.java
@@ -73,6 +73,13 @@ public ResultHandle process(BytecodeCreator bytecode, ClassOutput classOutput, C
      * the annotation members have the same values as the given annotation instance.
      * An implementation of the annotation type will be generated automatically.
      * <p>
+     * It is expected that given annotation instance is runtime-retained; an exception is thrown
+     * if not. Further, it is expected that the annotation type is available (that is,
+     * {@code annotationClass != null}); an exception is thrown if not. Callers that expect
+     * they always deal with runtime-retained annotations whose classes are available do not
+     * have to check (and will get decent errors for free), but callers that can possibly deal
+     * with class-retained annotations or missing annotation classes must check explicitly.
+     * <p>
      * We call the generated implementation of the annotation type an <em>annotation literal class</em>
      * and the instance produced by the generated bytecode an <em>annotation literal instance</em>,
      * even though the generated code doesn't use CDI's {@code AnnotationLiteral} anymore.
@@ -84,6 +91,10 @@ public ResultHandle process(BytecodeCreator bytecode, ClassOutput classOutput, C
      * @return an annotation literal instance result handle
      */
     public ResultHandle create(BytecodeCreator bytecode, ClassInfo annotationClass, AnnotationInstance annotationInstance) {
+        if (!annotationInstance.runtimeVisible()) {
+            throw new IllegalArgumentException("Annotation does not have @Retention(RUNTIME): " + annotationInstance);
+        }
+
         Objects.requireNonNull(annotationClass, "Annotation class not available: " + annotationInstance);
         AnnotationLiteralClassInfo literal = cache.getValue(new CacheKey(annotationClass));
 
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
index 698525b7650..b418627a6c0 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanGenerator.java
@@ -1932,9 +1932,14 @@ public static ResultHandle collectInjectionPointAnnotations(ClassOutput classOut
                 annotationHandle = constructor
                         .readStaticField(FieldDescriptor.of(InjectLiteral.class, "INSTANCE", InjectLiteral.class));
             } else {
-                // Create annotation literal if needed
-                ClassInfo literalClass = getClassByName(beanDeployment.getBeanArchiveIndex(), annotation.name());
-                annotationHandle = annotationLiterals.create(constructor, literalClass, annotation);
+                if (!annotation.runtimeVisible()) {
+                    continue;
+                }
+                ClassInfo annotationClass = getClassByName(beanDeployment.getBeanArchiveIndex(), annotation.name());
+                if (annotationClass == null) {
+                    continue;
+                }
+                annotationHandle = annotationLiterals.create(constructor, annotationClass, annotation);
             }
             constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, annotationsHandle,
                     annotationHandle);
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
index 68aaa2a2a97..2b64aadfa9d 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BuiltinBean.java
@@ -311,10 +311,16 @@ private static void generateBeanManagerBytecode(GeneratorContext ctx) {
     private static void generateResourceBytecode(GeneratorContext ctx) {
         ResultHandle annotations = ctx.constructor.newInstance(MethodDescriptor.ofConstructor(HashSet.class));
         // For a resource field the required qualifiers contain all annotations declared on the field
+        // (hence we need to check if they are runtime-retained and their classes are available)
         if (!ctx.injectionPoint.getRequiredQualifiers().isEmpty()) {
             for (AnnotationInstance annotation : ctx.injectionPoint.getRequiredQualifiers()) {
-                // Create annotation literal first
+                if (!annotation.runtimeVisible()) {
+                    continue;
+                }
                 ClassInfo annotationClass = getClassByName(ctx.beanDeployment.getBeanArchiveIndex(), annotation.name());
+                if (annotationClass == null) {
+                    continue;
+                }
                 ctx.constructor.invokeInterfaceMethod(MethodDescriptors.SET_ADD, annotations,
                         ctx.annotationLiterals.create(ctx.constructor, annotationClass, annotation));
             }
diff --git a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/AnnotationLiteralProcessorTest.java b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/AnnotationLiteralProcessorTest.java
index 48aa079f416..35ed23d0d5d 100644
--- a/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/AnnotationLiteralProcessorTest.java
+++ b/independent-projects/arc/processor/src/test/java/io/quarkus/arc/processor/AnnotationLiteralProcessorTest.java
@@ -1,6 +1,7 @@
 package io.quarkus.arc.processor;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
@@ -31,6 +32,10 @@ public enum SimpleEnum {
         BAZ,
     }
 
+    @Retention(RetentionPolicy.CLASS)
+    public @interface ClassRetainedAnnotation {
+    }
+
     @Retention(RetentionPolicy.RUNTIME)
     public @interface SimpleAnnotation {
         String value();
@@ -327,6 +332,35 @@ public void test() throws ReflectiveOperationException {
                 annotation.toString());
     }
 
+    @Test
+    public void missingAnnotationClass() {
+        AnnotationLiteralProcessor literals = new AnnotationLiteralProcessor(index, ignored -> true);
+
+        TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
+        try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className(generatedClass).build()) {
+            MethodCreator method = creator.getMethodCreator("hello", void.class);
+
+            assertThrows(NullPointerException.class, () -> {
+                literals.create(method, null, simpleAnnotationJandex("foobar"));
+            });
+        }
+    }
+
+    @Test
+    public void classRetainedAnnotation() {
+        AnnotationLiteralProcessor literals = new AnnotationLiteralProcessor(index, ignored -> true);
+
+        TestClassLoader cl = new TestClassLoader(getClass().getClassLoader());
+        try (ClassCreator creator = ClassCreator.builder().classOutput(cl).className(generatedClass).build()) {
+            MethodCreator method = creator.getMethodCreator("hello", void.class);
+
+            assertThrows(IllegalArgumentException.class, () -> {
+                literals.create(method, Index.singleClass(ClassRetainedAnnotation.class),
+                        AnnotationInstance.builder(ClassRetainedAnnotation.class).build());
+            });
+        }
+    }
+
     private static void verify(ComplexAnnotation ann) {
         assertEquals(true, ann.bool());
         assertEquals((byte) 1, ann.b());
diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/resource/ResourceInjectionTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/resource/ResourceInjectionTest.java
index 1658d1474e0..2bdb111422d 100644
--- a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/resource/ResourceInjectionTest.java
+++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/injection/resource/ResourceInjectionTest.java
@@ -36,12 +36,15 @@
 import javax.persistence.criteria.CriteriaUpdate;
 import javax.persistence.metamodel.Metamodel;
 
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.DotName;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 import io.quarkus.arc.Arc;
 import io.quarkus.arc.InstanceHandle;
 import io.quarkus.arc.ResourceReferenceProvider;
+import io.quarkus.arc.processor.InjectionPointsTransformer;
 import io.quarkus.arc.test.ArcTestContainer;
 
 public class ResourceInjectionTest {
@@ -50,7 +53,26 @@ public class ResourceInjectionTest {
     public ArcTestContainer container = ArcTestContainer.builder()
             .beanClasses(EEResourceField.class, JpaClient.class)
             .resourceReferenceProviders(EntityManagerProvider.class, DummyProvider.class)
-            .resourceAnnotations(PersistenceContext.class, Dummy.class).build();
+            .resourceAnnotations(PersistenceContext.class, Dummy.class)
+            .injectionPointsTransformers(new InjectionPointsTransformer() {
+                @Override
+                public boolean appliesTo(org.jboss.jandex.Type requiredType) {
+                    return requiredType.name().toString().equals(String.class.getName());
+                }
+
+                @Override
+                public void transform(TransformationContext transformationContext) {
+                    if (transformationContext.getAllAnnotations()
+                            .stream()
+                            .anyMatch(it -> it.name().toString().equals(Dummy.class.getName()))) {
+                        // pretend that the injection point has an annotation whose class is missing
+                        transformationContext.transform()
+                                .add(AnnotationInstance.builder(DotName.createSimple("missing.NonNull")).build())
+                                .done();
+                    }
+                }
+            })
+            .build();
 
     @Test
     public void testInjection() {