blob: 6addd553bc0615f5bf6b06f72a48a4d022d90558 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_FIDL_FIDLC_SRC_ATTRIBUTE_SCHEMA_H_
#define TOOLS_FIDL_FIDLC_SRC_ATTRIBUTE_SCHEMA_H_
#include <lib/fit/function.h>
#include <zircon/assert.h>
#include "tools/fidl/fidlc/src/experimental_flags.h"
#include "tools/fidl/fidlc/src/flat_ast.h"
#include "tools/fidl/fidlc/src/reporter.h"
namespace fidlc {
class CompileStep;
// AttributeArgSchema defines a schema for a single argument in an attribute.
// This includes its type (string, uint64, etc.), whether it is optional or
// required, and (if applicable) a special-case rule for resolving its value.
class AttributeArgSchema {
public:
enum class Optionality : uint8_t {
kOptional,
kRequired,
};
enum class SpecialCase : uint8_t {
// Allows a uint32 literal or the special constant `NEXT` or `HEAD`.
kVersion,
};
explicit AttributeArgSchema(std::variant<ConstantValue::Kind, SpecialCase> type,
Optionality optionality = Optionality::kRequired)
: type_(type), optionality_(optionality) {
if (auto kind = std::get_if<ConstantValue::Kind>(&type_)) {
ZX_ASSERT(*kind != ConstantValue::Kind::kDocComment);
}
}
bool IsOptional() const { return optionality_ == Optionality::kOptional; }
void ResolveArg(CompileStep* step, Attribute* attribute, AttributeArg* arg,
bool literal_only) const;
private:
const std::variant<ConstantValue::Kind, SpecialCase> type_;
const Optionality optionality_;
};
// Use transparent comparator std::less<> to allow std::string_view lookups.
using AttributeSchemaMap = std::map<std::string, AttributeSchema, std::less<>>;
// AttributeSchema defines a schema for attributes. This includes the allowed
// placement (e.g. on a method, on a struct), names and schemas for arguments,
// and an optional constraint validator.
class AttributeSchema {
public:
// Note: Constraints get access to the fully compiled Element.
// This is one reason why VerifyAttributesStep is a separate step.
using Constraint = fit::function<bool(Reporter* reporter, ExperimentalFlagSet flags,
const Attribute* attribute, const Element* element)>;
// Constructs a new schema that allows any placement, takes no arguments, and
// has no constraint. Use the methods below to customize it.
AttributeSchema() = default;
// Chainable mutators for customizing the schema.
AttributeSchema& RestrictTo(std::set<Element::Kind> placements);
AttributeSchema& RestrictToAnonymousLayouts();
AttributeSchema& DisallowOnAnonymousLayouts();
AttributeSchema& AddArg(AttributeArgSchema arg_schema);
AttributeSchema& AddArg(std::string name, AttributeArgSchema arg_schema);
AttributeSchema& Constrain(AttributeSchema::Constraint constraint);
// Marks as use-early. See Kind::kUseEarly below.
AttributeSchema& UseEarly();
// Marks as compile-early. See Kind::kCompileEarly below.
AttributeSchema& CompileEarly();
// Marks as deprecated. See Kind::kDeprecated below.
AttributeSchema& Deprecate();
// Special schema for arbitrary user-defined attributes.
static const AttributeSchema kUserDefined;
// Returns true if this schema allows early compilations.
bool IsCompileEarly() const { return kind_ == Kind::kCompileEarly; }
// Resolves constants in the attribute's arguments. In the case of an
// anonymous argument like @foo("abc"), infers the argument's name too.
void ResolveArgs(CompileStep* step, Attribute* attribute) const;
// Validates the attribute's placement and constraints. Must call
// `ResolveArgs` first.
void Validate(Reporter* reporter, ExperimentalFlagSet flags, const Attribute* attribute,
const Element* element) const;
// Returns attribute schemas for FIDL's officially recognized attributes.
static AttributeSchemaMap OfficialAttributes();
private:
enum class Kind : uint8_t {
// Most attributes are validate-only. They do not participate in compilation
// apart from validation at the end (possibly with a custom constraint).
kValidateOnly,
// Some attributes influence compilation and are used early, before
// VerifyAttributesStep. These schemas do not allow a constraint, since
// constraint validation happens too late to be relied on.
kUseEarly,
// Some attributes get compiled and used early, before the main CompileStep.
// These schemas ensure all arguments are literals to avoid kicking off
// other compilations. Like kUseEarly, they do not allow a constraint.
kCompileEarly,
// Deprecated attributes produce an error if used.
kDeprecated,
// All unrecognized attributes are considered user-defined. They receive
// minimal validation since we don't know what to expect. They allow any
// placement, only support string and bool arguments (lacking a way to
// decide between int8, uint32, etc.), and have no constraint.
kUserDefined,
};
enum class Placement : uint8_t {
// Allowed anywhere.
kAnywhere,
// Only allowed in certain places specified by std::set<Element::Kind>.
kSpecific,
// Only allowed on anonymous layouts (i.e. layouts not directly bound to a
// type declaration as in `type Foo = struct { ... };`).
kAnonymousLayout,
// The opposite of kAnonymousLayout.
kAnythingButAnonymousLayout,
};
explicit AttributeSchema(Kind kind) : kind_(kind) {}
static void ResolveArgsWithoutSchema(CompileStep* compile_step, Attribute* attribute);
Kind kind_ = Kind::kValidateOnly;
Placement placement_ = Placement::kAnywhere;
std::set<Element::Kind> specific_placements_;
// Use transparent comparator std::less<> to allow std::string_view lookups.
std::map<std::string, AttributeArgSchema, std::less<>> arg_schemas_;
Constraint constraint_ = nullptr;
};
} // namespace fidlc
#endif // TOOLS_FIDL_FIDLC_SRC_ATTRIBUTE_SCHEMA_H_