Improved error reporting in RuleContext:
- Unified duplicate code from RuleContext and RuleContext.Builder in a new class, RuleContext.ErrorReporter
- Added the BUILD file location to error/warning messages if the offending rule was created by a macro
--
MOS_MIGRATED_REVID=103934375
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 10395cb..b280086 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -137,6 +137,7 @@
private final ImmutableSet<String> features;
private final Map<String, Attribute> attributeMap;
private final BuildConfiguration hostConfiguration;
+ private final ErrorReporter reporter;
private ActionOwner actionOwner;
@@ -157,6 +158,7 @@
this.features = getEnabledFeatures();
this.attributeMap = attributeMap;
this.hostConfiguration = builder.hostConfiguration;
+ reporter = builder.reporter;
}
private ImmutableSet<String> getEnabledFeatures() {
@@ -391,7 +393,7 @@
*/
@Override
public void ruleError(String message) {
- reportError(rule.getLocation(), prefixRuleMessage(message));
+ reporter.ruleError(message);
}
/**
@@ -400,7 +402,7 @@
*/
@Override
public void ruleWarning(String message) {
- reportWarning(rule.getLocation(), prefixRuleMessage(message));
+ reporter.ruleWarning(message);
}
/**
@@ -412,11 +414,7 @@
*/
@Override
public void attributeError(String attrName, String message) {
- reportError(rule.getAttributeLocation(attrName),
- prefixAttributeMessage(Attribute.isImplicit(attrName)
- ? "(an implicit dependency)"
- : attrName,
- message));
+ reporter.attributeError(attrName, message);
}
/**
@@ -427,30 +425,7 @@
*/
@Override
public void attributeWarning(String attrName, String message) {
- reportWarning(rule.getAttributeLocation(attrName),
- prefixAttributeMessage(Attribute.isImplicit(attrName)
- ? "(an implicit dependency)"
- : attrName,
- message));
- }
-
- private String prefixAttributeMessage(String attrName, String message) {
- return "in " + attrName + " attribute of "
- + rule.getRuleClass() + " rule "
- + getLabel() + ": " + message;
- }
-
- private String prefixRuleMessage(String message) {
- return "in " + rule.getRuleClass() + " rule "
- + getLabel() + ": " + message;
- }
-
- private void reportError(Location location, String message) {
- getAnalysisEnvironment().getEventHandler().handle(Event.error(location, message));
- }
-
- private void reportWarning(Location location, String message) {
- getAnalysisEnvironment().getEventHandler().handle(Event.warn(location, message));
+ reporter.attributeWarning(attrName, message);
}
/**
@@ -1237,15 +1212,21 @@
return features;
}
+ @Override
+ public String toString() {
+ return "RuleContext(" + getLabel() + ", " + getConfiguration() + ")";
+ }
+
/**
* Builder class for a RuleContext.
*/
- public static final class Builder {
+ public static final class Builder implements RuleErrorConsumer {
private final AnalysisEnvironment env;
private final Rule rule;
private final BuildConfiguration configuration;
private final BuildConfiguration hostConfiguration;
private final PrerequisiteValidator prerequisiteValidator;
+ private final ErrorReporter reporter;
private ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap;
private Set<ConfigMatchingProvider> configConditions;
private NestedSet<PackageSpecification> visibility;
@@ -1258,6 +1239,7 @@
this.configuration = Preconditions.checkNotNull(configuration);
this.hostConfiguration = Preconditions.checkNotNull(hostConfiguration);
this.prerequisiteValidator = prerequisiteValidator;
+ reporter = new ErrorReporter(env, rule);
}
RuleContext build() {
@@ -1405,41 +1387,32 @@
return mapBuilder.build();
}
- private String prefixRuleMessage(String message) {
- return String.format("in %s rule %s: %s", rule.getRuleClass(), rule.getLabel(), message);
- }
-
- private String maskInternalAttributeNames(String name) {
- return Attribute.isImplicit(name) ? "(an implicit dependency)" : name;
- }
-
- private String prefixAttributeMessage(String attrName, String message) {
- return String.format("in %s attribute of %s rule %s: %s",
- maskInternalAttributeNames(attrName), rule.getRuleClass(), rule.getLabel(), message);
- }
-
public void reportError(Location location, String message) {
- env.getEventHandler().handle(Event.error(location, message));
+ reporter.reportError(location, message);
}
+ @Override
public void ruleError(String message) {
- reportError(rule.getLocation(), prefixRuleMessage(message));
+ reporter.ruleError(message);
}
+ @Override
public void attributeError(String attrName, String message) {
- reportError(rule.getAttributeLocation(attrName), prefixAttributeMessage(attrName, message));
+ reporter.attributeError(attrName, message);
}
public void reportWarning(Location location, String message) {
- env.getEventHandler().handle(Event.warn(location, message));
+ reporter.reportWarning(location, message);
}
+ @Override
public void ruleWarning(String message) {
- env.getEventHandler().handle(Event.warn(rule.getLocation(), prefixRuleMessage(message)));
+ reporter.ruleWarning(message);
}
+ @Override
public void attributeWarning(String attrName, String message) {
- reportWarning(rule.getAttributeLocation(attrName), prefixAttributeMessage(attrName, message));
+ reporter.attributeWarning(attrName, message);
}
private void reportBadPrerequisite(Attribute attribute, String targetKind,
@@ -1584,8 +1557,92 @@
}
}
- @Override
- public String toString() {
- return "RuleContext(" + getLabel() + ", " + getConfiguration() + ")";
+ /**
+ * Helper class for reporting errors and warnings.
+ */
+ public static final class ErrorReporter implements RuleErrorConsumer {
+ private final AnalysisEnvironment env;
+ private final Rule rule;
+
+ ErrorReporter(AnalysisEnvironment env, Rule rule) {
+ this.env = env;
+ this.rule = rule;
+ }
+
+ public void reportError(Location location, String message) {
+ env.getEventHandler().handle(Event.error(location, message));
+ }
+
+ @Override
+ public void ruleError(String message) {
+ reportError(rule.getLocation(), prefixRuleMessage(message));
+ }
+
+ @Override
+ public void attributeError(String attrName, String message) {
+ reportError(getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
+ }
+
+ public void reportWarning(Location location, String message) {
+ env.getEventHandler().handle(Event.warn(location, message));
+ }
+
+ @Override
+ public void ruleWarning(String message) {
+ env.getEventHandler().handle(Event.warn(rule.getLocation(), prefixRuleMessage(message)));
+ }
+
+ @Override
+ public void attributeWarning(String attrName, String message) {
+ reportWarning(getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
+ }
+
+ private String prefixRuleMessage(String message) {
+ return String.format("in %s rule %s: %s", rule.getRuleClass(), rule.getLabel(), message);
+ }
+
+ private String maskInternalAttributeNames(String name) {
+ return Attribute.isImplicit(name) ? "(an implicit dependency)" : name;
+ }
+
+ /**
+ * Prefixes the given message with details about the rule and appends details about the macro
+ * that created this rule, if applicable.
+ */
+ private String completeAttributeMessage(String attrName, String message) {
+ // Appends a note to the given message if the offending rule was created by a macro.
+ String macroMessageAppendix =
+ wasCreatedByMacro()
+ ? String.format(
+ ". Since this rule was created by the macro '%s', the error might have been "
+ + "caused by the macro implementation in %s",
+ getGeneratorFunction(), rule.getAttributeLocation(attrName))
+ : "";
+
+ return String.format("in %s attribute of %s rule %s: %s%s",
+ maskInternalAttributeNames(attrName), rule.getRuleClass(), rule.getLabel(), message,
+ macroMessageAppendix);
+ }
+
+ /**
+ * Returns the location of the specified attribute.
+ *
+ * <p>If the rule was created by a macro, we return the location from the BUILD file instead.
+ */
+ private Location getAttributeLocation(String attrName) {
+ // TODO(bazel-team): We assume that macros have set the rule location to the generator
+ // location. It would be better to read the "generator_location" attribute (which is currently
+ // not implemented).
+ return wasCreatedByMacro() ? rule.getLocation() : rule.getAttributeLocation(attrName);
+ }
+
+ private boolean wasCreatedByMacro() {
+ String generator = getGeneratorFunction();
+ return generator != null && !generator.isEmpty();
+ }
+
+ private String getGeneratorFunction() {
+ return (String) rule.getAttributeContainer().getAttr("generator_function");
+ }
}
}