Enforce that all modified templates in a delpackage file are from either a single file or the same file.
package com.google.template.soy.passes;
+import com.google.common.base.Joiner;
import com.google.template.soy.base.internal.IdGenerator;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
+import com.google.template.soy.exprtree.TemplateLiteralNode;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.TemplateBasicNode;
import com.google.template.soy.soytree.TemplateMetadata;
import com.google.template.soy.soytree.TemplateNode;
import com.google.template.soy.types.SoyType;
import com.google.template.soy.types.TemplateType;
+import java.util.Set;
+import java.util.TreeSet;
/** Checks modifiable templates. */
@RunAfter(ResolveExpressionTypesPass.class)
@@ -48,6 +52,11 @@ final class CheckModifiableTemplatesPass implements CompilerFilePass {
private static final SoyErrorKind BAD_VARIANT_TYPE =
SoyErrorKind.of("Expected variant of type {0}, found type {1}.");
+ private static final SoyErrorKind MODDING_MULTIPLE_FILES =
+ SoyErrorKind.of(
+ "A single Soy file can only modify templates from a single external namespace. "
+ + "Namespaces: {0}.");
+
private final ErrorReporter errorReporter;
CheckModifiableTemplatesPass(ErrorReporter errorReporter) {
@@ -56,6 +65,7 @@ final class CheckModifiableTemplatesPass implements CompilerFilePass {
@Override
public void run(SoyFileNode file, IdGenerator nodeIdGen) {
+ TreeSet<String> modifiedNamespaces = new TreeSet<>();
for (TemplateNode templateNode : file.getTemplates()) {
if (templateNode instanceof TemplateBasicNode) {
TemplateBasicNode templateBasicNode = (TemplateBasicNode) templateNode;
@@ -69,6 +79,7 @@ final class CheckModifiableTemplatesPass implements CompilerFilePass {
}
validateModifiesAttribute(templateBasicNode);
validateVariantExpr(templateBasicNode);
+ validateSingleFileIsModded(templateBasicNode, file, modifiedNamespaces);
}
}
}
@@ -103,4 +114,24 @@ final class CheckModifiableTemplatesPass implements CompilerFilePass {
variantType);
}
}
+
+ private void validateSingleFileIsModded(
+ TemplateBasicNode templateBasicNode, SoyFileNode file, Set<String> modifiedNamespaces) {
+ if (templateBasicNode.getModifiesExpr() == null) {
+ return;
+ }
+ TemplateLiteralNode literal =
+ (TemplateLiteralNode) templateBasicNode.getModifiesExpr().getRoot();
+ String namespace =
+ literal.getResolvedName().substring(0, literal.getResolvedName().lastIndexOf("."));
+ if (!namespace.equals(file.getNamespace())) {
+ modifiedNamespaces.add(namespace);
+ if (modifiedNamespaces.size() > 1) {
+ errorReporter.report(
+ templateBasicNode.getModifiesExpr().getSourceLocation(),
+ MODDING_MULTIPLE_FILES,
+ Joiner.on(", ").join(modifiedNamespaces));
+ }
+ }
+ }
}
