Skip to content

Commit 12be197

Browse files
mkoubagsmet
authored andcommitted
Qute type-safe fragments - fix validation for loop metadata and globals
- currently loop section metadata and global variables result in a build failure (cherry picked from commit 8b3918f)
1 parent 202b733 commit 12be197

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
@@ -647,6 +647,7 @@ public void beforeParsing(ParserHelper parserHelper) {
647647
@BuildStep
648648
void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validations,
649649
List<TemplateExpressionMatchesBuildItem> expressionMatches,
650+
List<TemplateGlobalBuildItem> templateGlobals,
650651
BeanArchiveIndexBuildItem beanArchiveIndex,
651652
BuildProducer<ValidationErrorBuildItem> validationErrors) {
652653

@@ -657,6 +658,8 @@ void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validatio
657658
Map<DotName, AssignableInfo> assignableCache = new HashMap<>();
658659
String[] hintPrefixes = { LoopSectionHelper.Factory.HINT_PREFIX, WhenSectionHelper.Factory.HINT_PREFIX,
659660
SetSectionHelper.Factory.HINT_PREFIX };
661+
Set<String> globals = templateGlobals.stream().map(TemplateGlobalBuildItem::getName)
662+
.collect(Collectors.toUnmodifiableSet());
660663

661664
for (CheckedFragmentValidationBuildItem validation : validations) {
662665
Map<String, Type> paramNamesToTypes = new HashMap<>();
@@ -672,33 +675,40 @@ void validateCheckedFragments(List<CheckedFragmentValidationBuildItem> validatio
672675
}
673676

674677
for (Expression expression : validation.fragmentExpressions) {
675-
// Note that we ignore literals and expressions with no type info and expressions with a hint referencing an expression from inside the fragment
676-
if (expression.isLiteral()) {
678+
// Note that we ignore:
679+
// - literals,
680+
// - globals,
681+
// - expressions with no type info,
682+
// - loop metadata; e.g. |java.lang.Integer|<loop-metadata>
683+
// - expressions with a hint referencing an expression from inside the fragment
684+
if (expression.isLiteral() || globals.contains(expression.getParts().get(0).getName())) {
677685
continue;
678686
}
679-
if (expression.hasTypeInfo()) {
680-
Info info = TypeInfos.create(expression, index, null).get(0);
681-
if (info.isTypeInfo()) {
682-
// |org.acme.Foo|.name
683-
paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType);
684-
} else if (info.hasHints()) {
685-
// foo<set#123>.name
686-
hintLoop: for (String helperHint : info.asHintInfo().hints) {
687-
for (String prefix : hintPrefixes) {
688-
if (helperHint.startsWith(prefix)) {
689-
int generatedId = parseHintId(helperHint, prefix);
690-
Expression localExpression = findExpression(generatedId, validation.fragmentExpressions);
691-
if (localExpression == null) {
692-
Match match = matchResults.getMatch(generatedId);
693-
if (match == null) {
694-
throw new IllegalStateException(
695-
"Match result not found for expression [" + expression.toOriginalString()
696-
+ "] in: "
697-
+ validation.templateId);
698-
}
699-
paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type);
700-
break hintLoop;
687+
String typeInfo = expression.getParts().get(0).getTypeInfo();
688+
if (typeInfo == null || (typeInfo != null && typeInfo.endsWith(LoopSectionHelper.Factory.HINT_METADATA))) {
689+
continue;
690+
}
691+
Info info = TypeInfos.create(expression, index, null).get(0);
692+
if (info.isTypeInfo()) {
693+
// |org.acme.Foo|.name
694+
paramNamesToTypes.put(expression.getParts().get(0).getName(), info.asTypeInfo().resolvedType);
695+
} else if (info.hasHints()) {
696+
// foo<set#123>.name
697+
hintLoop: for (String helperHint : info.asHintInfo().hints) {
698+
for (String prefix : hintPrefixes) {
699+
if (helperHint.startsWith(prefix)) {
700+
int generatedId = parseHintId(helperHint, prefix);
701+
Expression localExpression = findExpression(generatedId, validation.fragmentExpressions);
702+
if (localExpression == null) {
703+
Match match = matchResults.getMatch(generatedId);
704+
if (match == null) {
705+
throw new IllegalStateException(
706+
"Match result not found for expression [" + expression.toOriginalString()
707+
+ "] in: "
708+
+ validation.templateId);
701709
}
710+
paramNamesToTypes.put(expression.getParts().get(0).getName(), match.type);
711+
break hintLoop;
702712
}
703713
}
704714
}

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)