https://gcc.gnu.org/g:2f6f227d81e8a05e3fac2c247637d0996477ee7e
commit r17-4092-g2f6f227d81e8a05e3fac2c247637d0996477ee7e Author: Arthur Cohen <[email protected]> Date: Mon Aug 3 12:56:50 2026 +0200 gccrs: attributes: Check if an attribute is a tool attribute Tool attributes are allowed in Rust code, and should be ignored by the compiler and handled by the specified tools instead. gcc/rust/ChangeLog: * util/rust-attributes.h: Change Attributes::is_known API to return an enum, add AttributeKnowledge result enum. * util/rust-attribute-values.h: Add more known tool attributes. * util/rust-attributes.cc (Attributes::is_known): Check for tool attributes and adapt return type. * hir/rust-ast-lower-base.cc (ASTLoweringBase::handle_outer_attributes): Use new API and ignore tool attributes. * resolve/rust-early-name-resolver-2.0.cc (Early::visit): Likewise. gcc/testsuite/ChangeLog: * rust/compile/tool-attribute1.rs: New test. Diff: --- gcc/rust/hir/rust-ast-lower-base.cc | 8 +++++- gcc/rust/resolve/rust-early-name-resolver-2.0.cc | 16 +++++++----- gcc/rust/util/rust-attribute-values.h | 4 +++ gcc/rust/util/rust-attributes.cc | 31 ++++++++++++++++++++++-- gcc/rust/util/rust-attributes.h | 19 ++++++++++++++- gcc/testsuite/rust/compile/tool-attribute1.rs | 22 +++++++++++++++++ 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/gcc/rust/hir/rust-ast-lower-base.cc b/gcc/rust/hir/rust-ast-lower-base.cc index 0de6ace8acf3..a4ab5d5edbc8 100644 --- a/gcc/rust/hir/rust-ast-lower-base.cc +++ b/gcc/rust/hir/rust-ast-lower-base.cc @@ -797,13 +797,19 @@ ASTLoweringBase::handle_outer_attributes (const ItemWrapper &item) for (const auto &attr : item.get_outer_attrs ()) { const auto &str_path = attr.get_path ().as_string (); - if (!Analysis::Attributes::is_known (str_path)) + auto known_check = Analysis::Attributes::is_known (str_path); + if (known_check == Analysis::Attributes::AttributeKnowledge::Unknown) { rust_error_at (attr.get_locus (), "unknown attribute: %qs", str_path.c_str ()); continue; } + // If it is a tool attribute, the compiler can ignore it and let the tool + // handle it + if (known_check == Analysis::Attributes::AttributeKnowledge::Tool) + return; + bool is_lang_item = str_path == Values::Attributes::LANG && attr.has_attr_input () && attr.get_attr_input ().get_attr_input_type () diff --git a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc index 830932cdd7fb..8a07f8f293cc 100644 --- a/gcc/rust/resolve/rust-early-name-resolver-2.0.cc +++ b/gcc/rust/resolve/rust-early-name-resolver-2.0.cc @@ -454,17 +454,21 @@ Early::visit (AST::Attribute &attr) auto &mappings = Analysis::Mappings::get (); auto name = attr.get_path ().get_segments ().at (0).get_segment_name (); - auto is_not_builtin = [&name] (AST::Attribute &attr) { - return Analysis::BuiltinAttributeMappings::get () - ->lookup_builtin (name) - .is_error (); - }; + auto known_check = Analysis::Attributes::is_known (name); + + // If it is a tool attribute, the compiler can ignore it and let the tool + // handle it + if (known_check == Analysis::Attributes::AttributeKnowledge::Tool) + return; + + auto is_builtin + = known_check == Analysis::Attributes::AttributeKnowledge::Known; if (attr.is_derive ()) { visit_derive_attribute (attr, mappings); } - else if (is_not_builtin (attr)) // Do not resolve builtins + else if (!is_builtin) // Do not resolve builtins { visit_non_builtin_attribute (attr, mappings, name); } diff --git a/gcc/rust/util/rust-attribute-values.h b/gcc/rust/util/rust-attribute-values.h index 3ab91d35f9cc..e2718ca4a55f 100644 --- a/gcc/rust/util/rust-attribute-values.h +++ b/gcc/rust/util/rust-attribute-values.h @@ -105,6 +105,10 @@ public: static constexpr auto &NON_EXHAUSTIVE = "non_exhaustive"; static constexpr auto &RUSTFMT = "rustfmt"; + static constexpr auto &CLIPPY = "clippy"; + static constexpr auto &DIAGNOSTIC = "diagnostic"; + static constexpr auto &MIRI = "miri"; + static constexpr auto &RUST_ANALYZER = "rust_analyzer"; static constexpr auto &TEST = "test"; diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc index 2ae917fdb9f4..8ab4bc608b85 100644 --- a/gcc/rust/util/rust-attributes.cc +++ b/gcc/rust/util/rust-attributes.cc @@ -94,6 +94,10 @@ static const BuiltinAttrDefinition __definitions[] {Attrs::FUNDAMENTAL, TYPE_CHECK}, {Attrs::NON_EXHAUSTIVE, TYPE_CHECK}, {Attrs::RUSTFMT, EXTERNAL}, + {Attrs::CLIPPY, EXTERNAL}, + {Attrs::DIAGNOSTIC, EXTERNAL}, + {Attrs::MIRI, EXTERNAL}, + {Attrs::RUST_ANALYZER, EXTERNAL}, {Attrs::TEST, CODE_GENERATION}, {Attrs::NEEDS_ALLOCATOR, CODE_GENERATION}, {Attrs::RUSTC_ALLOCATOR, CODE_GENERATION}, @@ -120,13 +124,36 @@ static const std::set<std::string> __outer_attributes Attrs::LINK_NAME, Attrs::LINK_SECTION}; -bool +Attributes::AttributeKnowledge Attributes::is_known (const std::string &attribute_path) { const auto &lookup = BuiltinAttributeMappings::get ()->lookup_builtin (attribute_path); - return !lookup.is_error (); + if (!lookup.is_error ()) + return AttributeKnowledge::Known; + + // We have to check for tool attributes as well + // https://doc.rust-lang.org/reference/attributes.html#tool-attributes + // + // > rustc currently recognizes the tools “clippy”, “rustfmt”, “diagnostic”, + // “miri”, and “rust_analyzer”. + static std::unordered_set<std::string> known_tools = { + "clippy", "rustfmt", "diagnostic", "miri", "rust_analyzer", + }; + + auto colon = attribute_path.find ("::"); + if (colon != std::string::npos) + { + auto tool = attribute_path.substr (0, colon); + + // If this is a known tool, the attribute is "known". The rest of the path + // is up to the tool to interpret. + if (known_tools.find (tool) != known_tools.end ()) + return AttributeKnowledge::Tool; + } + + return AttributeKnowledge::Unknown; } bool diff --git a/gcc/rust/util/rust-attributes.h b/gcc/rust/util/rust-attributes.h index 778cc9e33db6..c173a3f764fd 100644 --- a/gcc/rust/util/rust-attributes.h +++ b/gcc/rust/util/rust-attributes.h @@ -27,7 +27,24 @@ namespace Analysis { class Attributes { public: - static bool is_known (const std::string &attribute_path); + enum class AttributeKnowledge + { + /** + * Built-in attribute + */ + Known, + /** + * Tool attribute, to be handled by the specified tool rather than the + * compiler + */ + Tool, + /** + * Unknown attribute + */ + Unknown, + }; + + static AttributeKnowledge is_known (const std::string &attribute_path); static bool valid_outer_attribute (const std::string &attribute_path); static tl::optional<std::string> extract_string_literal (const AST::Attribute &attr); diff --git a/gcc/testsuite/rust/compile/tool-attribute1.rs b/gcc/testsuite/rust/compile/tool-attribute1.rs new file mode 100644 index 000000000000..f8f198f5052a --- /dev/null +++ b/gcc/testsuite/rust/compile/tool-attribute1.rs @@ -0,0 +1,22 @@ +#![feature(no_core)] +#![feature(lang_items)] +#![feature(rustc_attrs)] +#![no_core] + +#[rustfmt::skip] +pub fn foo() {} + +#[clippy::something::useful] +#[diagnostic::diagnose_this] +struct Boo; + +#[miri::save_us_all] +unsafe fn scary() {} + +#[rust_analyzer::vade_retro] +unsafe fn stannanas() {} + +#[not_a_tool::but_still_tool_like] +// { dg-error "macro not found" "" { target *-*-* } .-1 } +// { dg-error "could not resolve attribute macro invocation" "" { target *-*-* } .-2 } +pub fn ha_exclam() {}
