Repository: quarkusio/quarkus
Issue: Qute does not tell me which file has an error

### Describe the bug

I'm porting some templates from a Play 1 app to Quarkus/Qute and I get the following exception:

```
2021-10-06 16:10:49,740 ERROR [io.qua.dep.dev.IsolatedDevModeMain] (vert.x-worker-thread-0) Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.qute.deployment.QuteProcessor#analyzeTemplates threw an exception: java.lang.IllegalArgumentException: Not a virtual method: )(/
	at io.quarkus.qute.Expressions.parseVirtualMethodParams(Expressions.java:39)
	at io.quarkus.qute.Parser.createPart(Parser.java:761)
	at io.quarkus.qute.Parser.parseExpression(Parser.java:743)
	at io.quarkus.qute.Parser.createPart(Parser.java:764)
	at io.quarkus.qute.Parser.parseExpression(Parser.java:743)
	at io.quarkus.qute.Parser.apply(Parser.java:817)
	at io.quarkus.qute.Parser.flushTag(Parser.java:486)
	at io.quarkus.qute.Parser.tag(Parser.java:293)
	at io.quarkus.qute.Parser.processCharacter(Parser.java:200)
	at io.quarkus.qute.Parser.parse(Parser.java:132)
	at io.quarkus.qute.EngineImpl.load(EngineImpl.java:138)
```

And it doesn't tell me which file it is, and I have dozens of them, so no idea where the error is :(

Can we make sure we always display the error location?

### Expected behavior

_No response_

### Actual behavior

_No response_

### 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.3.0.Final

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

_No response_

### Additional information

_No response_

Changed files:
independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java, independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java, independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java

diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java
index 8b4b17f7da7..c4b30c66064 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Expressions.java
@@ -1,5 +1,6 @@
 package io.quarkus.qute;
 
+import io.quarkus.qute.TemplateNode.Origin;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -30,20 +31,20 @@ public static String parseVirtualMethodName(String value) {
         return value.substring(0, start);
     }
 
-    public static List<String> parseVirtualMethodParams(String value) {
+    public static List<String> parseVirtualMethodParams(String value, Origin origin, String exprValue) {
         int start = value.indexOf(LEFT_BRACKET);
         if (start != -1 && value.endsWith(RIGHT_BRACKET)) {
             String params = value.substring(start + 1, value.length() - 1);
             return splitParts(params, PARAMS_SPLIT_CONFIG);
         }
-        throw new IllegalArgumentException("Not a virtual method: " + value);
+        throw Parser.parserError("invalid virtual method in {" + exprValue + "}", origin);
     }
 
-    public static String parseBracketContent(String value) {
+    public static String parseBracketContent(String value, Origin origin, String exprValue) {
         if (value.endsWith(SQUARE_RIGHT_BRACKET)) {
             return value.substring(1, value.length() - 1);
         }
-        throw new IllegalArgumentException("Not a bracket notation expression: " + value);
+        throw Parser.parserError("invalid bracket notation expression in {" + exprValue + "}", origin);
     }
 
     public static String buildVirtualMethodSignature(String name, List<String> params) {
diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
index 085a5b73e94..b03a0c8aac8 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
@@ -492,7 +492,7 @@ private TemplateException parserError(String message) {
         return parserError(message, origin(0));
     }
 
-    private static TemplateException parserError(String message, Origin origin) {
+    static TemplateException parserError(String message, Origin origin) {
         StringBuilder builder = new StringBuilder("Parser error");
         if (!origin.getTemplateId().equals(origin.getTemplateGeneratedId())) {
             builder.append(" in template [").append(origin.getTemplateId()).append("]");
@@ -740,7 +740,7 @@ static ExpressionImpl parseExpression(Supplier<Integer> idGenerator, String valu
         Part first = null;
         Iterator<String> strPartsIterator = strParts.iterator();
         while (strPartsIterator.hasNext()) {
-            Part part = createPart(idGenerator, namespace, first, strPartsIterator, scope, origin);
+            Part part = createPart(idGenerator, namespace, first, strPartsIterator, scope, origin, value);
             if (!isValidIdentifier(part.getName())) {
                 throw parserError("invalid identifier found {" + value + "}", origin);
             }
@@ -753,12 +753,11 @@ static ExpressionImpl parseExpression(Supplier<Integer> idGenerator, String valu
     }
 
     private static Part createPart(Supplier<Integer> idGenerator, String namespace, Part first,
-            Iterator<String> strPartsIterator, Scope scope,
-            Origin origin) {
+            Iterator<String> strPartsIterator, Scope scope, Origin origin, String exprValue) {
         String value = strPartsIterator.next();
         if (Expressions.isVirtualMethod(value)) {
             String name = Expressions.parseVirtualMethodName(value);
-            List<String> strParams = new ArrayList<>(Expressions.parseVirtualMethodParams(value));
+            List<String> strParams = new ArrayList<>(Expressions.parseVirtualMethodParams(value, origin, exprValue));
             List<Expression> params = new ArrayList<>(strParams.size());
             for (String strParam : strParams) {
                 params.add(parseExpression(idGenerator, strParam.trim(), scope, origin));
@@ -769,7 +768,7 @@ private static Part createPart(Supplier<Integer> idGenerator, String namespace,
         }
         // Try to parse the literal for bracket notation
         if (Expressions.isBracketNotation(value)) {
-            value = Expressions.parseBracketContent(value);
+            value = Expressions.parseBracketContent(value, origin, exprValue);
             Object literal = LiteralSupport.getLiteralValue(value);
             if (literal != null && !Results.isNotFound(literal)) {
                 value = literal.toString();
diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
index 4e2361c6963..6a978dca6ac 100644
--- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
+++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
@@ -355,6 +355,18 @@ public void testInvalidNamespaceExpression() {
                 "Parser error on line 1: empty expression found {data:}", 1);
     }
 
+    @Test
+    public void testInvalidVirtualMethod() {
+        assertParserError("{foo.baz()(}",
+                "Parser error on line 1: invalid virtual method in {foo.baz()(}", 1);
+    }
+
+    @Test
+    public void testInvalidBracket() {
+        assertParserError("{foo.baz[}",
+                "Parser error on line 1: invalid bracket notation expression in {foo.baz[}", 1);
+    }
+
     public static class Foo {
 
         public List<Item> getItems() {