Repository: quarkusio/quarkus
Issue: Qute: type-safe expressions - fix the template extension method matching algorithm

For example, if there is an expression `{item.discountedPrice}` a template extension method like this:
```java
@TemplateExtension
static BigDecimal discountedPrice(Item item, int discount) {
  ...
}
```
matches the expression even if the number of parameters does not match. The expected behavior: build fails. It does not match the expression `{item.discountedPrice()}` though (the correct behavior).

Changed files:
extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationFailuresTest.java
extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java

diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
index 3ee1e50f8c0..b0ff69992ad 100644
--- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
+++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java
@@ -1141,11 +1141,15 @@ private static AnnotationTarget findTemplateExtensionMethod(Info info, ClassInfo
                 // Name does not match
                 continue;
             }
+            List<Type> parameters = extensionMethod.getMethod().parameters();
+            if (parameters.size() > 1 && !info.isVirtualMethod()) {
+                // If method accepts additional params the info must be a virtual method
+                continue;
+            }
             if (info.isVirtualMethod()) {
                 // For virtual method validate the number of params and attempt to validate the parameter types if available
                 VirtualMethodPart virtualMethod = info.part.asVirtualMethod();
                 boolean isVarArgs = ValueResolverGenerator.isVarArgs(extensionMethod.getMethod());
-                List<Type> parameters = extensionMethod.getMethod().parameters();
                 int lastParamIdx = parameters.size() - 1;
                 int realParamSize = parameters.size() - (TemplateExtension.ANY.equals(extensionMethod.getMatchName()) ? 2 : 1);
 
diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationFailuresTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationFailuresTest.java
index 9aa457b5a19..df3911f9018 100644
--- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationFailuresTest.java
+++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/ValidationFailuresTest.java
@@ -32,7 +32,9 @@ public class ValidationFailuresTest {
                             + "{movie.findServices(age,name)}"
                             // Name and number of params ok for extension method; the parameter type does not match
                             + "{movie.toNumber(age)}"
-                            + "{#each movie.mainCharacters}{it.boom(1)}{/}"),
+                            + "{#each movie.mainCharacters}{it.boom(1)}{/}"
+                            // Template extension method must accept one param
+                            + "{movie.toNumber}"),
                             "templates/movie.html"))
             .assertException(t -> {
                 Throwable e = t;
@@ -45,7 +47,7 @@ public class ValidationFailuresTest {
                     e = e.getCause();
                 }
                 assertNotNull(te);
-                assertTrue(te.getMessage().contains("Found template problems (7)"), te.getMessage());
+                assertTrue(te.getMessage().contains("Found template problems (8)"), te.getMessage());
                 assertTrue(te.getMessage().contains("movie.foo"), te.getMessage());
                 assertTrue(te.getMessage().contains("movie.getName('foo')"), te.getMessage());
                 assertTrue(te.getMessage().contains("movie.findService(age)"), te.getMessage());
@@ -53,6 +55,7 @@ public class ValidationFailuresTest {
                 assertTrue(te.getMessage().contains("movie.findServices(age,name)"), te.getMessage());
                 assertTrue(te.getMessage().contains("movie.toNumber(age)"), te.getMessage());
                 assertTrue(te.getMessage().contains("it.boom(1)"), te.getMessage());
+                assertTrue(te.getMessage().contains("movie.toNumber"), te.getMessage());
             });
 
     @Test