Repository: quarkusio/quarkus
Issue: Qute: VarArgs processing error with 1 element

### Describe the bug

@mkouba I'm submitting this based on your suggesting to file here in Quarkus to fix https://github.com/quarkiverse/quarkus-renarde/issues/102

I have a method on my User class which is a RenardeUser, etc.

I have a method called `hasUserRole(String... roleNames)` that I'd like to pass in a list of roles to check from my Qute templates in a single method call instead of multiple calls.

This works IF I pass in a list of roles like `{#if inject:user && inject:user.hasUserRole("webadmin", "admin", "support")}` but I get an exception during RUNTIME if I try to pass in a single value, ie. `{#if inject:user && inject:user.hasUserRole("admin")}`

The project compiles and starts up just fine but dies when I access the page with the single permission check.

```
2023-02-26 17:27:11,348 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (executor-thread-1) Request failed: java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.String; (java.lang.String and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
```



### Expected behavior

VarAgs with a single parameter should also work

### Actual behavior

Get the error:

```
2023-02-26 17:27:11,348 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (executor-thread-1) Request failed: java.lang.ClassCastException: class java.lang.String cannot be cast to class [Ljava.lang.String; (java.lang.String and [Ljava.lang.String; are in module java.base of loader 'bootstrap')
```


### How to Reproduce?

_No response_

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

_No response_

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.16.1.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

_No response_

Changed files:
extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/varargs/VarargsMethodTest.java
independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java

diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/varargs/VarargsMethodTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/varargs/VarargsMethodTest.java
index 0cf8f640c66..957bae5ac11 100644
--- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/varargs/VarargsMethodTest.java
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/varargs/VarargsMethodTest.java
@@ -2,7 +2,11 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import java.util.Arrays;
+
 import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
 
 import org.jboss.shrinkwrap.api.asset.StringAsset;
 import org.junit.jupiter.api.Test;
@@ -22,7 +26,13 @@ public class VarargsMethodTest {
                     .addAsResource(new StringAsset("{foo.getStr}:{foo.getStr('foo','bar','baz')}"),
                             "templates/foo.txt")
                     .addAsResource(new StringAsset("{fmt.format('a','b','c')}"),
-                            "templates/bar.txt"));
+                            "templates/bar.txt")
+                    .addAsResource(new StringAsset("{foo.getInt}:{foo.getInt(1)}:{foo.getInt(1,2,3)}"),
+                            "templates/baz.txt")
+                    .addAsResource(
+                            new StringAsset(
+                                    "{cdi:qux.getBoolean(1)}:{cdi:qux.getBoolean(1, true, false)}:{cdi:qux.getBoolean(2, false)}"),
+                            "templates/qux.txt"));;
 
     @Inject
     Template foo;
@@ -30,15 +40,24 @@ public class VarargsMethodTest {
     @Inject
     Template bar;
 
+    @Inject
+    Template baz;
+
+    @Inject
+    Template qux;
+
     @Test
     public void testVarargs() {
         assertEquals("ok:foo:bar baz", foo.data("foo", new Foo()).render());
         assertEquals(" b a", bar.data("fmt", "%2$2s%1$2s").render());
+        assertEquals("[]:[1]:[1, 2, 3]", baz.data("foo", new Foo()).render());
+        assertEquals("1 []:1 [true, false]:2 [false]", qux.render());
     }
 
     @TemplateData(properties = false)
     public static class Foo {
 
+        // This one is ignored
         String getStr(String foo) {
             return foo;
         }
@@ -52,6 +71,10 @@ public String getStr(String foo, String... args) {
             return foo + String.format(":%s %s", args[0], args[1]);
         }
 
+        public String getInt(int... args) {
+            return Arrays.toString(args);
+        }
+
         @TemplateExtension
         static String format(String fmt, Object... args) {
             return String.format(fmt, args);
@@ -59,4 +82,14 @@ static String format(String fmt, Object... args) {
 
     }
 
+    @Named
+    @Singleton
+    public static class Qux {
+
+        public String getBoolean(int first, Boolean... args) {
+            return first + " " + Arrays.toString(args);
+        }
+
+    }
+
 }
diff --git a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java
index fe00046e029..05e62a4f268 100644
--- a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java
+++ b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java
@@ -690,21 +690,41 @@ private void matchMethod(MethodInfo method, ClassInfo clazz, MethodCreator resol
         exception.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE_EXCEPTIONALLY, whenRet,
                 exception.getCaughtException());
 
+        ResultHandle[] realParamsHandle = paramsHandle;
+        if (isVarArgs(method)) {
+            // For varargs the number of results may be higher than the number of method params
+            // First get the regular params
+            realParamsHandle = new ResultHandle[parameterTypes.size()];
+            for (int i = 0; i < parameterTypes.size() - 1; i++) {
+                ResultHandle resultHandle = tryCatch.invokeVirtualMethod(Descriptors.EVALUATED_PARAMS_GET_RESULT,
+                        whenEvaluatedParams, tryCatch.load(i));
+                realParamsHandle[i] = resultHandle;
+            }
+            // Then we need to create an array for the last argument
+            Type varargsParam = parameterTypes.get(parameterTypes.size() - 1);
+            ResultHandle componentType = tryCatch
+                    .loadClass(varargsParam.asArrayType().component().name().toString());
+            ResultHandle varargsResults = tryCatch.invokeVirtualMethod(Descriptors.EVALUATED_PARAMS_GET_VARARGS_RESULTS,
+                    evaluatedParams, tryCatch.load(parameterTypes.size()), componentType);
+            // E.g. String, String, String -> String, String[]
+            realParamsHandle[parameterTypes.size() - 1] = varargsResults;
+        }
+
         if (Modifier.isStatic(method.flags())) {
             if (Modifier.isInterface(clazz.flags())) {
                 tryCatch.assign(invokeRet,
-                        tryCatch.invokeStaticInterfaceMethod(MethodDescriptor.of(method), paramsHandle));
+                        tryCatch.invokeStaticInterfaceMethod(MethodDescriptor.of(method), realParamsHandle));
             } else {
                 tryCatch.assign(invokeRet,
-                        tryCatch.invokeStaticMethod(MethodDescriptor.of(method), paramsHandle));
+                        tryCatch.invokeStaticMethod(MethodDescriptor.of(method), realParamsHandle));
             }
         } else {
             if (Modifier.isInterface(clazz.flags())) {
                 tryCatch.assign(invokeRet,
-                        tryCatch.invokeInterfaceMethod(MethodDescriptor.of(method), whenBase, paramsHandle));
+                        tryCatch.invokeInterfaceMethod(MethodDescriptor.of(method), whenBase, realParamsHandle));
             } else {
                 tryCatch.assign(invokeRet,
-                        tryCatch.invokeVirtualMethod(MethodDescriptor.of(method), whenBase, paramsHandle));
+                        tryCatch.invokeVirtualMethod(MethodDescriptor.of(method), whenBase, realParamsHandle));
             }
         }