use of com.google.template.soy.exprtree.ExprNode in project closure-templates by google.
the class ExpressionCompilerTest method createTemplateBody.
private String createTemplateBody(String soyExpr) {
// collect all varrefs and apply them as template parameters. This way all varrefs have a valid
// vardef
// TODO(lukes): this logic would be useful in a lot of tests and potentially unblock efforts to
// eliminate UNDECLARED vars
ExprNode expr = SoyFileParser.parseExpression(soyExpr, PluginResolver.nullResolver(Mode.ALLOW_UNDEFINED, ErrorReporter.exploding()), ErrorReporter.exploding());
final StringBuilder templateBody = new StringBuilder();
new AbstractExprNodeVisitor<Void>() {
final Set<String> names = new HashSet<>();
@Override
protected void visitVarRefNode(VarRefNode node) {
if (names.add(node.getName())) {
SoyType type = variables.get(node.getName()).soyType();
templateBody.append("{@param ").append(node.getName()).append(": ").append(type).append("}\n");
}
}
@Override
protected void visitExprNode(ExprNode node) {
if (node instanceof ParentExprNode) {
visitChildren((ParentExprNode) node);
}
}
}.exec(expr);
templateBody.append("{" + soyExpr + "}\n");
return templateBody.toString();
}
use of com.google.template.soy.exprtree.ExprNode in project closure-templates by google.
the class CheckProtoInitCallsPass method checkProto.
private void checkProto(ProtoInitNode node, SoyProtoType soyType) {
// Check that all proto required fields are present.
// TODO(user): Consider writing a soyProtoTypeImpl.getRequiredFields()
Set<String> givenParams = Sets.newHashSet(node.getParamNames());
for (FieldDescriptor field : soyType.getDescriptor().getFields()) {
if (field.isRequired() && !givenParams.contains(field.getName())) {
errorReporter.report(node.getSourceLocation(), MISSING_REQUIRED_FIELD, field.getName());
}
}
ImmutableSet<String> fields = soyType.getFieldNames();
for (int i = 0; i < node.numChildren(); i++) {
String fieldName = node.getParamNames().get(i);
ExprNode expr = node.getChild(i);
// Check that each arg exists in the proto.
if (!fields.contains(fieldName)) {
String extraErrorMessage = SoyErrors.getDidYouMeanMessageForProtoFields(fields, fieldName);
errorReporter.report(expr.getSourceLocation(), FIELD_DOES_NOT_EXIST, fieldName, extraErrorMessage);
continue;
}
// Check that the arg type is not null and that it matches the expected field type.
SoyType argType = expr.getType();
if (argType.equals(NullType.getInstance())) {
errorReporter.report(expr.getSourceLocation(), NULL_ARG_TYPE, fieldName);
}
SoyType fieldType = soyType.getFieldType(fieldName);
// Let args with unknown or error types pass
if (argType.equals(UnknownType.getInstance()) || argType.equals(ErrorType.getInstance())) {
return;
}
// Same for List<?>, for repeated fields
if (fieldType.getKind() == Kind.LIST && argType.getKind() == Kind.LIST) {
SoyType argElementType = ((ListType) argType).getElementType();
if (argElementType == null || argElementType.equals(UnknownType.getInstance())) {
return;
}
}
SoyType expectedType = SoyTypes.makeNullable(fieldType);
if (!expectedType.isAssignableFrom(argType)) {
errorReporter.report(expr.getSourceLocation(), ARGUMENT_TYPE_MISMATCH, fieldName, expectedType, argType);
}
}
}
use of com.google.template.soy.exprtree.ExprNode in project closure-templates by google.
the class GenIncrementalDomCodeVisitor method visitPrintNode.
/**
* Visit an {@link PrintNode}, with special cases for a variable being printed within an attribute
* declaration or as HTML content.
*
* <p>For attributes, if the variable is of kind attributes, it is invoked. Any other kind of
* variable is an error.
*
* <p>For HTML, if the variable is of kind HTML, it is invoked. Any other kind of variable gets
* wrapped in a call to {@code incrementalDom.text}, resulting in a Text node.
*/
@Override
protected void visitPrintNode(PrintNode node) {
ExprNode firstNode = node.getExpr().getRoot();
// TODO(b/71896143): directives are not handled correctly in the html_tag case.
switch(node.getHtmlContext()) {
case HTML_TAG:
if (tryGenerateFunctionCall(SoyType.Kind.ATTRIBUTES, firstNode) == GenerateFunctionCallResult.INDIRECT_NODE) {
// Inside an HTML tag, we cannot emit indirect calls (like incrementalDom.text); the only
// valid commands
// are idom incrementalDom.attr() calls (which direct ATTRIBUTES functions will call).
// If we can't emit the print node as a direct call, give up and report an error.
errorReporter.report(node.getSourceLocation(), PRINT_ATTR_INVALID_KIND, firstNode.getType().getKind());
}
break;
case HTML_PCDATA:
// But if we statically know that it's an HTML function, we can call it directly.
if (tryGenerateFunctionCall(SoyType.Kind.HTML, firstNode) == GenerateFunctionCallResult.INDIRECT_NODE) {
List<CodeChunk.WithValue> chunks = genJsExprsVisitor.exec(node);
CodeChunk.WithValue printCall = SOY_IDOM_PRINT.call(CodeChunkUtils.concatChunks(chunks));
JsCodeBuilder codeBuilder = getJsCodeBuilder();
codeBuilder.append(printCall);
}
break;
default:
super.visitPrintNode(node);
break;
}
}
use of com.google.template.soy.exprtree.ExprNode in project closure-templates by google.
the class TemplateAnalysis method isListExpressionEmpty.
// consider moving this to SoyTreeUtils or some similar place.
private static StaticAnalysisResult isListExpressionEmpty(ForNode node) {
Optional<RangeArgs> rangeArgs = RangeArgs.createFromNode(node);
if (rangeArgs.isPresent()) {
return isRangeExpressionEmpty(rangeArgs.get());
}
ExprNode expr = node.getExpr().getRoot();
if (expr instanceof ListLiteralNode) {
return ((ListLiteralNode) expr).numChildren() > 0 ? StaticAnalysisResult.FALSE : StaticAnalysisResult.TRUE;
}
return StaticAnalysisResult.UNKNOWN;
}
use of com.google.template.soy.exprtree.ExprNode in project closure-templates by google.
the class MsgIdFunctionPass method handleMsgIdCall.
/**
* Rewrites calls to msgId($msgVar) to either a static constant message id or a conditional if
* there is a fallback.
*/
private void handleMsgIdCall(FunctionNode fn, MsgFallbackGroupNode msgNode) {
ExprNode replacement;
long primaryMsgId = MsgUtils.computeMsgIdForDualFormat(msgNode.getChild(0));
if (msgNode.numChildren() == 1) {
// easy peasy
replacement = createMsgIdNode(primaryMsgId, fn.getSourceLocation());
} else {
long fallbackMsgId = MsgUtils.computeMsgIdForDualFormat(msgNode.getChild(1));
ConditionalOpNode condOpNode = new ConditionalOpNode(fn.getSourceLocation());
FunctionNode isPrimaryMsgInUse = new FunctionNode(BuiltinFunction.IS_PRIMARY_MSG_IN_USE, fn.getSourceLocation());
// We add the varRef, and the 2 message ids to the funcitonnode as arguments so they are
// trivial to access in the backends. This is a little hacky however since we never generate
// code for these things.
// We could formalize the hack by providing a way to stash arbitrary data in the FunctionNode
// and then just pack this up in a non-AST datastructure.
isPrimaryMsgInUse.addChild(fn.getChild(0));
isPrimaryMsgInUse.addChild(new IntegerNode(primaryMsgId, fn.getSourceLocation()));
isPrimaryMsgInUse.addChild(new IntegerNode(fallbackMsgId, fn.getSourceLocation()));
condOpNode.addChild(isPrimaryMsgInUse);
condOpNode.addChild(createMsgIdNode(primaryMsgId, fn.getSourceLocation()));
condOpNode.addChild(createMsgIdNode(fallbackMsgId, fn.getSourceLocation()));
replacement = condOpNode;
}
fn.getParent().replaceChild(fn, replacement);
}
Aggregations