diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h index c4d5858dd636..122caa600531 100644 --- a/gcc/rust/ast/rust-ast.h +++ b/gcc/rust/ast/rust-ast.h @@ -1139,6 +1139,26 @@ class Stmt : public Node class Item : public Stmt { public: + enum class Kind + { + Static = 0, + Constant, + TypeAlias, + Function, + UseDeclaration, + ExternBlock, + ExternCrate, + Struct, + Union, + Enum, + EnumItem, + Trait, + Impl, + Module, + MacroRules, + MacroInvocation, + }; + // Unique pointer custom clone function std::unique_ptr clone_item () const { @@ -1151,6 +1171,8 @@ class Item : public Stmt add_crate_name (std::vector &names ATTRIBUTE_UNUSED) const {} + virtual Kind get_item_kind () const = 0; + Stmt::Kind get_stmt_kind () final { return Stmt::Kind::Item; } // FIXME: ARTHUR: Is it okay to have removed that final? Is it *required* diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h index 44963ba386e7..b26b5e63c9e4 100644 --- a/gcc/rust/ast/rust-item.h +++ b/gcc/rust/ast/rust-item.h @@ -732,6 +732,8 @@ class Module : public VisItem UNLOADED, }; + Item::Kind get_item_kind () const { return Item::Kind::Module; } + Identifier get_name () const { return module_name; } AST::Kind get_ast_kind () const override { return AST::Kind::MODULE; } @@ -898,6 +900,8 @@ class ExternCrate : public VisItem "extern crate foo" "extern crate std as cool_std" */ public: + Item::Kind get_item_kind () const { return Item::Kind::ExternCrate; } + std::string as_string () const override; // Returns whether extern crate declaration has an as clause. @@ -1227,6 +1231,8 @@ class UseDeclaration : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::UseDeclaration; } + std::string as_string () const override; UseDeclaration (std::unique_ptr use_tree, Visibility visibility, @@ -1302,6 +1308,8 @@ class Function : public VisItem, public AssociatedItem bool is_default; public: + Item::Kind get_item_kind () const { return Item::Kind::Function; } + std::string as_string () const override; // Returns whether function has generic parameters. @@ -1452,6 +1460,8 @@ class TypeAlias : public VisItem, public AssociatedItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::TypeAlias; } + std::string as_string () const override; // Returns whether type alias has generic parameters. @@ -1577,6 +1587,8 @@ class Struct : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Struct; } + // Returns whether struct has generic parameters. bool has_generics () const { return !generic_params.empty (); } @@ -1946,6 +1958,8 @@ class EnumItem : public VisItem variant_name (std::move (variant_name)), locus (locus) {} + Item::Kind get_item_kind () const { return Item::Kind::EnumItem; } + // Unique pointer custom clone function std::unique_ptr clone_enum_item () const { @@ -2115,6 +2129,8 @@ class Enum : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Enum; } + std::string as_string () const override; // Returns whether "enum" has generic parameters. @@ -2230,6 +2246,8 @@ class Union : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Union; } + std::string as_string () const override; // Returns whether union has generic params. @@ -2327,6 +2345,8 @@ class ConstantItem : public VisItem, public AssociatedItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Constant; } + std::string as_string () const override; ConstantItem (std::string ident, Visibility vis, std::unique_ptr type, @@ -2442,6 +2462,8 @@ class StaticItem : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Static; } + std::string as_string () const override; StaticItem (Identifier name, bool is_mut, std::unique_ptr type, @@ -2740,6 +2762,8 @@ class Trait : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Trait; } + std::string as_string () const override; // Returns whether trait has generic parameters. @@ -2912,6 +2936,8 @@ class Impl : public VisItem location_t locus; public: + Item::Kind get_item_kind () const { return Item::Kind::Impl; } + // Returns whether impl has generic parameters. bool has_generics () const { return !generic_params.empty (); } @@ -3718,6 +3744,8 @@ class ExternBlock : public VisItem bool marked_for_strip = false; public: + Item::Kind get_item_kind () const { return Item::Kind::ExternBlock; } + std::string as_string () const override; // Returns whether extern block has inner attributes. diff --git a/gcc/rust/ast/rust-macro.h b/gcc/rust/ast/rust-macro.h index bcf5b0b5a994..7b1278c6cf3d 100644 --- a/gcc/rust/ast/rust-macro.h +++ b/gcc/rust/ast/rust-macro.h @@ -459,6 +459,8 @@ class MacroRulesDefinition : public VisItem DeclMacro, }; + Item::Kind get_item_kind () const { return Item::Kind::MacroRules; } + private: std::vector outer_attrs; Identifier rule_name; @@ -608,6 +610,8 @@ class MacroInvocation : public TypeNoBounds, Builtin, }; + Item::Kind get_item_kind () const { return Item::Kind::MacroInvocation; } + std::string as_string () const override; /** diff --git a/gcc/rust/expand/rust-derive.cc b/gcc/rust/expand/rust-derive.cc index e9927df1559f..477d67d9a294 100644 --- a/gcc/rust/expand/rust-derive.cc +++ b/gcc/rust/expand/rust-derive.cc @@ -27,10 +27,30 @@ DeriveVisitor::DeriveVisitor (location_t loc) : loc (loc), builder (AstBuilder (loc)) {} +bool +check_if_can_derive (const Item &item, const Attribute &attr) +{ + switch (item.get_item_kind ()) + { + case Item::Kind::Struct: + case Item::Kind::Union: + case Item::Kind::Enum: + return true; + default: + rust_error_at ( + attr.get_locus (), + "the %<#[derive]%> attribute can only be used on struct, union and " + "enum declaration"); + return false; + } +} + std::unique_ptr -DeriveVisitor::derive (Item &item, const Attribute &attr, - BuiltinMacro to_derive) +derive (Item &item, const Attribute &attr, BuiltinMacro to_derive) { + if (!check_if_can_derive (item, attr)) + return nullptr; + switch (to_derive) { case BuiltinMacro::Clone: @@ -45,7 +65,7 @@ DeriveVisitor::derive (Item &item, const Attribute &attr, case BuiltinMacro::PartialOrd: case BuiltinMacro::Hash: default: - rust_sorry_at (attr.get_locus (), "uninmplemented builtin derive macro"); + rust_sorry_at (attr.get_locus (), "unimplemented builtin derive macro"); return nullptr; }; } diff --git a/gcc/rust/expand/rust-derive.h b/gcc/rust/expand/rust-derive.h index f953c3decbfa..04a55b742ff2 100644 --- a/gcc/rust/expand/rust-derive.h +++ b/gcc/rust/expand/rust-derive.h @@ -27,16 +27,28 @@ namespace Rust { namespace AST { + +std::unique_ptr derive (Item &item, const Attribute &derive, + BuiltinMacro to_derive); +bool check_if_can_derive (const Item &item, const Attribute &attr); + +template +bool +check_if_can_derive (const T &_item, const Attribute &attr) +{ + rust_error_at ( + attr.get_locus (), + "the %<#[derive]%> attribute can only be used on struct, union and " + "enum declaration"); + return false; +} + /** * The goal of this class is to accumulate and create the required items from a * builtin `#[derive]` macro applied on a struct, enum or union. */ class DeriveVisitor : public AST::ASTVisitor { -public: - static std::unique_ptr derive (Item &item, const Attribute &derive, - BuiltinMacro to_derive); - protected: DeriveVisitor (location_t loc); diff --git a/gcc/rust/expand/rust-expand-visitor.cc b/gcc/rust/expand/rust-expand-visitor.cc index bd49fd910929..433e407bee92 100644 --- a/gcc/rust/expand/rust-expand-visitor.cc +++ b/gcc/rust/expand/rust-expand-visitor.cc @@ -46,7 +46,7 @@ static std::unique_ptr builtin_derive_item (AST::Item &item, const AST::Attribute &derive, BuiltinMacro to_derive) { - return AST::DeriveVisitor::derive (item, derive, to_derive); + return AST::derive (item, derive, to_derive); } static std::vector> @@ -191,9 +191,12 @@ ExpandVisitor::expand_inner_items ( auto new_item = builtin_derive_item (*item, current, maybe_builtin.value ()); - // this inserts the derive *before* the item - is it a - // problem? - it = items.insert (it, std::move (new_item)); + if (new_item != nullptr) + { + // this inserts the derive *before* the item - is + // it a problem? + it = items.insert (it, std::move (new_item)); + } } else { @@ -368,7 +371,9 @@ void ExpandVisitor::expand_tuple_fields (std::vector &fields) { for (auto &field : fields) - maybe_expand_type (field.get_field_type ()); + { + maybe_expand_type (field.get_field_type ()); + } } // FIXME: This can definitely be refactored with the method above diff --git a/gcc/rust/expand/rust-expand-visitor.h b/gcc/rust/expand/rust-expand-visitor.h index f40b5773fc1c..d36e37c20728 100644 --- a/gcc/rust/expand/rust-expand-visitor.h +++ b/gcc/rust/expand/rust-expand-visitor.h @@ -87,6 +87,11 @@ class ExpandVisitor : public AST::DefaultASTVisitor void expand_closure_params (std::vector ¶ms); void expand_where_clause (AST::WhereClause &where_clause); + /** + * Checks if there is a `#[derive]` attribute and emit an error if so. + */ + void check_derive (std::vector &attrs); + /** * Expand a set of values, erasing them if they are marked for strip, and * replacing them with expanded macro nodes if necessary. diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc index 715e9a0b361a..5b2f43b103b2 100644 --- a/gcc/rust/util/rust-attributes.cc +++ b/gcc/rust/util/rust-attributes.cc @@ -24,7 +24,6 @@ #include "rust-ast-full.h" #include "rust-diagnostics.h" #include "rust-unicode.h" -#include "rust-attribute-values.h" namespace Rust { namespace Analysis { @@ -99,16 +98,7 @@ AttributeChecker::go (AST::Crate &crate) visit (crate); } -void -AttributeChecker::visit (AST::Crate &crate) -{ - check_attributes (crate.get_inner_attrs ()); - - for (auto &item : crate.items) - item->accept_vis (*this); -} - -static bool +bool is_builtin (const AST::Attribute &attribute, BuiltinAttrDefinition &builtin) { auto &segments = attribute.get_path ().get_segments (); @@ -165,7 +155,7 @@ check_doc_alias (const std::string &alias_input, const location_t locus) "%<#[doc(alias)]%> input cannot start or end with a space"); } -static void +void check_doc_attribute (const AST::Attribute &attribute) { if (!attribute.has_attr_input ()) @@ -209,6 +199,12 @@ check_doc_attribute (const AST::Attribute &attribute) } } +void +AttributeChecker::visit (AST::Crate &crate) +{ + check_attributes (crate, crate.get_inner_attrs ()); +} + static bool is_proc_macro_type (const AST::Attribute &attribute) { @@ -255,7 +251,23 @@ check_proc_macro_non_root (AST::AttrVec attributes, location_t loc) } void -AttributeChecker::check_attribute (const AST::Attribute &attribute) +check_derive (const AST::Item &item, const AST::Attribute &attr) +{ + if (attr.is_derive ()) + { + // It will generate an error if the item cannot derive. + if (!AST::check_if_can_derive(item, attr)) + { + rust_error_at ( + attr.get_locus (), + "the %<#[derive]%> attribute can only be used on struct, union and " + "enum declaration"); + } + } +} + +void +AttributeChecker::check_attribute (const AST::Item &item, const AST::Attribute &attribute) { BuiltinAttrDefinition result; @@ -263,6 +275,8 @@ AttributeChecker::check_attribute (const AST::Attribute &attribute) if (!is_builtin (attribute, result)) return; + check_derive (item, attribute); + // TODO: Add checks here for each builtin attribute // TODO: Have an enum of builtins as well, switching on strings is annoying // and costly @@ -271,10 +285,10 @@ AttributeChecker::check_attribute (const AST::Attribute &attribute) } void -AttributeChecker::check_attributes (const AST::AttrVec &attributes) +AttributeChecker::check_attributes (const AST::Item &item, const AST::AttrVec &attributes) { for (auto &attr : attributes) - check_attribute (attr); + check_attribute (item, attr); } void @@ -590,6 +604,7 @@ AttributeChecker::visit (AST::Module &module) void AttributeChecker::visit (AST::ExternCrate &crate) { + check_attributes (crate, crate.get_outer_attrs ()); check_proc_macro_non_function (crate.get_outer_attrs ()); } @@ -608,6 +623,7 @@ AttributeChecker::visit (AST::UseTreeRebind &) void AttributeChecker::visit (AST::UseDeclaration &declaration) { + check_attributes (declaration, declaration.get_outer_attrs ()); check_proc_macro_non_function (declaration.get_outer_attrs ()); } @@ -639,6 +655,7 @@ AttributeChecker::visit (AST::Function &fun) }; BuiltinAttrDefinition result; + for (auto &attribute : fun.get_outer_attrs ()) { if (!is_builtin (attribute, result)) @@ -646,6 +663,8 @@ AttributeChecker::visit (AST::Function &fun) auto name = result.name.c_str (); + check_attribute (fun, attribute); + if (result.name == Attrs::PROC_MACRO_DERIVE) { if (!attribute.has_attr_input ()) @@ -674,79 +693,99 @@ AttributeChecker::visit (AST::Function &fun) void AttributeChecker::visit (AST::TypeAlias &alias) { + check_attributes (alias, alias.get_outer_attrs ()); check_proc_macro_non_function (alias.get_outer_attrs ()); } void AttributeChecker::visit (AST::StructStruct &struct_item) { - check_attributes (struct_item.get_outer_attrs ()); + check_attributes (struct_item, struct_item.get_outer_attrs ()); check_proc_macro_non_function (struct_item.get_outer_attrs ()); } void AttributeChecker::visit (AST::TupleStruct &tuplestruct) { + check_attributes (tuplestruct, tuplestruct.get_outer_attrs ()); check_proc_macro_non_function (tuplestruct.get_outer_attrs ()); } void -AttributeChecker::visit (AST::EnumItem &) -{} +AttributeChecker::visit (AST::EnumItem &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::EnumItemTuple &) -{} +AttributeChecker::visit (AST::EnumItemTuple &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::EnumItemStruct &) -{} +AttributeChecker::visit (AST::EnumItemStruct &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::EnumItemDiscriminant &) -{} +AttributeChecker::visit (AST::EnumItemDiscriminant &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void AttributeChecker::visit (AST::Enum &enumeration) { + check_attributes (enumeration, enumeration.get_outer_attrs ()); check_proc_macro_non_function (enumeration.get_outer_attrs ()); } void AttributeChecker::visit (AST::Union &u) { + check_attributes (u, u.get_outer_attrs ()); check_proc_macro_non_function (u.get_outer_attrs ()); } void AttributeChecker::visit (AST::ConstantItem &item) { + check_attributes (item, item.get_outer_attrs ()); check_proc_macro_non_function (item.get_outer_attrs ()); } void AttributeChecker::visit (AST::StaticItem &item) { + check_attributes (item, item.get_outer_attrs ()); check_proc_macro_non_function (item.get_outer_attrs ()); } void -AttributeChecker::visit (AST::TraitItemConst &) -{} +AttributeChecker::visit (AST::TraitItemConst &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::TraitItemType &) -{} +AttributeChecker::visit (AST::TraitItemType &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void AttributeChecker::visit (AST::Trait &trait) { + check_attributes (trait, trait.get_outer_attrs ()); check_proc_macro_non_function (trait.get_outer_attrs ()); } void AttributeChecker::visit (AST::InherentImpl &impl) { + check_attributes (impl, impl.get_outer_attrs ()); check_proc_macro_non_function (impl.get_outer_attrs ()); AST::DefaultASTVisitor::visit (impl); } @@ -754,25 +793,33 @@ AttributeChecker::visit (AST::InherentImpl &impl) void AttributeChecker::visit (AST::TraitImpl &impl) { + check_attributes (impl, impl.get_outer_attrs ()); check_proc_macro_non_function (impl.get_outer_attrs ()); AST::DefaultASTVisitor::visit (impl); } void -AttributeChecker::visit (AST::ExternalTypeItem &) -{} +AttributeChecker::visit (AST::ExternalTypeItem &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::ExternalStaticItem &) -{} +AttributeChecker::visit (AST::ExternalStaticItem &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void -AttributeChecker::visit (AST::ExternalFunctionItem &) -{} +AttributeChecker::visit (AST::ExternalFunctionItem &item) +{ + check_attributes (item, item.get_outer_attrs ()); +} void AttributeChecker::visit (AST::ExternBlock &block) { + check_attributes (block, block.get_outer_attrs ()); check_proc_macro_non_function (block.get_outer_attrs ()); } @@ -863,7 +910,7 @@ AttributeChecker::visit (AST::ReferencePattern &) // void AttributeChecker::visit(StructPatternField& ){} void -AttributeChecker::visit (AST::StructPatternFieldTuplePat &) +AttributeChecker::visit (AST::StructPatternFieldTuplePat &item) {} void diff --git a/gcc/rust/util/rust-attributes.h b/gcc/rust/util/rust-attributes.h index d78ab0b33196..eae4f3a963bb 100644 --- a/gcc/rust/util/rust-attributes.h +++ b/gcc/rust/util/rust-attributes.h @@ -21,6 +21,8 @@ #include "rust-ast.h" #include "rust-system.h" #include "rust-ast-visitor.h" +#include "rust-derive.h" +#include "rust-attribute-values.h" namespace Rust { namespace Analysis { @@ -70,6 +72,27 @@ class BuiltinAttributeMappings std::map mappings; }; +bool is_builtin (const AST::Attribute &attribute, BuiltinAttrDefinition &builtin); + +void check_doc_attribute (const AST::Attribute &attribute); + +template +void +check_derive (const T &item, const AST::Attribute &attr) +{ + if (attr.is_derive ()) + { + // It will generate an error if the item cannot derive. + if (!AST::check_if_can_derive(item, attr)) + { + rust_error_at ( + attr.get_locus (), + "the %<#[derive]%> attribute can only be used on struct, union and " + "enum declaration"); + } + } +} + /** * Checks the validity of various attributes. The goal of this visitor is to * make sure that attributes are applied in allowed contexts, for example to @@ -90,10 +113,34 @@ class AttributeChecker : public AST::DefaultASTVisitor private: using AST::DefaultASTVisitor::visit; /* Check the validity of a given attribute */ - void check_attribute (const AST::Attribute &attribute); + void check_attribute (const AST::Item &item, const AST::Attribute &attribute); + template + void check_attribute (const T &item, const AST::Attribute &attribute) + { + BuiltinAttrDefinition result; + + + // This checker does not check non-builtin attributes + if (!is_builtin (attribute, result)) + return; + + check_derive (item, attribute); + + // TODO: Add checks here for each builtin attribute + // TODO: Have an enum of builtins as well, switching on strings is annoying + // and costly + if (result.name == Values::Attributes::DOC) + check_doc_attribute (attribute); + } /* Check the validity of all given attributes */ - void check_attributes (const AST::AttrVec &attributes); + void check_attributes (const AST::Item &item, const AST::AttrVec &attributes); + template + void check_attributes (const T &item, const AST::AttrVec &attributes) + { + for (auto &attr : attributes) + check_attribute (item, attr); + } // rust-ast.h void visit (AST::Crate &crate) override; diff --git a/gcc/testsuite/rust/compile/derive_macro9.rs b/gcc/testsuite/rust/compile/derive_macro9.rs new file mode 100644 index 000000000000..3703bb92c5b6 --- /dev/null +++ b/gcc/testsuite/rust/compile/derive_macro9.rs @@ -0,0 +1,27 @@ +#![derive(Clone)] + +#[derive(Clone)] // { dg-error "attribute can only be used on struct, union and enum declaration" } +pub fn foo() {} + +pub struct Struct { + #[derive(Clone)] // { dg-error "attribute can only be used on struct, union and enum declaration" } + a: u32, +} + +pub enum Enum { + // FIXME: This is a bug, it should warn! + #[derive(Clone)] + A, + // FIXME: This is a bug, it should warn! + #[derive(Clone)] + B(u32), + // FIXME: This is a bug, it should warn! + #[derive(Clone)] + C { a: u32 }, +} + +pub union Union { + #[derive(Clone)] // { dg-error "attribute can only be used on struct, union and enum declaration" } + a: u32, + b: u8, +}