Skip to content

Commit 8b3918f

Browse files
committed
Qute type-safe fragments - fix validation for loop metadata and globals
- currently loop section metadata and global variables result in a build failure
1 parent 819a7eb commit 8b3918f

File tree

2 files changed

+45
-25
lines changed

2 files changed

+45
-25
lines changed

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java

+34-24
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ public void beforeParsing(ParserHelper parserHelper) {
672672
@BuildStep
673673
void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validations,
674674
List<TemplateExpressionMatchesBuildItem> expressionMatches,
675+
List<TemplateGlobalBuildItem> templateGlobals,
675676
BeanArchiveIndexBuildItem beanArchiveIndex,
676677
BuildProducer<ValidationErrorBuildItem> validationErrors) {
677678

@@ -682,6 +683,8 @@ void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validatio
682683
Map<DotName, AssignableInfo> assignableCache = new HashMap<>();
683684
String[] hintPrefixes = { LoopSectionHelper.Factory.HINT_PREFIX, WhenSectionHelper.Factory.HINT_PREFIX,
684685
SetSectionHelper.Factory.HINT_PREFIX };
686+
Set<String> globals = templateGlobals.stream().map(TemplateGlobalBuildItem::getName)
687+
.collect(Collectors.toUnmodifiableSet());
685688

686689
for (CheckedFragmentValidationBuildItem validation : validations) {
687690
Map<String, Type> paramNamesToTypes = new HashMap<>();
@@ -697,33 +700,40 @@ void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validatio
697700
}
698701

699702
for (Expression expression : validation.fragmentExpressions) {
700-
// Note that we ignore literals and expressions with no type info and expressions with a hint referencing an expression from inside the fragment
701-
if (expression.isLiteral()) {
703+
// Note that we ignore:
704+
// - literals,
705+
// - globals,
706+
// - expressions with no type info,
707+
// - loop metadata; e.g. |java.lang.Integer|<loop-metadata>
708+
// - expressions with a hint referencing an expression from inside the fragment
709+
if (expression.isLiteral() || globals.contains(expression.getParts().get(0).getName())) {
702710
continue;
703711
}
704-
if (expression.hasTypeInfo()) {
705-
Info info = TypeInfos.create(expression, index, null).get(0);
706-
if (info.isTypeInfo()) {
707-
// |org.acme.Foo|.name
708-
paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType);
709-
} else if (info.hasHints()) {
710-
// foo<set#123>.name
711-
hintLoop: for (String helperHint : info.asHintInfo().hints) {
712-
for (String prefix : hintPrefixes) {
713-
if (helperHint.startsWith(prefix)) {
714-
int generatedId = parseHintId(helperHint, prefix);
715-
Expression localExpression = findExpression(generatedId, validation.fragmentExpressions);
716-
if (localExpression == null) {
717-
Match match = matchResults.getMatch(generatedId);
718-
if (match == null) {
719-
throw new IllegalStateException(
720-
"Match result not found for expression [" + expression.toOriginalString()
721-
+ "] in: "
722-
+ validation.templateId);
723-
}
724-
paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type);
725-
break hintLoop;
712+
String typeInfo = expression.getParts().get(0).getTypeInfo();
713+
if (typeInfo == null || (typeInfo != null && typeInfo.endsWith(LoopSectionHelper.Factory.HINT_METADATA))) {
714+
continue;
715+
}
716+
Info info = TypeInfos.create(expression, index, null).get(0);
717+
if (info.isTypeInfo()) {
718+
// |org.acme.Foo|.name
719+
paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType);
720+
} else if (info.hasHints()) {
721+
// foo<set#123>.name
722+
hintLoop: for (String helperHint : info.asHintInfo().hints) {
723+
for (String prefix : hintPrefixes) {
724+
if (helperHint.startsWith(prefix)) {
725+
int generatedId = parseHintId(helperHint, prefix);
726+
Expression localExpression = findExpression(generatedId, validation.fragmentExpressions);
727+
if (localExpression == null) {
728+
Match match = matchResults.getMatch(generatedId);
729+
if (match == null) {
730+
throw new IllegalStateException(
731+
"Match result not found for expression [" + expression.toOriginalString()
732+
+ "] in: "
733+
+ validation.templateId);
726734
}
735+
paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type);
736+
break hintLoop;
727737
}
728738
}
729739
}

extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/typesafe/fragment/CheckedTemplateFragmentTest.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.junit.jupiter.api.extension.RegisterExtension;
1010

1111
import io.quarkus.qute.CheckedTemplate;
12+
import io.quarkus.qute.TemplateGlobal;
1213
import io.quarkus.qute.TemplateInstance;
1314
import io.quarkus.test.QuarkusUnitTest;
1415

@@ -20,21 +21,30 @@ public class CheckedTemplateFragmentTest {
2021
.addClasses(Templates.class, Item.class)
2122
.addAsResource(new StringAsset(
2223
"{#each items}{#fragment id='item'}{it.name}{#if it.name.length > 5} is a long name{/if}{/fragment}{/each}"),
23-
"templates/CheckedTemplateFragmentTest/items.html"));
24+
"templates/CheckedTemplateFragmentTest/items.html")
25+
.addAsResource(new StringAsset(
26+
"{#fragment id=foo}{#for i in bar}{i_count}. <{i}>{#if i_hasNext}, {/if}{/for}{/fragment}"),
27+
"templates/CheckedTemplateFragmentTest/foos.html"));
2428

2529
@Test
2630
public void testFragment() {
2731
assertEquals("Foo", Templates.items(null).getFragment("item").data("it", new Item("Foo")).render());
2832
assertEquals("Foo", Templates.items$item(new Item("Foo")).render());
2933
assertEquals("FooAndBar is a long name", Templates.items$item(new Item("FooAndBar")).render());
34+
assertEquals("1. <1>, 2. <2>, 3. <3>, 4. <4>, 5. <5>", Templates.foos$foo().render());
3035
}
3136

3237
@CheckedTemplate
3338
public static class Templates {
3439

40+
@TemplateGlobal
41+
static int bar = 5;
42+
3543
static native TemplateInstance items(List<Item> items);
3644

3745
static native TemplateInstance items$item(Item it);
46+
47+
static native TemplateInstance foos$foo();
3848
}
3949

4050
}

0 commit comments

Comments
 (0)