LCOV - code coverage report
Current view: top level - gcc/rust/checks/errors/feature - rust-feature-gate.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 97.4 % 190 185
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 24 24
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-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-feature-gate.h"
      20              : #include "rust-abi.h"
      21              : #include "rust-attribute-values.h"
      22              : #include "rust-ast-visitor.h"
      23              : #include "rust-feature.h"
      24              : #include "rust-ast-full.h"
      25              : 
      26              : namespace Rust {
      27              : 
      28              : void
      29         4509 : FeatureGate::check (AST::Crate &crate)
      30              : {
      31         4509 :   visit (crate);
      32         4509 : }
      33              : 
      34              : void
      35         4509 : FeatureGate::visit (AST::Crate &crate)
      36              : {
      37         4509 :   valid_lang_features.clear ();
      38         4509 :   valid_lib_features.clear ();
      39              : 
      40              :   // avoid clearing defined features (?)
      41              : 
      42        15731 :   for (const auto &attr : crate.inner_attrs)
      43              :     {
      44        11222 :       if (attr.get_path ().as_string () == "feature")
      45              :         {
      46              :           // check for empty feature, such as `#![feature], this is an error
      47         6585 :           if (attr.empty_input ())
      48              :             {
      49            1 :               rust_error_at (attr.get_locus (), ErrorCode::E0556,
      50              :                              "malformed %<feature%> attribute input");
      51            1 :               continue;
      52              :             }
      53         6584 :           const auto &attr_input = attr.get_attr_input ();
      54         6584 :           auto type = attr_input.get_attr_input_type ();
      55         6584 :           if (type == AST::AttrInput::AttrInputType::TOKEN_TREE)
      56              :             {
      57         6583 :               const auto &option = static_cast<const AST::DelimTokenTree &> (
      58         6583 :                 attr.get_attr_input ());
      59         6583 :               std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
      60         6583 :                 option.parse_to_meta_item ());
      61        13333 :               for (const auto &item : meta_item->get_items ())
      62              :                 {
      63         6750 :                   const auto &name_str = item->as_string ();
      64              : 
      65              :                   // TODO: detect duplicates
      66         6750 :                   if (auto tname = Feature::as_name (name_str))
      67         6745 :                     valid_lang_features.insert (tname.value ());
      68              :                   else
      69            5 :                     valid_lib_features.emplace (name_str, item->get_locus ());
      70         6750 :                 }
      71         6583 :             }
      72            1 :           else if (type == AST::AttrInput::AttrInputType::META_ITEM)
      73              :             {
      74            1 :               const auto &meta_item
      75              :                 = static_cast<const AST::AttrInputMetaItemContainer &> (
      76              :                   attr_input);
      77            2 :               for (const auto &item : meta_item.get_items ())
      78              :                 {
      79            1 :                   const auto &name_str = item->as_string ();
      80              : 
      81              :                   // TODO: detect duplicates
      82            1 :                   if (auto tname = Feature::as_name (name_str))
      83            1 :                     valid_lang_features.insert (tname.value ());
      84              :                   else
      85            0 :                     valid_lib_features.emplace (name_str, item->get_locus ());
      86            1 :                 }
      87              :             }
      88              :         }
      89              :     }
      90              : 
      91         4509 :   AST::DefaultASTVisitor::visit (crate);
      92              : 
      93         4514 :   for (auto &ent : valid_lib_features)
      94              :     {
      95            5 :       const std::string &feature = ent.first;
      96            5 :       location_t locus = ent.second;
      97              : 
      98              :       // rustc treats these as valid,
      99              :       // but apparently has special handling for them
     100            5 :       if (feature == "libc" || feature == "test")
     101            0 :         continue;
     102              : 
     103            5 :       if (defined_lib_features.find (feature) != defined_lib_features.end ())
     104              :         {
     105              :           // TODO: emit warning if stable
     106            0 :           continue;
     107              :         }
     108              : 
     109            5 :       rust_error_at (locus, ErrorCode::E0635, "unknown feature %qs",
     110              :                      feature.c_str ());
     111              :     }
     112         4509 : }
     113              : 
     114              : void
     115         3835 : FeatureGate::gate (Feature::Name name, location_t loc,
     116              :                    const std::string &error_msg)
     117              : {
     118         3835 :   if (!valid_lang_features.count (name))
     119              :     {
     120           13 :       auto &feature = Feature::lookup (name);
     121           13 :       if (auto issue = feature.issue ())
     122              :         {
     123            7 :           auto issue_number = issue.value ();
     124            7 :           const char *fmt_str
     125              :             = "%s. see issue %u "
     126              :               "<https://github.com/rust-lang/rust/issues/%u> for more "
     127              :               "information. add `#![feature(%s)]` to the crate attributes to "
     128              :               "enable.";
     129            7 :           rust_error_at (loc, ErrorCode::E0658, fmt_str, error_msg.c_str (),
     130              :                          issue_number, issue_number,
     131            7 :                          feature.as_string ().c_str ());
     132              :         }
     133              :       else
     134              :         {
     135            6 :           const char *fmt_str
     136              :             = "%s. add `#![feature(%s)]` to the crate attributes to enable.";
     137            6 :           rust_error_at (loc, ErrorCode::E0658, fmt_str, error_msg.c_str (),
     138            6 :                          feature.as_string ().c_str ());
     139              :         }
     140              :     }
     141         3835 : }
     142              : 
     143              : void
     144         1471 : FeatureGate::visit (AST::ExternBlock &block)
     145              : {
     146         1471 :   if (block.has_abi ())
     147              :     {
     148         1471 :       const auto abi = block.get_abi ();
     149              : 
     150         1471 :       if (get_abi_from_string (abi) == ABI::INTRINSIC)
     151          409 :         gate (Feature::Name::INTRINSICS, block.get_locus (),
     152          818 :               "intrinsics are subject to change");
     153         1471 :     }
     154         1471 :   AST::DefaultASTVisitor::visit (block);
     155         1471 : }
     156              : 
     157              : void
     158        17040 : FeatureGate::check_rustc_attri (const std::vector<AST::Attribute> &attributes)
     159              : {
     160        27130 :   for (const AST::Attribute &attr : attributes)
     161              :     {
     162        10090 :       auto name = attr.get_path ().as_string ();
     163        10090 :       if (name.rfind ("rustc_", 0) == 0)
     164              :         {
     165          263 :           gate (Feature::Name::RUSTC_ATTRS, attr.get_locus (),
     166          526 :                 "internal implementation detail");
     167              :         }
     168        10090 :     }
     169        17040 : }
     170              : 
     171              : void
     172         8505 : FeatureGate::check_may_dangle_attribute (
     173              :   const std::vector<AST::Attribute> &attributes)
     174              : {
     175         8510 :   for (const AST::Attribute &attr : attributes)
     176              :     {
     177            5 :       if (attr.get_path ().as_string () == Values::Attributes::MAY_DANGLE)
     178            5 :         gate (Feature::Name::DROPCK_EYEPATCH, attr.get_locus (),
     179           10 :               "`may_dangle` has unstable semantics and may be removed in the "
     180              :               "future");
     181              :     }
     182         8505 : }
     183              : 
     184              : void
     185        25350 : FeatureGate::check_lang_item_attribute (
     186              :   const std::vector<AST::Attribute> &attributes)
     187              : {
     188        40073 :   for (const AST::Attribute &attr : attributes)
     189              :     {
     190        14723 :       const auto &str_path = attr.get_path ().as_string ();
     191        14723 :       bool is_lang_item = str_path == Values::Attributes::LANG
     192         3105 :                           && attr.has_attr_input ()
     193        17828 :                           && attr.get_attr_input ().get_attr_input_type ()
     194        14723 :                                == AST::AttrInput::AttrInputType::LITERAL;
     195              : 
     196         3105 :       if (is_lang_item)
     197         3105 :         gate (Feature::Name::LANG_ITEMS, attr.get_locus (),
     198         6210 :               "lang items are subject to change");
     199        14723 :     }
     200        25350 : }
     201              : 
     202              : void
     203        19252 : FeatureGate::note_stability_attribute (
     204              :   const std::vector<AST::Attribute> &attributes)
     205              : {
     206        29513 :   for (const AST::Attribute &attr : attributes)
     207              :     {
     208        10261 :       std::string attr_name = attr.get_path ().as_string ();
     209              : 
     210        10261 :       Stability stability;
     211              : 
     212        10261 :       if (attr_name == Values::Attributes::STABLE)
     213          643 :         stability = Stability::STABLE;
     214         9618 :       else if (attr_name == Values::Attributes::UNSTABLE)
     215          100 :         stability = Stability::UNSTABLE;
     216         9518 :       else if (attr_name == Values::Attributes::RUSTC_CONST_STABLE)
     217          158 :         stability = Stability::STABLE;
     218         9360 :       else if (attr_name == Values::Attributes::RUSTC_CONST_UNSTABLE)
     219            8 :         stability = Stability::UNSTABLE;
     220              :       else
     221         9352 :         continue;
     222              : 
     223          909 :       if (attr.empty_input ())
     224              :         // TODO: error?
     225            0 :         continue;
     226              : 
     227          909 :       auto &attr_input = attr.get_attr_input ();
     228          909 :       if (attr_input.get_attr_input_type ()
     229              :           != AST::AttrInput::AttrInputType::TOKEN_TREE)
     230              :         // TODO: error?
     231            0 :         continue;
     232              : 
     233          909 :       std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
     234              :         static_cast<const AST::DelimTokenTree &> (attr_input)
     235          909 :           .parse_to_meta_item ());
     236              : 
     237         2727 :       for (auto &item : meta_item->get_items ())
     238              :         {
     239              :           // TODO: more thorough error checking?
     240              :           // ~only the standard libraries should ever exercise this
     241         1818 :           if (item->is_key_value_pair ())
     242              :             {
     243         1818 :               auto &pair = static_cast<const AST::MetaNameValueStr &> (*item);
     244         1818 :               if (pair.get_name ().as_string () == "feature")
     245          909 :                 defined_lib_features.emplace (pair.get_value (), stability);
     246              :             }
     247              :         }
     248        10261 :     }
     249        19252 : }
     250              : 
     251              : void
     252          967 : FeatureGate::visit (AST::MacroRulesDefinition &rules_def)
     253              : {
     254          967 :   check_rustc_attri (rules_def.get_outer_attrs ());
     255          967 :   note_stability_attribute (rules_def.get_outer_attrs ());
     256          967 : }
     257              : 
     258              : void
     259        18285 : FeatureGate::visit (AST::Function &function)
     260              : {
     261        18285 :   if (!function.is_external ())
     262        16073 :     check_rustc_attri (function.get_outer_attrs ());
     263              : 
     264        18285 :   check_lang_item_attribute (function.get_outer_attrs ());
     265              : 
     266        18285 :   note_stability_attribute (function.get_outer_attrs ());
     267              : 
     268        18285 :   AST::DefaultASTVisitor::visit (function);
     269        18285 : }
     270              : 
     271              : void
     272            1 : FeatureGate::visit (AST::ExternalTypeItem &item)
     273              : {
     274              :   // TODO(mxlol233): The gating needs a complete visiting chain to activate
     275              :   // `AST::ExternalTypeItem`.
     276            1 :   gate (Feature::Name::EXTERN_TYPES, item.get_locus (),
     277            1 :         "extern types are experimental");
     278            1 : }
     279              : 
     280              : void
     281         4739 : FeatureGate::visit (AST::TraitImpl &impl)
     282              : {
     283         4739 :   if (impl.is_exclam ())
     284            6 :     gate (Feature::Name::NEGATIVE_IMPLS, impl.get_locus (),
     285           12 :           "negative_impls are not yet implemented");
     286              : 
     287         4739 :   AST::DefaultASTVisitor::visit (impl);
     288         4739 : }
     289              : 
     290              : void
     291         3736 : FeatureGate::visit (AST::Trait &trait)
     292              : {
     293         3736 :   if (trait.is_auto ())
     294           20 :     gate (Feature::Name::OPTIN_BUILTIN_TRAITS, trait.get_locus (),
     295           40 :           "auto traits are experimental and possibly buggy");
     296         3736 :   check_lang_item_attribute (trait.get_outer_attrs ());
     297         3736 :   AST::DefaultASTVisitor::visit (trait);
     298         3736 : }
     299              : 
     300              : void
     301            1 : FeatureGate::visit (AST::BoxExpr &expr)
     302              : {
     303            1 :   gate (
     304              :     Feature::Name::BOX_SYNTAX, expr.get_locus (),
     305            1 :     "box expression syntax is experimental; you can call `Box::new` instead");
     306            1 :   AST::DefaultASTVisitor::visit (expr);
     307            1 : }
     308              : 
     309              : void
     310          277 : FeatureGate::visit (AST::LifetimeParam &lifetime_param)
     311              : {
     312          277 :   check_may_dangle_attribute (lifetime_param.get_outer_attrs ());
     313          277 :   AST::DefaultASTVisitor::visit (lifetime_param);
     314          277 : }
     315              : 
     316              : void
     317           97 : FeatureGate::visit (AST::ConstGenericParam &const_param)
     318              : {
     319           97 :   check_may_dangle_attribute (const_param.get_outer_attrs ());
     320           97 :   AST::DefaultASTVisitor::visit (const_param);
     321           97 : }
     322              : 
     323              : void
     324         8131 : FeatureGate::visit (AST::TypeParam &param)
     325              : {
     326         8131 :   check_may_dangle_attribute (param.get_outer_attrs ());
     327         8131 :   AST::DefaultASTVisitor::visit (param);
     328         8131 : }
     329              : 
     330              : void
     331         1929 : FeatureGate::visit (AST::BorrowExpr &expr)
     332              : {
     333         1929 :   if (expr.is_raw_borrow ())
     334            5 :     gate (Feature::Name::RAW_REF_OP, expr.get_locus (),
     335           10 :           "raw address of syntax is experimental");
     336         1929 : }
     337              : 
     338              : void
     339           51 : FeatureGate::visit (AST::RangePattern &pattern)
     340              : {
     341           51 :   if (pattern.get_range_kind () == AST::RangeKind::EXCLUDED)
     342           20 :     gate (Feature::Name::EXCLUSIVE_RANGE_PATTERN, pattern.get_locus (),
     343           40 :           "exclusive range pattern syntax is experimental");
     344           51 : }
     345              : 
     346              : void
     347           10 : FeatureGate::visit (AST::UseTreeGlob &use)
     348              : {
     349              :   // At the moment, UseTrees do not have outer attributes, but they should. we
     350              :   // need to eventually gate `#[prelude_import]` on use-trees based on the
     351              :   // #[feature(prelude_import)]
     352           10 : }
     353              : 
     354              : void
     355         1591 : FeatureGate::visit (AST::StructStruct &struct_item)
     356              : {
     357         1591 :   check_lang_item_attribute (struct_item.get_outer_attrs ());
     358         1591 :   AST::DefaultASTVisitor::visit (struct_item);
     359         1591 : }
     360              : 
     361              : void
     362          738 : FeatureGate::visit (AST::TraitItemType &trait_item_type)
     363              : {
     364          738 :   check_lang_item_attribute (trait_item_type.get_outer_attrs ());
     365          738 :   AST::DefaultASTVisitor::visit (trait_item_type);
     366          738 : }
     367              : 
     368              : void
     369          544 : FeatureGate::visit (AST::Enum &enum_item)
     370              : {
     371          544 :   check_lang_item_attribute (enum_item.get_outer_attrs ());
     372          544 :   AST::DefaultASTVisitor::visit (enum_item);
     373          544 : }
     374              : 
     375              : void
     376          456 : FeatureGate::visit (AST::EnumItem &enum_variant)
     377              : {
     378          456 :   check_lang_item_attribute (enum_variant.get_outer_attrs ());
     379          456 :   AST::DefaultASTVisitor::visit (enum_variant);
     380          456 : }
     381              : 
     382              : } // 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.