LCOV - code coverage report
Current view: top level - gcc/rust/checks/errors - rust-builtin-attribute-checker.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 77.6 % 246 191
Test Date: 2026-04-20 14:57:17 Functions: 100.0 % 35 35
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-builtin-attribute-checker.h"
      20              : #include "optional.h"
      21              : #include "rust-attributes.h"
      22              : #include "rust-attribute-values.h"
      23              : #include "rust-session-manager.h"
      24              : 
      25              : namespace Rust {
      26              : namespace Analysis {
      27              : 
      28              : using Attrs = Values::Attributes;
      29              : 
      30              : void
      31        11863 : check_inner_attribute (const AST::Attribute &attribute)
      32              : {
      33        11863 :   auto result_opt = lookup_builtin (attribute);
      34        11863 :   if (!result_opt.has_value ())
      35            0 :     return;
      36        11863 :   auto result = result_opt.value ();
      37              : 
      38        11863 :   if (Attributes::valid_outer_attribute (result.name))
      39            3 :     rust_error_at (attribute.get_locus (),
      40              :                    "attribute cannot be used at crate level");
      41        23726 : }
      42              : 
      43              : /**
      44              :  * Check that the string given to #[doc(alias = ...)] or #[doc(alias(...))] is
      45              :  * valid.
      46              :  *
      47              :  * This means no whitespace characters other than spaces and no quoting
      48              :  * characters.
      49              :  */
      50              : static void
      51          700 : check_doc_alias (const std::string &alias_input, const location_t locus)
      52              : {
      53              :   // FIXME: The locus here is for the whole attribute. Can we get the locus
      54              :   // of the alias input instead?
      55         1872 :   for (auto c : alias_input)
      56         1172 :     if ((ISSPACE (c) && c != ' ') || c == '\'' || c == '\"')
      57              :       {
      58            7 :         auto to_print = std::string (1, c);
      59            7 :         switch (c)
      60              :           {
      61            7 :           case '\n':
      62            7 :             to_print = "\\n";
      63            7 :             break;
      64            0 :           case '\t':
      65            0 :             to_print = "\\t";
      66            0 :             break;
      67              :           default:
      68              :             break;
      69              :           }
      70            7 :         rust_error_at (locus,
      71              :                        "invalid character used in %<#[doc(alias)]%> input: %qs",
      72              :                        to_print.c_str ());
      73            7 :       }
      74              : 
      75          700 :   if (alias_input.empty ())
      76              :     return;
      77              : 
      78         1400 :   if (alias_input.front () == ' ' || alias_input.back () == ' ')
      79            0 :     rust_error_at (locus,
      80              :                    "%<#[doc(alias)]%> input cannot start or end with a space");
      81              : }
      82              : 
      83              : // This namespace contains handlers for the builtin attribute checker,
      84              : // those handlers must verify the attribute internal structure and emit the
      85              : // appropriate error message if the structure is incorrect.
      86              : //
      87              : // They DO NOT check the attribute validity on the parent item.
      88              : namespace handlers {
      89              : 
      90              : void
      91         8935 : doc (const AST::Attribute &attribute)
      92              : {
      93         8935 :   if (!attribute.has_attr_input ())
      94              :     {
      95            2 :       rust_error_at (
      96              :         attribute.get_locus (),
      97              :         "valid forms for the attribute are "
      98              :         "%<#[doc(hidden|inline|...)]%> and %<#[doc = \" string \"]%>");
      99            2 :       return;
     100              :     }
     101              : 
     102         8933 :   switch (attribute.get_attr_input ().get_attr_input_type ())
     103              :     {
     104              :     case AST::AttrInput::LITERAL:
     105              :     case AST::AttrInput::META_ITEM:
     106              :     case AST::AttrInput::EXPR:
     107              :       break;
     108              :       // FIXME: Handle them as well
     109              : 
     110          799 :     case AST::AttrInput::TOKEN_TREE:
     111          799 :       {
     112              :         // FIXME: This doesn't check for #[doc(alias(...))]
     113          799 :         const auto &option = static_cast<const AST::DelimTokenTree &> (
     114          799 :           attribute.get_attr_input ());
     115          799 :         auto *meta_item = option.parse_to_meta_item ();
     116              : 
     117         1605 :         for (auto &item : meta_item->get_items ())
     118              :           {
     119          806 :             if (item->is_key_value_pair ())
     120              :               {
     121          707 :                 auto name_value
     122          707 :                   = static_cast<AST::MetaNameValueStr *> (item.get ())
     123          707 :                       ->get_name_value_pair ();
     124              : 
     125              :                 // FIXME: Check for other stuff than #[doc(alias = ...)]
     126          707 :                 if (name_value.first.as_string () == "alias")
     127          700 :                   check_doc_alias (name_value.second, attribute.get_locus ());
     128          707 :               }
     129              :           }
     130              :         break;
     131              :       }
     132              :     }
     133              : }
     134              : 
     135              : void
     136            6 : deprecated (const AST::Attribute &attribute)
     137              : {
     138            6 :   if (!attribute.has_attr_input ())
     139              :     return;
     140              : 
     141            4 :   const auto &input = attribute.get_attr_input ();
     142              : 
     143            4 :   if (input.get_attr_input_type () != AST::AttrInput::META_ITEM)
     144              :     return;
     145              : 
     146            0 :   auto &meta = static_cast<const AST::AttrInputMetaItemContainer &> (input);
     147              : 
     148            0 :   for (auto &current : meta.get_items ())
     149              :     {
     150            0 :       switch (current->get_kind ())
     151              :         {
     152            0 :         case AST::MetaItemInner::Kind::MetaItem:
     153            0 :           {
     154            0 :             auto *meta_item = static_cast<AST::MetaItem *> (current.get ());
     155              : 
     156            0 :             switch (meta_item->get_item_kind ())
     157              :               {
     158            0 :               case AST::MetaItem::ItemKind::NameValueStr:
     159            0 :                 {
     160            0 :                   auto *nv = static_cast<AST::MetaNameValueStr *> (meta_item);
     161              : 
     162            0 :                   const std::string key = nv->get_name ().as_string ();
     163              : 
     164            0 :                   if (key != "since" && key != "note")
     165              :                     {
     166            0 :                       rust_error_at (nv->get_locus (), "unknown meta item %qs",
     167              :                                      key.c_str ());
     168            0 :                       rust_inform (nv->get_locus (),
     169              :                                    "expected one of %<since%>, %<note%>");
     170              :                     }
     171            0 :                 }
     172            0 :                 break;
     173              : 
     174            0 :               case AST::MetaItem::ItemKind::Path:
     175            0 :                 {
     176              :                   // #[deprecated(a,a)]
     177            0 :                   auto *p = static_cast<AST::MetaItemPath *> (meta_item);
     178              : 
     179            0 :                   std::string ident = p->get_path ().as_string ();
     180              : 
     181            0 :                   rust_error_at (p->get_locus (), "unknown meta item %qs",
     182              :                                  ident.c_str ());
     183            0 :                   rust_inform (p->get_locus (),
     184              :                                "expected one of %<since%>, %<note%>");
     185            0 :                 }
     186            0 :                 break;
     187              : 
     188            0 :               case AST::MetaItem::ItemKind::Word:
     189            0 :                 {
     190              :                   // #[deprecated("a")]
     191            0 :                   auto *w = static_cast<AST::MetaWord *> (meta_item);
     192              : 
     193            0 :                   rust_error_at (
     194            0 :                     w->get_locus (),
     195              :                     "item in %<deprecated%> must be a key/value pair");
     196              :                 }
     197            0 :                 break;
     198              : 
     199            0 :               case AST::MetaItem::ItemKind::PathExpr:
     200            0 :                 {
     201              :                   // #[deprecated(since=a)]
     202            0 :                   auto *px = static_cast<AST::MetaItemPathExpr *> (meta_item);
     203              : 
     204            0 :                   rust_error_at (
     205            0 :                     px->get_locus (),
     206              :                     "expected unsuffixed literal or identifier, found %qs",
     207            0 :                     px->get_expr ().as_string ().c_str ());
     208              :                 }
     209            0 :                 break;
     210              : 
     211            0 :               case AST::MetaItem::ItemKind::Seq:
     212            0 :               case AST::MetaItem::ItemKind::ListPaths:
     213            0 :               case AST::MetaItem::ItemKind::ListNameValueStr:
     214            0 :               default:
     215            0 :                 gcc_unreachable ();
     216            0 :                 break;
     217              :               }
     218              :           }
     219            0 :           break;
     220              : 
     221            0 :         case AST::MetaItemInner::Kind::LitExpr:
     222            0 :         default:
     223            0 :           gcc_unreachable ();
     224            0 :           break;
     225              :         }
     226              :     }
     227              : }
     228              : 
     229              : void
     230            2 : link_section (const AST::Attribute &attribute)
     231              : {
     232            2 :   if (!attribute.has_attr_input ())
     233              :     {
     234            1 :       rust_error_at (attribute.get_locus (),
     235              :                      "malformed %<link_section%> attribute input");
     236            1 :       rust_inform (attribute.get_locus (),
     237              :                    "must be of the form: %<#[link_section = \"name\"]%>");
     238              :     }
     239            2 : }
     240              : 
     241              : void
     242           11 : export_name (const AST::Attribute &attribute)
     243              : {
     244           11 :   if (!attribute.has_attr_input ())
     245              :     {
     246            2 :       rust_error_at (attribute.get_locus (),
     247              :                      "malformed %<export_name%> attribute input");
     248            2 :       rust_inform (attribute.get_locus (),
     249              :                    "must be of the form: %<#[export_name = \"name\"]%>");
     250            2 :       return;
     251              :     }
     252              : 
     253            9 :   auto &attr_input = attribute.get_attr_input ();
     254            9 :   if (attr_input.get_attr_input_type ()
     255              :       == AST::AttrInput::AttrInputType::LITERAL)
     256              :     {
     257            7 :       auto &literal_expr
     258            7 :         = static_cast<AST::AttrInputLiteral &> (attr_input).get_literal ();
     259            7 :       auto lit_type = literal_expr.get_lit_type ();
     260            7 :       switch (lit_type)
     261              :         {
     262              :         case AST::Literal::LitType::STRING:
     263              :         case AST::Literal::LitType::RAW_STRING:
     264              :         case AST::Literal::LitType::BYTE_STRING:
     265              :           return;
     266              :         default:
     267              :           break;
     268              :         }
     269              :     }
     270              : 
     271            4 :   rust_error_at (attribute.get_locus (), "attribute must be a string literal");
     272              : }
     273              : 
     274              : void
     275          102 : lint (const AST::Attribute &attribute)
     276              : {
     277          102 :   if (!attribute.has_attr_input ())
     278              :     {
     279            1 :       auto name = attribute.get_path ().as_string ();
     280            1 :       rust_error_at (attribute.get_locus (), "malformed %qs attribute input",
     281              :                      name.c_str ());
     282            1 :       rust_inform (attribute.get_locus (),
     283              :                    "must be of the form: %<#[%s(lint1, lint2, ...)]%>",
     284              :                    name.c_str ());
     285            1 :     }
     286          102 : }
     287              : 
     288              : void
     289            8 : link_name (const AST::Attribute &attribute)
     290              : {
     291            8 :   if (!attribute.has_attr_input ())
     292              :     {
     293            1 :       rust_error_at (attribute.get_locus (),
     294              :                      "malformed %<link_name%> attribute input");
     295            1 :       rust_inform (attribute.get_locus (),
     296              :                    "must be of the form: %<#[link_name = \"name\"]%>");
     297              :     }
     298            8 : }
     299              : 
     300              : namespace {
     301              : void
     302           58 : check_crate_type (const AST::Attribute &attribute)
     303              : {
     304           58 :   if (!Session::get_instance ().options.is_proc_macro ())
     305              :     {
     306            3 :       auto name = attribute.get_path ().as_string ();
     307              : 
     308            3 :       rust_error_at (attribute.get_locus (),
     309              :                      "the %<#[%s]%> attribute is only usable with crates of "
     310              :                      "the %<proc-macro%> crate type",
     311              :                      name.c_str ());
     312            3 :     }
     313           58 : }
     314              : } // namespace
     315              : 
     316              : static void
     317           20 : proc_macro_derive (const AST::Attribute &attribute)
     318              : {
     319           20 :   if (!attribute.has_attr_input ())
     320              :     {
     321            1 :       auto name = attribute.get_path ().as_string ();
     322            1 :       rust_error_at (attribute.get_locus (), "malformed %qs attribute input",
     323              :                      name.c_str ());
     324            1 :       rust_inform (attribute.get_locus (),
     325              :                    "must be of the form: %<#[proc_macro_derive(TraitName, "
     326              :                    "/*opt*/ attributes(name1, name2, ...))]%>");
     327            1 :     }
     328           20 :   check_crate_type (attribute);
     329           20 : }
     330              : 
     331              : static void
     332           38 : proc_macro (const AST::Attribute &attribute)
     333              : {
     334           38 :   check_crate_type (attribute);
     335           38 : }
     336              : 
     337              : static void
     338            4 : target_feature (const AST::Attribute &attribute)
     339              : {
     340            4 :   if (!attribute.has_attr_input ())
     341              :     {
     342            1 :       rust_error_at (attribute.get_locus (),
     343              :                      "malformed %<target_feature%> attribute input");
     344            1 :       rust_inform (attribute.get_locus (),
     345              :                    "must be of the form: %<#[target_feature(enable = "
     346              :                    "\"name\")]%>");
     347              :     }
     348            4 : }
     349              : 
     350              : void
     351            2 : no_mangle (const AST::Attribute &attribute)
     352              : {
     353            2 :   if (attribute.has_attr_input ())
     354              :     {
     355            1 :       rust_error_at (attribute.get_locus (), ErrorCode::E0754,
     356              :                      "malformed %<no_mangle%> attribute input");
     357            1 :       rust_inform (attribute.get_locus (),
     358              :                    "must be of the form: %<#[no_mangle]%>");
     359              :     }
     360            2 : }
     361              : 
     362              : } // namespace handlers
     363              : 
     364              : const std::unordered_map<std::string, std::function<void (AST::Attribute &)>>
     365              :   attribute_checking_handlers
     366              :   = {{Attrs::DOC, handlers::doc},
     367              :      {Attrs::DEPRECATED, handlers::deprecated},
     368              :      {Attrs::LINK_SECTION, handlers::link_section},
     369              :      {Attrs::EXPORT_NAME, handlers::export_name},
     370              :      {Attrs::NO_MANGLE, handlers::no_mangle},
     371              :      {Attrs::ALLOW, handlers::lint},
     372              :      {Attrs::DENY, handlers::lint},
     373              :      {Attrs::WARN, handlers::lint},
     374              :      {Attrs::FORBID, handlers::lint},
     375              :      {Attrs::LINK_NAME, handlers::link_name},
     376              :      {Attrs::PROC_MACRO_DERIVE, handlers::proc_macro_derive},
     377              :      {Attrs::PROC_MACRO, handlers::proc_macro},
     378              :      {Attrs::PROC_MACRO_ATTRIBUTE, handlers::proc_macro},
     379              :      {Attrs::TARGET_FEATURE, handlers::target_feature}};
     380              : 
     381              : tl::optional<std::function<void (AST::Attribute &)>>
     382        28684 : lookup_handler (std::string attr_name)
     383              : {
     384        28684 :   auto res = attribute_checking_handlers.find (attr_name);
     385        28684 :   if (res != attribute_checking_handlers.cend ())
     386         9128 :     return res->second;
     387        19556 :   return tl::nullopt;
     388              : }
     389              : 
     390              : static void
     391            2 : check_no_mangle_function (const AST::Attribute &attribute,
     392              :                           const AST::Function &fun)
     393              : {
     394            2 :   if (!is_ascii_only (fun.get_function_name ().as_string ()))
     395            0 :     rust_error_at (fun.get_function_name ().get_locus (),
     396              :                    "the %<#[no_mangle]%> attribute requires ASCII identifier");
     397            2 : }
     398              : 
     399              : /**
     400              :  * Emit an error when an attribute is attached
     401              :  * to an incompatable item type. e.g.:
     402              :  *
     403              :  * #[cold]
     404              :  * struct A(u8, u8);
     405              :  *
     406              :  * Note that "#[derive]" is handled
     407              :  * explicitly in rust-derive.cc
     408              :  */
     409              : void
     410        15064 : check_valid_attribute_for_item (const AST::Attribute &attr,
     411              :                                 const AST::Item &item)
     412              : {
     413        30128 :   if (item.get_item_kind () != AST::Item::Kind::Function
     414        35090 :       && (attr.get_path () == Values::Attributes::TARGET_FEATURE
     415        24986 :           || attr.get_path () == Values::Attributes::COLD
     416        20025 :           || attr.get_path () == Values::Attributes::INLINE))
     417              :     {
     418            1 :       rust_error_at (attr.get_locus (),
     419              :                      "the %<#[%s]%> attribute may only be applied to functions",
     420            2 :                      attr.get_path ().as_string ().c_str ());
     421              :     }
     422        30126 :   else if (attr.get_path () == Values::Attributes::REPR
     423           50 :            && item.get_item_kind () != AST::Item::Kind::Enum
     424           47 :            && item.get_item_kind () != AST::Item::Kind::Union
     425        15098 :            && item.get_item_kind () != AST::Item::Kind::Struct)
     426              :     {
     427            1 :       rust_error_at (attr.get_locus (),
     428              :                      "the %<#[%s]%> attribute may only be applied "
     429              :                      "to structs, enums and unions",
     430            2 :                      attr.get_path ().as_string ().c_str ());
     431              :     }
     432        15064 : }
     433              : 
     434         4684 : BuiltinAttributeChecker::BuiltinAttributeChecker () {}
     435              : 
     436              : void
     437         4684 : BuiltinAttributeChecker::go (AST::Crate &crate)
     438              : {
     439         4684 :   visit (crate);
     440         4684 : }
     441              : 
     442              : void
     443         4684 : BuiltinAttributeChecker::visit (AST::Crate &crate)
     444              : {
     445        16547 :   for (auto &attr : crate.get_inner_attrs ())
     446              :     {
     447        11863 :       check_inner_attribute (attr);
     448              :     }
     449              : 
     450         4684 :   AST::DefaultASTVisitor::visit (crate);
     451         4684 : }
     452              : 
     453              : void
     454        28684 : BuiltinAttributeChecker::visit (AST::Attribute &attribute)
     455              : {
     456        37812 :   lookup_handler (attribute.get_path ().as_string ()).map ([&] (auto handler) {
     457         9128 :     handler (attribute);
     458              :   });
     459        28684 :   AST::DefaultASTVisitor::visit (attribute);
     460        28684 : }
     461              : 
     462              : void
     463         1309 : BuiltinAttributeChecker::visit (AST::Module &module)
     464              : {
     465         1309 :   default_outer_attribute_check (module);
     466         1309 : }
     467              : 
     468              : void
     469           27 : BuiltinAttributeChecker::visit (AST::ExternCrate &extern_crate)
     470              : {
     471           27 :   default_outer_attribute_check (extern_crate);
     472           27 : }
     473              : 
     474              : void
     475          682 : BuiltinAttributeChecker::visit (AST::UseDeclaration &declaration)
     476              : {
     477          682 :   default_outer_attribute_check (declaration);
     478          682 : }
     479              : 
     480              : void
     481        18749 : BuiltinAttributeChecker::visit (AST::Function &function)
     482              : {
     483        18749 :   BuiltinAttrDefinition result;
     484        28851 :   for (auto &attribute : function.get_outer_attrs ())
     485              :     {
     486        10102 :       check_valid_attribute_for_item (attribute, function);
     487              : 
     488        10102 :       auto result = lookup_builtin (attribute);
     489        10102 :       if (!result)
     490            0 :         return;
     491              : 
     492        10102 :       if (result->name == Attrs::TARGET_FEATURE)
     493              :         {
     494            3 :           if (!function.get_qualifiers ().is_unsafe ())
     495              :             {
     496            1 :               rust_error_at (
     497              :                 attribute.get_locus (),
     498              :                 "the %<#[target_feature]%> attribute can only be applied "
     499              :                 "to %<unsafe%> functions");
     500              :             }
     501              :         }
     502        10099 :       else if (result->name == Attrs::NO_MANGLE)
     503              :         {
     504            2 :           check_no_mangle_function (attribute, function);
     505              :         }
     506        10102 :     }
     507              : 
     508        18749 :   AST::DefaultASTVisitor::visit (function);
     509        18749 : }
     510              : 
     511              : void
     512         1298 : BuiltinAttributeChecker::visit (AST::TypeAlias &alias)
     513              : {
     514         1298 :   default_outer_attribute_check (alias);
     515         1298 : }
     516              : 
     517              : void
     518         1601 : BuiltinAttributeChecker::visit (AST::StructStruct &struct_item)
     519              : {
     520         1601 :   default_outer_attribute_check (struct_item);
     521         1601 : }
     522              : 
     523              : void
     524          980 : BuiltinAttributeChecker::visit (AST::TupleStruct &tuple_struct)
     525              : {
     526          980 :   default_outer_attribute_check (tuple_struct);
     527          980 : }
     528              : 
     529              : void
     530          553 : BuiltinAttributeChecker::visit (AST::Enum &enumeration)
     531              : {
     532          553 :   default_outer_attribute_check (enumeration);
     533          553 : }
     534              : 
     535              : void
     536          111 : BuiltinAttributeChecker::visit (AST::Union &u)
     537              : {
     538          111 :   default_outer_attribute_check (u);
     539          111 : }
     540              : 
     541              : void
     542          585 : BuiltinAttributeChecker::visit (AST::ConstantItem &item)
     543              : {
     544          585 :   default_outer_attribute_check (item);
     545          585 : }
     546              : 
     547              : void
     548           64 : BuiltinAttributeChecker::visit (AST::StaticItem &item)
     549              : {
     550           72 :   for (auto &attr : item.get_outer_attrs ())
     551            8 :     check_valid_attribute_for_item (attr, item);
     552              : 
     553           64 :   AST::DefaultASTVisitor::visit (item);
     554           64 : }
     555              : 
     556              : void
     557         3895 : BuiltinAttributeChecker::visit (AST::Trait &trait)
     558              : {
     559         3895 :   default_outer_attribute_check (trait);
     560         3895 : }
     561              : 
     562              : void
     563          973 : BuiltinAttributeChecker::visit (AST::InherentImpl &impl)
     564              : {
     565          973 :   default_outer_attribute_check (impl);
     566          973 : }
     567              : 
     568              : void
     569         4742 : BuiltinAttributeChecker::visit (AST::TraitImpl &impl)
     570              : {
     571         4742 :   default_outer_attribute_check (impl);
     572         4742 : }
     573              : 
     574              : void
     575         1617 : BuiltinAttributeChecker::visit (AST::ExternBlock &block)
     576              : {
     577         1617 :   default_outer_attribute_check (block);
     578         1617 : }
     579              : 
     580              : } // namespace Analysis
     581              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.