use of com.google.template.soy.passes.FindIndirectParamsVisitor.IndirectParamsInfo in project closure-templates by google.
the class GenJsCodeVisitor method genParamsRecordType.
// -----------------------------------------------------------------------------------------------
// Helpers
/**
* Generate the JSDoc for the opt_data parameter.
*/
private String genParamsRecordType(TemplateNode node) {
Set<String> paramNames = new HashSet<>();
// Generate members for explicit params.
Map<String, String> record = new LinkedHashMap<>();
for (TemplateParam param : node.getParams()) {
JsType jsType = getJsType(param.type());
record.put(genParamAlias(param.name()), jsType.typeExprForRecordMember(/* isOptional= */
!param.isRequired()));
for (GoogRequire require : jsType.getGoogRequires()) {
jsCodeBuilder.addGoogRequire(require);
}
paramNames.add(param.name());
}
// Do the same for indirect params, if we can find them.
// If there's a conflict between the explicitly-declared type, and the type
// inferred from the indirect params, then the explicit type wins.
// Also note that indirect param types may not be inferrable if the target
// is not in the current compilation file set.
IndirectParamsInfo ipi = new FindIndirectParamsVisitor(templateRegistry).exec(node);
// types in JS allow additional undeclared fields to be present.
if (!ipi.mayHaveIndirectParamsInExternalCalls && !ipi.mayHaveIndirectParamsInExternalDelCalls) {
for (String indirectParamName : ipi.indirectParamTypes.keySet()) {
if (paramNames.contains(indirectParamName)) {
continue;
}
Collection<SoyType> paramTypes = ipi.indirectParamTypes.get(indirectParamName);
SoyType combinedType = SoyTypes.computeLowestCommonType(typeRegistry, paramTypes);
// Note that Union folds duplicate types and flattens unions, so if
// the combinedType is already a union this will do the right thing.
// TODO: detect cases where nullable is not needed (requires flow
// analysis to determine if the template is always called.)
SoyType indirectParamType = typeRegistry.getOrCreateUnionType(combinedType, NullType.getInstance());
JsType jsType = getJsType(indirectParamType);
// NOTE: we do not add goog.requires for indirect types. This is because it might introduce
// strict deps errors. This should be fine though since the transitive soy template that
// actually has the param will add them.
record.put(genParamAlias(indirectParamName), jsType.typeExprForRecordMember(/* isOptional= */
true));
}
}
StringBuilder sb = new StringBuilder();
sb.append("{\n * ");
Joiner.on(",\n * ").withKeyValueSeparator(": ").appendTo(sb, record);
// trailing comma in record is important in case the last record member is the
// unknown type
sb.append(",\n * }");
return sb.toString();
}
use of com.google.template.soy.passes.FindIndirectParamsVisitor.IndirectParamsInfo in project closure-templates by google.
the class GenerateParseInfoVisitor method visitTemplateNode.
@Override
protected void visitTemplateNode(TemplateNode node) {
// Don't generate anything for private or delegate templates.
if (node.getVisibility() != Visibility.PUBLIC || node instanceof TemplateDelegateNode) {
return;
}
// First build list of all transitive params (direct and indirect).
LinkedHashMap<String, TemplateParam> transitiveParamMap = Maps.newLinkedHashMap();
// Direct params.
for (TemplateParam param : node.getParams()) {
transitiveParamMap.put(param.name(), param);
}
// Indirect params.
IndirectParamsInfo indirectParamsInfo = new FindIndirectParamsVisitor(templateRegistry).exec(node);
for (TemplateParam param : indirectParamsInfo.indirectParams.values()) {
TemplateParam existingParam = transitiveParamMap.get(param.name());
if (existingParam == null) {
// Note: We don't list the description for indirect params.
transitiveParamMap.put(param.name(), param.copyEssential());
}
}
// Get info on injected params.
IjParamsInfo ijParamsInfo = new FindIjParamsVisitor(templateRegistry).exec(node);
// for IntelliJ
@SuppressWarnings("ConstantConditions") String upperUnderscoreName = convertToUpperUnderscore(node.getPartialTemplateName().substring(1));
String templateInfoClassName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, upperUnderscoreName) + "SoyTemplateInfo";
// ------ *SoyTemplateInfo class start. ------
ilb.appendLine();
ilb.appendLine();
appendJavadoc(ilb, Optional.fromNullable(node.getSoyDocDesc()).or(""), true, false);
ilb.appendLine("public static final class ", templateInfoClassName, " extends SoyTemplateInfo {");
ilb.increaseIndent();
// ------ Constants for template name. ------
ilb.appendLine();
ilb.appendLine("/** This template's full name. */");
ilb.appendLine("public static final String __NAME__ = \"", node.getTemplateName(), "\";");
ilb.appendLine("/** This template's partial name. */");
ilb.appendLine("public static final String __PARTIAL_NAME__ = \"", node.getPartialTemplateName(), "\";");
// ------ Param constants. ------
boolean hasSeenFirstDirectParam = false;
boolean hasSwitchedToIndirectParams = false;
for (TemplateParam param : transitiveParamMap.values()) {
if (param.desc() != null) {
// Direct param.
if (!hasSeenFirstDirectParam) {
ilb.appendLine();
hasSeenFirstDirectParam = true;
}
appendJavadoc(ilb, param.desc(), false, false);
} else {
// Indirect param.
if (!hasSwitchedToIndirectParams) {
ilb.appendLine();
ilb.appendLine("// Indirect params.");
hasSwitchedToIndirectParams = true;
}
// Get the list of all transitive callee names as they will appear in the generated
// Javadoc (possibly containing both partial and full names) and sort them before
// generating the Javadoc.
SortedSet<String> sortedJavadocCalleeNames = Sets.newTreeSet();
for (TemplateNode transitiveCallee : indirectParamsInfo.paramKeyToCalleesMultimap.get(param.name())) {
String javadocCalleeName = buildTemplateNameForJavadoc(node.getParent(), transitiveCallee);
sortedJavadocCalleeNames.add(javadocCalleeName);
}
// Generate the Javadoc.
StringBuilder javadocSb = new StringBuilder();
javadocSb.append("Listed by ");
boolean isFirst = true;
for (String javadocCalleeName : sortedJavadocCalleeNames) {
if (isFirst) {
isFirst = false;
} else {
javadocSb.append(", ");
}
javadocSb.append(javadocCalleeName);
}
javadocSb.append('.');
appendJavadoc(ilb, javadocSb.toString(), false, true);
}
// The actual param field.
ilb.appendLine("public static final String ", convertToUpperUnderscore(param.name()), " = \"", param.name(), "\";");
}
// ------ Constructor. ------
ilb.appendLine();
ilb.appendLine("private ", templateInfoClassName, "() {");
ilb.increaseIndent();
ilb.appendLine("super(");
ilb.increaseIndent(2);
ilb.appendLine("\"", node.getTemplateName(), "\",");
if (!transitiveParamMap.isEmpty()) {
ImmutableMap.Builder<String, String> entrySnippetPairs = ImmutableMap.builder();
for (TemplateParam param : transitiveParamMap.values()) {
entrySnippetPairs.put("\"" + param.name() + "\"", param.isRequired() ? "ParamRequisiteness.REQUIRED" : "ParamRequisiteness.OPTIONAL");
}
appendImmutableMap(ilb, "<String, ParamRequisiteness>", entrySnippetPairs.build());
ilb.appendLineEnd(",");
} else {
ilb.appendLine("ImmutableMap.<String, ParamRequisiteness>of(),");
}
appendIjParamSet(ilb, ijParamsInfo);
ilb.appendLineEnd(",");
ilb.appendLine("\"", node.getAutoescapeMode().getAttributeValue(), "\");");
ilb.decreaseIndent(2);
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Singleton instance and its getter. ------
ilb.appendLine();
ilb.appendLine("private static final ", templateInfoClassName, " __INSTANCE__ =");
ilb.increaseIndent(2);
ilb.appendLine("new ", templateInfoClassName, "();");
ilb.decreaseIndent(2);
ilb.appendLine();
ilb.appendLine("public static ", templateInfoClassName, " getInstance() {");
ilb.increaseIndent();
ilb.appendLine("return __INSTANCE__;");
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ *SoyTemplateInfo class end. ------
ilb.decreaseIndent();
ilb.appendLine("}");
// ------ Static field with instance of *SoyTemplateInfo class. ------
ilb.appendLine();
ilb.appendLine("/** Same as ", templateInfoClassName, ".getInstance(). */");
ilb.appendLine("public static final ", templateInfoClassName, " ", upperUnderscoreName, " =");
ilb.increaseIndent(2);
ilb.appendLine(templateInfoClassName, ".getInstance();");
ilb.decreaseIndent(2);
}
use of com.google.template.soy.passes.FindIndirectParamsVisitor.IndirectParamsInfo in project closure-templates by google.
the class CheckTemplateParamsVisitor method visitTemplateNode.
// -----------------------------------------------------------------------------------------------
// Implementations for specific nodes.
@Override
protected void visitTemplateNode(TemplateNode node) {
if (!node.couldHaveSyntaxVersionAtLeast(SyntaxVersion.V2_0)) {
return;
}
ListMultimap<String, SourceLocation> dataKeys = ArrayListMultimap.create();
for (VarRefNode varRefNode : SoyTreeUtils.getAllNodesOfType(node, VarRefNode.class)) {
if (varRefNode.isPossibleParam()) {
dataKeys.put(varRefNode.getName(), varRefNode.getSourceLocation());
}
}
IndirectParamsInfo ipi = new FindIndirectParamsVisitor(templateRegistry).exec(node);
Set<String> allParamNames = new HashSet<>();
List<TemplateParam> unusedParams = new ArrayList<>();
for (TemplateParam param : node.getAllParams()) {
allParamNames.add(param.name());
if (dataKeys.containsKey(param.name())) {
// Good: Declared and referenced in template. We remove these from dataKeys so
// that at the end of the for-loop, dataKeys will only contain the keys that are referenced
// but not declared in SoyDoc.
dataKeys.removeAll(param.name());
} else if (ipi.paramKeyToCalleesMultimap.containsKey(param.name()) || ipi.mayHaveIndirectParamsInExternalCalls || ipi.mayHaveIndirectParamsInExternalDelCalls) {
// Good: Declared in SoyDoc and either (a) used in a call that passes all data or (b) used
// in an external call or delcall that passes all data, which may need the param (we can't
// verify).
} else {
// Bad: Declared in SoyDoc but not referenced in template.
unusedParams.add(param);
}
}
// At this point, the only keys left in dataKeys are undeclared.
for (Entry<String, SourceLocation> undeclared : dataKeys.entries()) {
String extraErrorMessage = SoyErrors.getDidYouMeanMessage(allParamNames, undeclared.getKey());
errorReporter.report(undeclared.getValue(), UNDECLARED_DATA_KEY, undeclared.getKey(), extraErrorMessage);
}
// of the same delegate may need to use those params.
if (node instanceof TemplateBasicNode) {
for (TemplateParam unusedParam : unusedParams) {
errorReporter.report(unusedParam.nameLocation(), UNUSED_PARAM, unusedParam.name());
}
}
}
use of com.google.template.soy.passes.FindIndirectParamsVisitor.IndirectParamsInfo in project closure-templates by google.
the class FindIndirectParamsVisitorTest method testFindIndirectParams.
@Test
public void testFindIndirectParams() {
String fileContent1 = "{namespace alpha}\n" + "\n" + "/** @param? a0 @param? b3 */\n" + // 'b3' listed by alpha.zero
"{template .zero}\n" + " {call .zero data=\"all\" /}\n" + // recursive call should not cause 'a0' to be added
" {call .one data=\"all\" /}\n" + " {call .two /}\n" + " {call beta.zero /}\n" + " {call .five data=\"all\"}\n" + " {param a5: $a0 /}\n" + " {param b2: 88 /}\n" + " {/call}\n" + "{/template}\n" + "\n" + "/** @param? a1 */\n" + "{template .one}\n" + " {call .three data=\"all\" /}\n" + " {call .four /}\n" + " {$a1}\n" + "{/template}\n" + "\n" + "/** @param? a2 */\n" + "{template .two}\n" + " {$a2}\n" + "{/template}\n" + "\n" + "/** @param? a3 */\n" + "{template .three}\n" + " {call beta.one data=\"all\" /}\n" + " {$a3}\n" + "{/template}\n" + "\n" + "/** @param? a4 */\n" + "{template .four}\n" + " {call external.one /}\n" + " {$a4}\n" + "{/template}\n" + "\n" + "/** @param? a5 @param? b4 */\n" + // 'b4' listed by alpha.five
"{template .five}\n" + " {call beta.two data=\"all\" /}\n" + " {call beta.three data=\"all\" /}\n" + " {call beta.four data=\"all\" /}\n" + " {$b4}\n" + " {$a5}\n" + "{/template}\n" + "\n" + "/** @param? a6 */\n" + "{template .six}\n" + " {$a6}\n" + "{/template}\n";
String fileContent2 = "{namespace beta}\n" + "\n" + "/** @param? b0 */\n" + "{template .zero}\n" + " {$b0}\n" + "{/template}\n" + "\n" + "/** @param? b1 */\n" + "{template .one}\n" + " {call alpha.six data=\"all\" /}\n" + " {$b1}\n" + "{/template}\n" + "\n" + "/** @param? b2 */\n" + "{template .two}\n" + " {$b2}\n" + "{/template}\n" + "\n" + "/** @param? b3 */\n" + "{template .three}\n" + " {$b3}\n" + "{/template}\n" + "\n" + "/** @param? b4 */\n" + "{template .four}\n" + " {$b4}\n" + "{/template}\n";
ErrorReporter boom = ErrorReporter.exploding();
SoyFileSetNode soyTree = SoyFileSetParserBuilder.forFileContents(fileContent1, fileContent2).errorReporter(boom).parse().fileSet();
TemplateRegistry registry = new TemplateRegistry(soyTree, boom);
SoyFileNode a = soyTree.getChild(0);
TemplateNode a0 = a.getChild(0);
TemplateNode a1 = a.getChild(1);
// TemplateNode a2 = a.getChild(2);
TemplateNode a3 = a.getChild(3);
// TemplateNode a4 = a.getChild(4);
TemplateNode a5 = a.getChild(5);
TemplateNode a6 = a.getChild(6);
SoyFileNode b = soyTree.getChild(1);
// TemplateNode b0 = b.getChild(0);
TemplateNode b1 = b.getChild(1);
// TemplateNode b2 = b.getChild(2);
TemplateNode b3 = b.getChild(3);
TemplateNode b4 = b.getChild(4);
IndirectParamsInfo ipi = new FindIndirectParamsVisitor(registry).exec(a0);
assertThat(ipi.mayHaveIndirectParamsInExternalCalls).isFalse();
assertThat(ipi.mayHaveIndirectParamsInExternalDelCalls).isFalse();
Map<String, TemplateParam> ipMap = ipi.indirectParams;
assertThat(ipMap).hasSize(6);
assertThat(ipMap).doesNotContainKey("a0");
assertThat(ipMap).containsKey("a1");
assertThat(ipMap).doesNotContainKey("a2");
assertThat(ipMap).containsKey("a3");
assertThat(ipMap).doesNotContainKey("a4");
assertThat(ipMap).doesNotContainKey("a5");
assertThat(ipMap).containsKey("a6");
assertThat(ipMap).doesNotContainKey("b0");
assertThat(ipMap).containsKey("b1");
assertThat(ipMap).doesNotContainKey("b2");
assertThat(ipMap).containsKey("b3");
assertThat(ipMap).containsKey("b4");
Multimap<String, TemplateNode> pktcm = ipi.paramKeyToCalleesMultimap;
assertThat(pktcm).valuesForKey("a1").isEqualTo(ImmutableSet.of(a1));
assertThat(pktcm).valuesForKey("a3").isEqualTo(ImmutableSet.of(a3));
assertThat(pktcm).valuesForKey("a6").isEqualTo(ImmutableSet.of(a6));
assertThat(pktcm).valuesForKey("b1").isEqualTo(ImmutableSet.of(b1));
assertThat(pktcm).valuesForKey("b3").isEqualTo(ImmutableSet.of(b3));
// 'b4' listed by alpha.five
assertThat(pktcm).valuesForKey("b4").isEqualTo(ImmutableSet.of(a5, b4));
}
Aggregations