quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.87k stars 2.71k forks source link

Qute resolving issue and error reporting #39641

Open FroMage opened 8 months ago

FroMage commented 8 months ago

Describe the bug

I'm using the following template:

{#include admin.html}
{#title}Upload program form{/title}

{#form uri:Admin.uploadProgram() enctype='multipart/form-data'}

<fieldset>
    <legend>Upload a JSON program</legend>
    <div class="control-group {#errorClassBootstrap 'program'/}">
        <label class="control-label" for="program">Program JSON file</label>
        <div class="controls">
            <input name="program" type="file" value="{flash:program}" title="Choose file to upload"/>
            <span class="help-inline">​{#error 'program'/}</span>
        </div>
    </div>
    <div class="form-actions">
        <a class="btn" href="{uri:Admin.index()}" title="Cancel and go back to the root">Cancel</a> ​
        <input type="submit" class="btn btn-primary" value="Upload program"
            title="Upload this JSON program"/>
    </div>
</fieldset>

{/form}
{/include}

And I'm getting the following error:

Caused by: io.quarkus.qute.TemplateException: Rendering error in template [tags/ifError] line 1: Key "it" not found in the map with keys [_args, nested-content] in expression {it}
    at io.quarkus.qute.TemplateException$Builder.build(TemplateException.java:169)
    at io.quarkus.qute.EvaluatorImpl.propertyNotFound(EvaluatorImpl.java:234)
    at io.quarkus.qute.EvaluatorImpl.resolve(EvaluatorImpl.java:204)
    at io.quarkus.qute.EvaluatorImpl.resolveReference(EvaluatorImpl.java:131)
    at io.quarkus.qute.EvaluatorImpl.evaluate(EvaluatorImpl.java:85)
    at io.quarkus.qute.ResolutionContextImpl.evaluate(ResolutionContextImpl.java:29)
    at io.quarkus.qute.EvaluatorImpl$EvalContextImpl.evaluate(EvaluatorImpl.java:387)
    at io.quarkus.qute.EvaluatedParams.evaluate(EvaluatedParams.java:26)
    at io.quarkiverse.renarde.util.Validation_ValueResolver.resolve(Unknown Source)
    at io.quarkus.qute.EvaluatorImpl.resolve(EvaluatorImpl.java:211)
    at io.quarkus.qute.EvaluatorImpl.resolveReference(EvaluatorImpl.java:131)
    at io.quarkus.qute.EvaluatorImpl.lambda$evaluate$0(EvaluatorImpl.java:78)
    at io.quarkus.qute.CompletedStage.thenCompose(CompletedStage.java:249)
    at io.quarkus.qute.EvaluatorImpl.evaluate(EvaluatorImpl.java:77)
    at io.quarkus.qute.ResolutionContextImpl.evaluate(ResolutionContextImpl.java:29)
    at io.quarkus.qute.IfSectionHelper$OperandCondition.evaluate(IfSectionHelper.java:249)
    at io.quarkus.qute.IfSectionHelper$SingletonContext.resolve(IfSectionHelper.java:64)
    at io.quarkus.qute.IfSectionHelper.resolve(IfSectionHelper.java:45)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:66)
    at io.quarkus.qute.Parser$1.resolve(Parser.java:1288)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.IncludeSectionHelper.lambda$resolve$1(IncludeSectionHelper.java:70)
    at io.quarkus.qute.CompletedStage.whenComplete(CompletedStage.java:285)
    at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:66)
    at io.quarkus.qute.Parser$1.resolve(Parser.java:1288)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.IncludeSectionHelper.lambda$resolve$1(IncludeSectionHelper.java:70)
    at io.quarkus.qute.CompletedStage.whenComplete(CompletedStage.java:285)
    at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:76)
    at io.quarkus.qute.UserTagSectionHelper.addAdditionalEvaluatedParams(UserTagSectionHelper.java:46)
    at io.quarkus.qute.IncludeSectionHelper.lambda$resolve$1(IncludeSectionHelper.java:57)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenCompleteStage(CompletableFuture.java:887)
    at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:2357)
    at java.base/java.util.concurrent.CompletableFuture.whenComplete(CompletableFuture.java:144)
    at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.InsertSectionHelper.resolve(InsertSectionHelper.java:20)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(SectionHelper.java:66)
    at io.quarkus.qute.Parser$1.resolve(Parser.java:1288)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.IncludeSectionHelper.resolve(IncludeSectionHelper.java:50)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    at io.quarkus.qute.SectionNode$SectionResolutionContextImpl.execute(SectionNode.java:228)
    at io.quarkus.qute.SectionHelper$SectionResolutionContext.execute(Selper.java:66)
    at io.quarkus.qute.Parser$1.resolve(Parser.java:1288)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:53)
    at io.quarkus.qute.SectionNode.resolve(SectionNode.java:58)
    ... 20 more

As you can see, I cannot guess what the error is, or even where it is.

Using a debugger, I can find out that the ifError call is within the {#errorClassBootstrap 'program'/} call:

tags/errorClassBootstrap.html:

{#ifError it}error{/ifError}

tags/ifError.html:

{#if inject:validation.hasError(it)}{nested-content}{/if}

Now, an error of the following form would have helped me find the issue without a debugger:

Error: Key "it" not found in the map 
tags/ifError.html#1: {#if inject:validation.hasError(it)}{nested-content}{/if}
tags/errorClassBootstrap.html#1: {#ifError it}error{/ifError}
Admin/uploadProgramForm.html#8: {#errorClassBootstrap 'program'/}

Now, I still can't understand why I'm getting an error, because I think I'm passing the default argument properly, and this used to work, so perhaps you can tell me what I'm doing wrong, but really I think we need to include better info in the Qute error messages when it comes to includes and displaying call stacks.

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

Quarkus version or git rev

No response

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

No response

Additional information

No response

quarkus-bot[bot] commented 8 months ago

/cc @mkouba (qute)

mkouba commented 8 months ago

Now, I still can't understand why I'm getting an error, because I think I'm passing the default argument properly, and this used to work, so perhaps you can tell me what I'm doing wrong,

I don't think that this ever worked but I might be wrong. The problem here is that {#ifError it} is translated to {#ifError it=it} and this parameter is intentionally ignored because it's used to signal that there is no "first unnamed parameter" used. I have to think about it for a while, I can't see an obvious fix right now. A workaround is to use an explicit param name; e.g. something like {#ifError val=it}error{/ifError} and {#if inject:validation.hasError(val)}{nested-content}{/if}.

but really I think we need to include better info in the Qute error messages when it comes to includes and displaying call stacks.

This won't be easy because a tag/included template currently does not know the context of the parent that included it. I mean, it should be doable... more or less... but it's not exactly a one-liner ;-).