`AbstractConfiguredTarget#getIndex` no longer crashes the build tool when given an unexported provider.
The crash can be produced with just a few lines of Starlark and BUILD file content.
`demo/bad.bzl`:
```
bad_rule = rule(
implementation = lambda ctx: ctx.attr.input[provider()],
attrs = {"input": attr.label(allow_single_file = True)},
)
```
`demo/BUILD`:
```
load(":bad.bzl", "bad_rule")
bad_rule(
name = "bad_target",
input = "example.in",
)
```
Running `bazel build demo:bad_target` will crash the build tool absent a change like this one.
PiperOrigin-RevId: 627744499
Change-Id: I28f4c8ba1a51f950d72a24f223447865a5a8c973
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
index def147d..03041a5 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
@@ -168,30 +168,21 @@
@Override
public final Object getIndex(StarlarkSemantics semantics, Object key) throws EvalException {
- if (!(key instanceof Provider constructor)) {
- throw Starlark.errorf(
- "Type Target only supports indexing by object constructors, got %s instead",
- Starlark.type(key));
- }
+ // Only call `getKey()` on unexported Providers to avoid crashing. Users can write:
+ // rule(implementation = lambda ctx: ctx.attr.input[provider()], attr = {"input": ...})
+ Provider constructor = selectExportedProvider(key, "index");
Object declaredProvider = get(constructor.getKey());
if (declaredProvider != null) {
return declaredProvider;
}
throw Starlark.errorf(
"%s%s doesn't contain declared provider '%s'",
- Starlark.repr(this),
- getRuleClassString().isEmpty() ? "" : " (rule '" + getRuleClassString() + "')",
- constructor.getPrintableName());
+ Starlark.repr(this), getRuleClassStringForError(), constructor.getPrintableName());
}
@Override
public boolean containsKey(StarlarkSemantics semantics, Object key) throws EvalException {
- if (!(key instanceof Provider)) {
- throw Starlark.errorf(
- "Type Target only supports querying by object constructors, got %s instead",
- Starlark.type(key));
- }
- return get(((Provider) key).getKey()) != null;
+ return get(selectExportedProvider(key, "query").getKey()) != null;
}
@Override
@@ -270,6 +261,29 @@
printer.append("<unknown target " + getLabel() + ">");
}
+ private String getRuleClassStringForError() {
+ return getRuleClassString().isEmpty() ? "" : " (rule '" + getRuleClassString() + "')";
+ }
+
+ /**
+ * Selects the provider identified by {@code key}, throwing a Starlark error if the key is not a
+ * provider or not exported.
+ */
+ private Provider selectExportedProvider(Object key, String operation) throws EvalException {
+ if (!(key instanceof Provider constructor)) {
+ throw Starlark.errorf(
+ "Type Target only supports %sing by object constructors, got %s instead",
+ operation, Starlark.type(key));
+ }
+ if (!constructor.isExported()) {
+ throw Starlark.errorf(
+ "%s%s only supports %sing by exported providers. Assign the provider a name "
+ + "in a top-level assignment statement.",
+ Starlark.repr(this), getRuleClassStringForError(), operation);
+ }
+ return constructor;
+ }
+
/**
* Returns a {@link Dict} of provider names to their values for a configured target, intended to
* be called from {@link #getProvidersDictForQuery}.
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
index 5f65f20..fb671ab 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkRuleFunctionsApi.java
@@ -60,7 +60,8 @@
@StarlarkMethod(
name = "provider",
doc =
- "Defines a provider symbol. The provider may be instantiated by calling it, or used"
+ "Defines a provider symbol. The result of this function must be stored in a global value."
+ + " The provider may be instantiated by calling it, or used"
+ " directly as a key for retrieving an instance of that provider from a target."
+ " Example:<br><pre class=\"language-python\">" //
+ "MyInfo = provider()\n"