Simplify `DependencyResolver` attribute resolution.
- Move the logic for resolving aspect/rule merged attributes into `AspectAwareAttributeMapper` (this is a dedicated class for this purpose)
- Make `AspectAwareAttributeMapper` more powerful (it now resolves *values*) with less code
- Simplify `DependencyResolver`'s code path
- Eliminate the duplicate logic for excluding "nodep" dependencies
- Reduce LoC by a nice little amount
Testing: since this is core logic, all changes have test coverage - if not directly then by the build logic that expects the invariants preserved here
PiperOrigin-RevId: 424938613
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
index 6e5e797..1386282 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapper.java
@@ -17,80 +17,78 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.AbstractAttributeMapper;
import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.DependencyFilter;
+import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Type;
+import java.util.Map;
import java.util.function.BiConsumer;
-import java.util.function.Consumer;
/**
- * An {@link AttributeMap} that supports attribute type queries on both a rule and its aspects and
- * attribute value queries on the rule.
+ * An {@link AttributeMap} that supports queries on both a rule and its aspects.
*
- * <p>An attribute type query is anything accessible from {@link Attribute} (i.e. anything about how
- * the attribute is integrated into the {@link com.google.devtools.build.lib.packages.RuleClass}).
- * An attribute value query is anything related to the actual value an attribute takes.
+ * <p>When both the rule and aspect declare the same attribute, the aspect's value takes precedence.
+ * This is because aspects expect access to the merged rule/attribute data (including possible
+ * aspect overrides) while rules evaluate before aspects are attached.
*
- * <p>For example, given {@code deps = [":adep"]}, checking that {@code deps} exists or that it's
- * type is {@link com.google.devtools.build.lib.packages.BuildType#LABEL_LIST} are type queries.
- * Checking that its value is explicitly set in the BUILD File or that its value {@code [":adep"]}
- * are value queries..
- *
- * <p>Value queries on aspect attributes trigger {@link UnsupportedOperationException}.
+ * <p>Note that public aspect attributes must be strings that inherit the values of the equivalent
+ * rule attributes. Only private (implicit) attributes can have different values.
*/
-class AspectAwareAttributeMapper implements AttributeMap {
- private final AttributeMap ruleAttributes;
+final class AspectAwareAttributeMapper extends AbstractAttributeMapper {
+ private final AbstractAttributeMapper ruleAttributes;
+ // Attribute name -> definition.
private final ImmutableMap<String, Attribute> aspectAttributes;
+ // Attribute name -> value. null values (which are valid) are excluded from this map. Use
+ // aspectAttributes to check attribute existence.
+ private final ImmutableMap<String, Object> aspectAttributeValues;
- public AspectAwareAttributeMapper(AttributeMap ruleAttributes,
+ public AspectAwareAttributeMapper(
+ Rule rule,
+ AbstractAttributeMapper ruleAttributes,
ImmutableMap<String, Attribute> aspectAttributes) {
+ super(rule);
this.ruleAttributes = ruleAttributes;
this.aspectAttributes = aspectAttributes;
- }
-
- @Override
- public String getName() {
- return ruleAttributes.getName();
- }
-
- @Override
- public String getRuleClassName() {
- return ruleAttributes.getRuleClassName();
- }
-
- @Override
- public Label getLabel() {
- return ruleAttributes.getLabel();
+ ImmutableMap.Builder<String, Object> valueBuilder = new ImmutableMap.Builder<>();
+ for (Map.Entry<String, Attribute> aspectAttribute : aspectAttributes.entrySet()) {
+ Attribute attribute = aspectAttribute.getValue();
+ Object defaultValue = attribute.getDefaultValue(rule);
+ Object attributeValue =
+ attribute
+ .getType()
+ .cast(
+ defaultValue instanceof ComputedDefault
+ ? ((ComputedDefault) defaultValue).getDefault(ruleAttributes)
+ : defaultValue);
+ if (attributeValue != null) {
+ valueBuilder.put(aspectAttribute.getKey(), attributeValue);
+ }
+ }
+ this.aspectAttributeValues = valueBuilder.buildOrThrow();
}
@Override
public <T> T get(String attributeName, Type<T> type) {
+ Attribute aspectAttribute = aspectAttributes.get(attributeName);
+ if (aspectAttribute != null) {
+ if (aspectAttribute.getType() != type) {
+ throw new IllegalArgumentException(
+ String.format(
+ "attribute %s has type %s, not expected type %s",
+ attributeName, aspectAttribute.getType(), type));
+ }
+ return type.cast(aspectAttributeValues.get(attributeName));
+ }
if (ruleAttributes.has(attributeName, type)) {
return ruleAttributes.get(attributeName, type);
- } else {
- Attribute attribute = aspectAttributes.get(attributeName);
- if (attribute == null) {
- throw new IllegalArgumentException(String.format(
+ }
+ throw new IllegalArgumentException(
+ String.format(
"no attribute '%s' in either %s or its aspects",
attributeName, ruleAttributes.getLabel()));
- } else if (attribute.getType() != type) {
- throw new IllegalArgumentException(String.format(
- "attribute %s has type %s, not expected type %s",
- attributeName, attribute.getType(), type));
- } else {
- throw new UnsupportedOperationException(
- String.format(
- "Attribute '%s' comes from an aspect. "
- + "Value retrieval for aspect attributes is not supported.",
- attributeName));
- }
- }
- }
-
- @Override
- public boolean isConfigurable(String attributeName) {
- return ruleAttributes.isConfigurable(attributeName);
}
@Override
@@ -103,23 +101,20 @@
@Override
public Type<?> getAttributeType(String attrName) {
- Type<?> type = ruleAttributes.getAttributeType(attrName);
- if (type != null) {
- return type;
- } else {
- Attribute attribute = aspectAttributes.get(attrName);
- return attribute != null ? attribute.getType() : null;
+ Attribute aspectAttribute = aspectAttributes.get(attrName);
+ if (aspectAttribute != null) {
+ return aspectAttribute.getType();
}
+ return ruleAttributes.getAttributeType(attrName);
}
@Override
public Attribute getAttributeDefinition(String attrName) {
- Attribute attribute = ruleAttributes.getAttributeDefinition(attrName);
- if (attribute != null) {
- return attribute;
- } else {
- return aspectAttributes.get(attrName);
+ Attribute aspectAttribute = aspectAttributes.get(attrName);
+ if (aspectAttribute != null) {
+ return aspectAttribute;
}
+ return ruleAttributes.getAttributeDefinition(attrName);
}
@Override
@@ -128,43 +123,26 @@
}
@Override
- public void visitAllLabels(BiConsumer<Attribute, Label> consumer) {
- throw new UnsupportedOperationException("rule + aspects label visition is not supported");
- }
-
- @Override
- public void visitLabels(Attribute attribute, Consumer<Label> consumer) {
- throw new UnsupportedOperationException("rule + aspects label visition is not supported");
- }
-
- @Override
public void visitLabels(DependencyFilter filter, BiConsumer<Attribute, Label> consumer) {
- throw new UnsupportedOperationException("rule + aspects label visition is not supported");
+ ImmutableList.Builder<Attribute> combined = ImmutableList.builder();
+ combined.addAll(rule.getAttributes());
+ aspectAttributes.values().forEach(combined::add);
+ visitLabels(combined.build(), filter, consumer);
}
@Override
- public String getPackageDefaultHdrsCheck() {
- return ruleAttributes.getPackageDefaultHdrsCheck();
- }
-
- @Override
- public boolean isPackageDefaultHdrsCheckSet() {
- return ruleAttributes.isPackageDefaultHdrsCheckSet();
- }
-
- @Override
- public Boolean getPackageDefaultTestOnly() {
- return ruleAttributes.getPackageDefaultTestOnly();
- }
-
- @Override
- public String getPackageDefaultDeprecation() {
- return ruleAttributes.getPackageDefaultDeprecation();
- }
-
- @Override
- public ImmutableList<String> getPackageDefaultCopts() {
- return ruleAttributes.getPackageDefaultCopts();
+ public <T> void visitLabels(Attribute attribute, Type<T> type, Type.LabelVisitor visitor) {
+ Attribute aspectAttr = aspectAttributes.get(attribute.getName());
+ if (aspectAttr != null) {
+ T value = type.cast(aspectAttributeValues.get(attribute.getName()));
+ if (value != null) { // null values are particularly possible for computed defaults.
+ type.visitLabels(visitor, value, attribute);
+ }
+ return; // If both the aspect and rule have this attribute, the aspect instance overrides.
+ }
+ if (ruleAttributes.has(attribute.getName(), type)) {
+ ruleAttributes.visitLabels(attribute, type, visitor);
+ }
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
index 816e8a0..4f0b687 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
@@ -42,7 +42,6 @@
import com.google.devtools.build.lib.packages.Aspect;
import com.google.devtools.build.lib.packages.AspectClass;
import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.AttributeTransitionData;
@@ -59,6 +58,7 @@
import com.google.devtools.build.lib.packages.RuleTransitionData;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.packages.Type.LabelVisitor;
import com.google.devtools.build.lib.util.OrderedSetMultimap;
import java.util.ArrayList;
import java.util.Collection;
@@ -591,11 +591,11 @@
Iterable<AttributeDependencyKind> attributeDependencyKinds,
OrderedSetMultimap<DependencyKind, Label> outgoingLabels,
Rule rule,
- ConfiguredAttributeMapper attributeMap,
+ ConfiguredAttributeMapper ruleAttributes,
BuildConfigurationValue ruleConfig) {
for (AttributeDependencyKind dependencyKind : attributeDependencyKinds) {
Attribute attribute = dependencyKind.getAttribute();
- if (!attribute.getCondition().apply(attributeMap)
+ if (!attribute.getCondition().apply(ruleAttributes)
// Not only is resolving CONFIG_SETTING_DEPS_ATTRIBUTE deps here wasteful, since the only
// place they're used is in ConfiguredTargetFunction.getConfigConditions, but it actually
// breaks trimming as shown by
@@ -604,70 +604,36 @@
// part of an unchosen select() branch.
|| attribute.getName().equals(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE)) {
continue;
+ } else if (attribute.isLateBound()) {
+ // Latebound attributes: compute their values here instead of from the rule / aspect attrs.
+ addLateBoundDefault(
+ resolveLateBoundDefault(rule, ruleAttributes, attribute, ruleConfig),
+ attribute.getType(),
+ (depLabel, ctx) -> outgoingLabels.put(dependencyKind, depLabel));
+ } else {
+ // Everything else: read the rule / aspect attribute settings.
+ AspectAwareAttributeMapper combinedAttributes =
+ new AspectAwareAttributeMapper(
+ rule,
+ ruleAttributes,
+ dependencyKind.getOwningAspect() != null
+ ? ImmutableMap.of(attribute.getName(), attribute)
+ : ImmutableMap.of());
+ combinedAttributes.visitLabels(
+ attribute, (depLabel) -> outgoingLabels.put(dependencyKind, depLabel));
}
-
- Type<?> type = attribute.getType();
- if (type == BuildType.OUTPUT
- || type == BuildType.OUTPUT_LIST
- || type == BuildType.NODEP_LABEL
- || type == BuildType.NODEP_LABEL_LIST) {
- // These types invoke visitLabels() so that they are reported in "bazel query" but do not
- // create a dependency. Maybe it's better to remove that, but then the labels() query
- // function would need to be rethought.
- continue;
- }
-
- resolveAttribute(
- attribute, type, dependencyKind, outgoingLabels, rule, attributeMap, ruleConfig);
}
}
- private <T> void resolveAttribute(
- Attribute attribute,
- Type<T> type,
- AttributeDependencyKind dependencyKind,
- OrderedSetMultimap<DependencyKind, Label> outgoingLabels,
- Rule rule,
- ConfiguredAttributeMapper attributeMap,
- BuildConfigurationValue ruleConfig) {
- T attributeValue = null;
- if (attribute.isImplicit()) {
- // Since the attributes that come from aspects do not appear in attributeMap, we have to get
- // their values from somewhere else. This incidentally means that aspects attributes are not
- // configurable. It would be nice if that wasn't the case, but we'd have to revamp how
- // attribute mapping works, which is a large chunk of work.
- if (dependencyKind.getOwningAspect() == null) {
- attributeValue = attributeMap.get(attribute.getName(), type);
- } else {
- Object defaultValue = attribute.getDefaultValue(rule);
- attributeValue =
- type.cast(
- defaultValue instanceof ComputedDefault
- ? ((ComputedDefault) defaultValue).getDefault(attributeMap)
- : defaultValue);
- }
- } else if (attribute.isLateBound()) {
- attributeValue =
- type.cast(resolveLateBoundDefault(rule, attributeMap, attribute, ruleConfig));
- } else if (attributeMap.has(attribute.getName())) {
- // This condition is false for aspect attributes that do not give rise to dependencies because
- // attributes that come from aspects do not appear in attributeMap (see the comment in the
- // case that handles implicit attributes).
- attributeValue = attributeMap.get(attribute.getName(), type);
+ private <T> void addLateBoundDefault(
+ @Nullable Object value, Type<T> type, LabelVisitor labelVisitor) {
+ if (value != null) {
+ type.visitLabels(labelVisitor, type.cast(value), /*context=*/ null);
}
-
- if (attributeValue == null) {
- return;
- }
-
- type.visitLabels(
- (depLabel, ctx) -> outgoingLabels.put(dependencyKind, depLabel),
- attributeValue,
- /*context=*/ null);
}
@VisibleForTesting(/* used to test LateBoundDefaults' default values */ )
- public static <FragmentT> Object resolveLateBoundDefault(
+ static <FragmentT> Object resolveLateBoundDefault(
Rule rule,
AttributeMap attributeMap,
Attribute attribute,
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 3788fea..d8414f7 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
@@ -209,7 +209,9 @@
this.ruleClassProvider = builder.ruleClassProvider;
this.targetMap = targetMap;
this.configConditions = builder.configConditions.asProviders();
- this.attributes = new AspectAwareAttributeMapper(attributes, builder.aspectAttributes);
+ this.attributes =
+ new AspectAwareAttributeMapper(
+ rule, (ConfiguredAttributeMapper) attributes, builder.aspectAttributes);
Set<String> allEnabledFeatures = new HashSet<>();
Set<String> allDisabledFeatures = new HashSet<>();
getAllFeatures(allEnabledFeatures, allDisabledFeatures);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
index 4c34b48..7243807 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AbstractAttributeMapper.java
@@ -32,7 +32,7 @@
public abstract class AbstractAttributeMapper implements AttributeMap {
private final RuleClass ruleClass;
private final Label ruleLabel;
- final Rule rule;
+ protected final Rule rule;
protected AbstractAttributeMapper(Rule rule) {
this.ruleClass = rule.getRuleClassObject();
@@ -181,11 +181,11 @@
}
@Override
- public final void visitLabels(DependencyFilter filter, BiConsumer<Attribute, Label> consumer) {
+ public void visitLabels(DependencyFilter filter, BiConsumer<Attribute, Label> consumer) {
visitLabels(ruleClass.getAttributes(), filter, consumer);
}
- private void visitLabels(
+ protected void visitLabels(
List<Attribute> attributes, DependencyFilter filter, BiConsumer<Attribute, Label> consumer) {
Type.LabelVisitor visitor =
(label, attribute) -> {
@@ -195,8 +195,12 @@
};
for (Attribute attribute : attributes) {
Type<?> type = attribute.getType();
- // TODO(bazel-team): clean up the typing / visitation interface so we don't have to
- // special-case these types.
+ // These types invoke visitLabels() so that they are reported in "bazel query" but do not
+ // create a dependency. Maybe it's better to remove that, but then the labels() query
+ // function would need to be rethought.
+ //
+ // TODO(bazel-team): clarify which variations of visitLabels include these types (for query)
+ // and which don't (for dependency analysis)
if (type != BuildType.OUTPUT
&& type != BuildType.OUTPUT_LIST
&& type != BuildType.NODEP_LABEL
@@ -208,7 +212,7 @@
}
/** Visits all labels reachable from the given attribute. */
- <T> void visitLabels(Attribute attribute, Type<T> type, Type.LabelVisitor visitor) {
+ public <T> void visitLabels(Attribute attribute, Type<T> type, Type.LabelVisitor visitor) {
T value = get(attribute.getName(), type);
if (value != null) { // null values are particularly possible for computed defaults.
type.visitLabels(visitor, value, attribute);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
index 5c8fa74..49d2156 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java
@@ -70,7 +70,7 @@
* {@link #visitAttribute}'s documentation. So we want to avoid that code path when possible.
*/
@Override
- <T> void visitLabels(Attribute attribute, Type<T> type, Type.LabelVisitor visitor) {
+ public <T> void visitLabels(Attribute attribute, Type<T> type, Type.LabelVisitor visitor) {
visitLabels(attribute, type, /*includeSelectKeys=*/ true, visitor);
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index 7b48285..2aa948e 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -64,7 +64,7 @@
import net.starlark.java.eval.Structure;
/**
- * Metadata of a rule attribute. Contains the attribute name and type, and an default value to be
+ * Metadata of a rule attribute. Contains the attribute name and type, and a default value to be
* used if none is provided in a rule declaration in a BUILD file. Attributes are immutable, and may
* be shared by more than one rule (for example, <code>foo_binary</code> and <code>foo_library
* </code> may share many attributes in common).
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapperTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapperTest.java
index be08836..dbb8341 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapperTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectAwareAttributeMapperTest.java
@@ -26,6 +26,7 @@
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
import com.google.devtools.build.lib.util.FileTypeSet;
+import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,12 +54,15 @@
RuleConfiguredTarget ct = (RuleConfiguredTarget) ctad.getConfiguredTarget();
rule = (Rule) ctad.getTarget();
- Attribute aspectAttr = new Attribute.Builder<Label>("fromaspect", BuildType.LABEL)
- .allowedFileTypes(FileTypeSet.ANY_FILE)
- .build();
+ Attribute aspectAttr =
+ new Attribute.Builder<Label>("$fromaspect", BuildType.LABEL)
+ .allowedFileTypes(FileTypeSet.ANY_FILE)
+ .defaultValue(Label.parseAbsoluteUnchecked("//:aspect_default_value"))
+ .build();
aspectAttributes = ImmutableMap.<String, Attribute>of(aspectAttr.getName(), aspectAttr);
mapper =
new AspectAwareAttributeMapper(
+ rule,
ConfiguredAttributeMapper.of(
rule,
ct.getConfigConditions(),
@@ -85,18 +89,18 @@
@Test
public void getAspectAttributeValue() throws Exception {
- assertThrows(
- UnsupportedOperationException.class, () -> mapper.get("fromaspect", BuildType.LABEL));
+ assertThat(mapper.get("$fromaspect", BuildType.LABEL))
+ .isEqualTo(Label.parseAbsoluteUnchecked("//:aspect_default_value"));
}
@Test
public void getAspectValueWrongType() throws Exception {
IllegalArgumentException e =
assertThrows(
- IllegalArgumentException.class, () -> mapper.get("fromaspect", BuildType.LABEL_LIST));
+ IllegalArgumentException.class, () -> mapper.get("$fromaspect", BuildType.LABEL_LIST));
assertThat(e)
.hasMessageThat()
- .isEqualTo("attribute fromaspect has type label, not expected type list(label)");
+ .isEqualTo("attribute $fromaspect has type label, not expected type list(label)");
}
@Test
@@ -111,31 +115,43 @@
@Test
public void isConfigurable() throws Exception {
assertThat(mapper.isConfigurable("linkstatic")).isTrue();
- assertThat(mapper.isConfigurable("fromaspect")).isFalse();
+ assertThat(mapper.isConfigurable("$fromaspect")).isFalse();
}
@Test
public void getAttributeNames() throws Exception {
- assertThat(mapper.getAttributeNames()).containsAtLeast("srcs", "linkstatic", "fromaspect");
+ assertThat(mapper.getAttributeNames()).containsAtLeast("srcs", "linkstatic", "$fromaspect");
}
@Test
public void getAttributeType() throws Exception {
assertThat(mapper.getAttributeType("srcs")).isEqualTo(BuildType.LABEL_LIST);
- assertThat(mapper.getAttributeType("fromaspect")).isEqualTo(BuildType.LABEL);
+ assertThat(mapper.getAttributeType("$fromaspect")).isEqualTo(BuildType.LABEL);
}
@Test
public void getAttributeDefinition() throws Exception {
assertThat(mapper.getAttributeDefinition("srcs").getName()).isEqualTo("srcs");
- assertThat(mapper.getAttributeDefinition("fromaspect").getName()).isEqualTo("fromaspect");
-
+ assertThat(mapper.getAttributeDefinition("$fromaspect").getName()).isEqualTo("$fromaspect");
}
@Test
public void has() throws Exception {
assertThat(mapper.has("srcs")).isTrue();
- assertThat(mapper.has("fromaspect")).isTrue();
+ assertThat(mapper.has("$fromaspect")).isTrue();
+ assertThat(mapper.has("noexist")).isFalse();
+ }
+
+ @Test
+ public void visitLabels() throws Exception {
+ ArrayList<Label> actual = new ArrayList<>();
+ mapper.visitAllLabels((attr, label) -> actual.add(label));
+ // From the aspect:
+ assertThat(actual).contains(Label.parseAbsoluteUnchecked("//:aspect_default_value"));
+ // From the rule:
+ assertThat(actual).contains(Label.parseAbsoluteUnchecked("//foo:a.cc"));
+ // The rule also has implicit toolchain targets we don't care about here.
+ // TODO(bazel-team): use a Starlark rule instead of a cc_binary to eliminate that variation.
}
}