| // Copyright 2022 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.packages; | |
| import com.google.common.base.Preconditions; | |
| import java.util.Map; | |
| import javax.annotation.Nullable; | |
| import net.starlark.java.syntax.Location; | |
| /** | |
| * A struct-like object supporting a custom no-such-field error message. | |
| * | |
| * <p>This is used for certain special structs like `ctx.attr`. | |
| */ | |
| // TODO(bazel-team): It's questionable whether the use cases for this class should be part of the | |
| // class hierarchy of StructImpl at all. They really only need to have fields and custom error | |
| // messages (which are features of the simpler Structure class), not `+` concatenation, | |
| // proto/json encoding, or provider functionality. | |
| public final class StarlarkInfoWithMessage extends StarlarkInfoNoSchema { | |
| // A format string with one %s placeholder for the missing field name. | |
| // TODO(adonovan): make the provider determine the error message | |
| // (but: this has implications for struct+struct, the equivalence | |
| // relation, and other observable behaviors). | |
| // Perhaps it should be a property of the StarlarkInfo instance, but | |
| // defined by a subclass? | |
| private final String unknownFieldError; | |
| private StarlarkInfoWithMessage( | |
| Provider provider, | |
| Map<String, Object> values, | |
| @Nullable Location loc, | |
| String unknownFieldError) { | |
| super(provider, values, loc); | |
| this.unknownFieldError = unknownFieldError; | |
| } | |
| /** Returns the per-instance error message, if specified, or the provider's message otherwise. */ | |
| @Override | |
| public String getErrorMessageForUnknownField(String name) { | |
| return String.format(unknownFieldError, name) + allAttributesSuffix(); | |
| } | |
| /** | |
| * Creates a schemaless provider instance with the given provider type, field values, and | |
| * unknown-field error message. | |
| * | |
| * <p>This is used to create structs for special purposes, such as {@code ctx.attr} and the {@code | |
| * native} module. The creation location will be {@link Location#BUILTIN}. | |
| * | |
| * <p>{@code unknownFieldError} is a string format, as for {@link | |
| * Provider#getErrorMessageFormatForUnknownField}. | |
| * | |
| * @deprecated Do not use this method. Instead, create a new subclass of {@link BuiltinProvider} | |
| * with the desired error message format, and create a corresponding {@link NativeInfo} | |
| * subclass. | |
| */ | |
| // TODO(bazel-team): Eliminate the need for this class by migrating the special structs that need | |
| // a custom error message to inherit from Structure rather than from the provider machinery. If | |
| // there are any use cases where the object is an actual native provider, migrate them to their | |
| // own subclasses of BuiltinProvider. | |
| // | |
| // However, either of these migrations could cause obscure user-visible changes in: | |
| // - the type name ("struct" vs something else) | |
| // - equality (`==`) semantics | |
| // - the availability of the ".to_proto" and ".to_json" methods | |
| // - the availability of the struct concatenation operator (`+`) and the type of its result. | |
| // (Today, concatenation of structs with different error messages is allowed, and the result | |
| // uses the error message of the left-hand side. But maybe this should be disallowed, or maybe | |
| // it should always return a plain struct, or maybe the operator should be abolished | |
| // altogether.) | |
| @Deprecated | |
| public static StarlarkInfo createWithCustomMessage( | |
| Provider provider, Map<String, Object> values, String unknownFieldError) { | |
| Preconditions.checkNotNull(unknownFieldError); | |
| return new StarlarkInfoWithMessage(provider, values, Location.BUILTIN, unknownFieldError); | |
| } | |
| } |