| // Copyright (C) 2026 Free Software Foundation, Inc. |
| |
| // This file is part of GCC. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with GCC; see the file COPYING3. If not see |
| // <http://www.gnu.org/licenses/>. |
| |
| #include "rust-attribute-checker.h" |
| #include "rust-attributes.h" |
| #include "rust-attribute-values.h" |
| #include "rust-diagnostics.h" |
| #include "rust-session-manager.h" |
| |
| namespace Rust { |
| namespace Analysis { |
| |
| using Attrs = Values::Attributes; |
| |
| static bool |
| is_proc_macro_type (const AST::Attribute &attribute) |
| { |
| auto result_opt = lookup_builtin (attribute); |
| if (!result_opt.has_value ()) |
| return false; |
| auto result = result_opt.value (); |
| |
| auto name = result.name; |
| return name == Attrs::PROC_MACRO || name == Attrs::PROC_MACRO_DERIVE |
| || name == Attrs::PROC_MACRO_ATTRIBUTE; |
| } |
| |
| // Emit an error when one encountered attribute is either #[proc_macro], |
| // #[proc_macro_attribute] or #[proc_macro_derive] |
| static void |
| check_proc_macro_non_function (const AST::Attribute &attr) |
| { |
| if (is_proc_macro_type (attr)) |
| rust_error_at (attr.get_locus (), |
| "the %<#[%s]%> attribute may only be used on bare functions", |
| attr.get_path ().get_segments ()[0].as_string ().c_str ()); |
| } |
| |
| // Emit an error when one attribute is either proc_macro, proc_macro_attribute |
| // or proc_macro_derive |
| static void |
| check_proc_macro_non_root (const AST::Attribute &attr, location_t loc) |
| { |
| if (is_proc_macro_type (attr)) |
| { |
| rust_error_at ( |
| loc, |
| "functions tagged with %<#[%s]%> must currently " |
| "reside in the root of the crate", |
| attr.get_path ().get_segments ().at (0).as_string ().c_str ()); |
| } |
| } |
| |
| AttributeChecker::AttributeChecker () {} |
| |
| void |
| AttributeChecker::go (AST::Crate &crate) |
| { |
| visit (crate); |
| } |
| |
| void |
| AttributeChecker::visit (AST::Attribute &attribute) |
| { |
| auto &session = Session::get_instance (); |
| if (attribute.get_path () == Values::Attributes::CFG_ATTR) |
| { |
| if (!attribute.is_parsed_to_meta_item ()) |
| attribute.parse_attr_to_meta_item (); |
| if (!attribute.check_cfg_predicate (session)) |
| return; // Do not emit errors for attribute that'll get stripped. |
| } |
| |
| AST::DefaultASTVisitor::visit (attribute); |
| } |
| |
| void |
| AttributeChecker::visit (AST::Token &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::DelimTokenTree &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IdentifierExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::Lifetime &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::LifetimeParam &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ConstGenericParam &) |
| {} |
| |
| // rust-path.h |
| void |
| AttributeChecker::visit (AST::PathInExpression &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypePathSegment &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypePathSegmentGeneric &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypePathSegmentFunction &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypePath &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::QualifiedPathInExpression &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::QualifiedPathInType &) |
| {} |
| |
| // rust-expr.h |
| void |
| AttributeChecker::visit (AST::LiteralExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::AttrInputLiteral &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaItemLitExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaItemPathExpr &attribute) |
| { |
| if (!attribute.get_expr ().is_literal ()) |
| { |
| rust_error_at (attribute.get_expr ().get_locus (), |
| "malformed %<path%> attribute input"); |
| rust_inform (attribute.get_expr ().get_locus (), |
| "must be of the form: %<#[path = \"file\"]%>"); |
| } |
| } |
| |
| void |
| AttributeChecker::visit (AST::BorrowExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::DereferenceExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ErrorPropagationExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::NegationExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArithmeticOrLogicalExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ComparisonExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::LazyBooleanExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypeCastExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::AssignmentExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::CompoundAssignmentExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::GroupedExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArrayElemsValues &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArrayElemsCopied &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArrayExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArrayIndexExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TupleExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TupleIndexExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprStruct &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprFieldIdentifier &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprFieldIdentifierValue &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprFieldIndexValue &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprStructFields &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructExprStructBase &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::CallExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MethodCallExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::FieldAccessExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ClosureExprInner &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::BlockExpr &expr) |
| { |
| for (auto &stmt : expr.get_statements ()) |
| { |
| if (stmt->get_stmt_kind () == AST::Stmt::Kind::Item) |
| { |
| // Non owning pointer, let it go out of scope |
| auto item = static_cast<AST::Item *> (stmt.get ()); |
| for (auto &attr : item->get_outer_attrs ()) |
| check_proc_macro_non_root (attr, item->get_locus ()); |
| } |
| } |
| AST::DefaultASTVisitor::visit (expr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::ClosureExprInnerTyped &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ContinueExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::BreakExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeFromToExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeFromExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeToExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeFullExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeFromToInclExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangeToInclExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ReturnExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::LoopExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::WhileLoopExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::WhileLetLoopExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ForLoopExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IfExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IfExprConseqElse &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IfLetExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IfLetExprConseqElse &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MatchExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::AwaitExpr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::AsyncBlockExpr &) |
| {} |
| |
| // rust-item.h |
| void |
| AttributeChecker::visit (AST::TypeParam &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::LifetimeWhereClauseItem &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TypeBoundWhereClauseItem &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::Module &module) |
| { |
| for (auto &attr : module.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| |
| for (auto &item : module.get_items ()) |
| for (auto &attr : item->get_outer_attrs ()) |
| check_proc_macro_non_root (attr, item->get_locus ()); |
| |
| AST::DefaultASTVisitor::visit (module); |
| } |
| |
| void |
| AttributeChecker::visit (AST::ExternCrate &crate) |
| { |
| for (auto &attr : crate.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::UseTreeGlob &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::UseTreeList &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::UseTreeRebind &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::UseDeclaration &declaration) |
| { |
| for (auto &attr : declaration.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::Function &fun) |
| { |
| if (fun.has_body ()) |
| fun.get_definition ().value ()->accept_vis (*this); |
| } |
| |
| void |
| AttributeChecker::visit (AST::TypeAlias &alias) |
| { |
| for (auto &attr : alias.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::StructStruct &struct_item) |
| { |
| for (auto &attr : struct_item.get_outer_attrs ()) |
| { |
| check_proc_macro_non_function (attr); |
| } |
| |
| AST::DefaultASTVisitor::visit (struct_item); |
| } |
| |
| void |
| AttributeChecker::visit (AST::TupleStruct &tuplestruct) |
| { |
| for (auto &attr : tuplestruct.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::EnumItem &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::EnumItemTuple &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::EnumItemStruct &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::EnumItemDiscriminant &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::Enum &enumeration) |
| { |
| for (auto &attr : enumeration.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::Union &u) |
| { |
| for (auto &attr : u.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::ConstantItem &item) |
| { |
| for (auto &attr : item.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::StaticItem &item) |
| { |
| for (auto &attr : item.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| void |
| AttributeChecker::visit (AST::TraitItemType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::Trait &trait) |
| { |
| for (auto &attr : trait.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| |
| AST::DefaultASTVisitor::visit (trait); |
| } |
| |
| void |
| AttributeChecker::visit (AST::InherentImpl &impl) |
| { |
| for (auto &attr : impl.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| |
| AST::DefaultASTVisitor::visit (impl); |
| } |
| |
| void |
| AttributeChecker::visit (AST::TraitImpl &impl) |
| { |
| for (auto &attr : impl.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| |
| AST::DefaultASTVisitor::visit (impl); |
| } |
| |
| void |
| AttributeChecker::visit (AST::ExternalTypeItem &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ExternalStaticItem &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ExternBlock &block) |
| { |
| for (auto &attr : block.get_outer_attrs ()) |
| check_proc_macro_non_function (attr); |
| } |
| |
| // rust-macro.h |
| void |
| AttributeChecker::visit (AST::MacroMatchFragment &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MacroMatchRepetition &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MacroMatcher &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MacroRulesDefinition &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MacroInvocation &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaItemPath &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaWord &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaNameValueStr &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaListPaths &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::MetaListNameValueStr &) |
| {} |
| |
| // rust-pattern.h |
| void |
| AttributeChecker::visit (AST::LiteralPattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::IdentifierPattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::WildcardPattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RestPattern &) |
| {} |
| |
| // void AttributeChecker::visit(RangePatternBound& ){} |
| |
| void |
| AttributeChecker::visit (AST::RangePatternBoundLiteral &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangePatternBoundPath &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangePatternBoundQualPath &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RangePattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ReferencePattern &) |
| {} |
| |
| // void AttributeChecker::visit(StructPatternField& ){} |
| |
| void |
| AttributeChecker::visit (AST::StructPatternFieldTuplePat &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructPatternFieldIdentPat &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructPatternFieldIdent &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::StructPattern &) |
| {} |
| |
| // void AttributeChecker::visit(TupleStructItems& ){} |
| |
| void |
| AttributeChecker::visit (AST::TupleStructItemsNoRest &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TupleStructItemsHasRest &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TupleStructPattern &) |
| {} |
| |
| // void AttributeChecker::visit(TuplePatternItems& ){} |
| |
| void |
| AttributeChecker::visit (AST::TuplePatternItemsNoRest &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TuplePatternItemsHasRest &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TuplePattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::GroupedPattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::SlicePattern &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::AltPattern &) |
| {} |
| |
| // rust-stmt.h |
| void |
| AttributeChecker::visit (AST::EmptyStmt &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::LetStmt &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ExprStmt &) |
| {} |
| |
| // rust-type.h |
| void |
| AttributeChecker::visit (AST::TraitBound &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ImplTraitType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TraitObjectType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ParenthesisedType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ImplTraitTypeOneBound &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TraitObjectTypeOneBound &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::TupleType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::NeverType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::RawPointerType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ReferenceType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::ArrayType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::SliceType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::InferredType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::BareFunctionType &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::SelfParam &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::VariadicParam &) |
| {} |
| |
| void |
| AttributeChecker::visit (AST::FunctionParam &) |
| {} |
| |
| } // namespace Analysis |
| } // namespace Rust |