LCOV - code coverage report
Current view: top level - gcc/rust/parse - rust-parse-impl-attribute.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 85.8 % 148 127
Test Date: 2026-03-28 14:25:54 Functions: 94.4 % 18 17
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2025-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              : /* DO NOT INCLUDE ANYWHERE - this is automatically included
      20              :  *   by rust-parse-impl*.h
      21              :  * This is also the reason why there are no include guards. */
      22              : 
      23              : #include "rust-parse.h"
      24              : #include "rust-parse-error.h"
      25              : #include "rust-attribute-values.h"
      26              : #include "expected.h"
      27              : 
      28              : namespace Rust {
      29              : 
      30              : // Parse a inner or outer doc comment into an doc attribute
      31              : template <typename ManagedTokenSource>
      32              : Parse::AttributeBody
      33         8176 : Parser<ManagedTokenSource>::parse_doc_comment ()
      34              : {
      35         8176 :   const_TokenPtr token = lexer.peek_token ();
      36         8176 :   location_t locus = token->get_locus ();
      37         8176 :   AST::SimplePathSegment segment (Values::Attributes::DOC, locus);
      38         8176 :   std::vector<AST::SimplePathSegment> segments;
      39         8176 :   segments.push_back (std::move (segment));
      40         8176 :   AST::SimplePath attr_path (std::move (segments), false, locus);
      41        16352 :   AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
      42              :                              PrimitiveCoreType::CORETYPE_STR, {}, locus);
      43         8176 :   std::unique_ptr<AST::AttrInput> attr_input (
      44         8176 :     new AST::AttrInputLiteral (std::move (lit_expr)));
      45         8176 :   lexer.skip_token ();
      46              : 
      47              :   return Parse::AttributeBody{std::move (attr_path), std::move (attr_input),
      48         8176 :                               locus};
      49         8176 : }
      50              : 
      51              : // Parse a single inner attribute.
      52              : template <typename ManagedTokenSource>
      53              : tl::expected<AST::Attribute, Parse::Error::Attribute>
      54        11852 : Parser<ManagedTokenSource>::parse_inner_attribute ()
      55              : {
      56        23704 :   if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
      57              :     {
      58          171 :       auto body = parse_doc_comment ();
      59          342 :       return AST::Attribute (std::move (body.path), std::move (body.input),
      60          171 :                              body.locus, true);
      61          171 :     }
      62              : 
      63        11681 :   rust_assert (lexer.peek_token ()->get_id () == HASH);
      64              : 
      65        11681 :   lexer.skip_token ();
      66              : 
      67        23362 :   if (lexer.peek_token ()->get_id () != EXCLAM)
      68              :     {
      69            0 :       Error error (lexer.peek_token ()->get_locus (),
      70              :                    "expected %<!%> or %<[%> for inner attribute");
      71            0 :       add_error (std::move (error));
      72              : 
      73            0 :       return Parse::Error::Attribute::make_malformed ();
      74            0 :     }
      75        11681 :   lexer.skip_token ();
      76              : 
      77        11681 :   if (!skip_token (LEFT_SQUARE))
      78              :     return Parse::Error::Attribute::make_malformed ();
      79              : 
      80        11681 :   auto body_res = parse_attribute_body ();
      81        11681 :   if (!body_res)
      82              :     return Parse::Error::Attribute::make_malformed ();
      83        11681 :   auto body = std::move (body_res.value ());
      84              : 
      85        11681 :   auto actual_attribute
      86        23362 :     = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
      87              :                       true);
      88              : 
      89        11681 :   if (!skip_token (RIGHT_SQUARE))
      90              :     return Parse::Error::Attribute::make_malformed ();
      91              : 
      92        11681 :   return actual_attribute;
      93        23362 : }
      94              : 
      95              : // Parse a single outer attribute.
      96              : template <typename ManagedTokenSource>
      97              : tl::expected<AST::Attribute, Parse::Error::Attribute>
      98        16818 : Parser<ManagedTokenSource>::parse_outer_attribute ()
      99              : {
     100        33636 :   if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
     101              :     {
     102         8005 :       auto body = parse_doc_comment ();
     103        16010 :       return AST::Attribute (std::move (body.path), std::move (body.input),
     104         8005 :                              body.locus, false);
     105         8005 :     }
     106              : 
     107        17626 :   if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
     108              :     {
     109            2 :       Error error (
     110            2 :         lexer.peek_token ()->get_locus (), ErrorCode::E0753,
     111              :         "expected outer doc comment, inner doc (%<//!%> or %</*!%>) only "
     112              :         "allowed at start of item "
     113              :         "and before any outer attribute or doc (%<#[%>, %<///%> or %</**%>)");
     114            2 :       add_error (std::move (error));
     115            2 :       lexer.skip_token ();
     116            2 :       return Parse::Error::Attribute::make_unexpected_inner ();
     117            2 :     }
     118              : 
     119              :   /* OuterAttribute -> '#' '[' Attr ']' */
     120              : 
     121        17622 :   if (lexer.peek_token ()->get_id () != HASH)
     122              :     return Parse::Error::Attribute::make_malformed ();
     123              : 
     124         8811 :   lexer.skip_token ();
     125              : 
     126         8811 :   TokenId id = lexer.peek_token ()->get_id ();
     127         8811 :   if (id != LEFT_SQUARE)
     128              :     {
     129            0 :       if (id == EXCLAM)
     130              :         {
     131              :           // this is inner attribute syntax, so throw error
     132              :           // inner attributes were either already parsed or not allowed here.
     133            0 :           Error error (
     134            0 :             lexer.peek_token ()->get_locus (),
     135              :             "token %<!%> found, indicating inner attribute definition. Inner "
     136              :             "attributes are not possible at this location");
     137            0 :           add_error (std::move (error));
     138            0 :         }
     139              :       return Parse::Error::Attribute::make_unexpected_inner ();
     140              :     }
     141              : 
     142         8811 :   lexer.skip_token ();
     143              : 
     144         8811 :   auto body_res = parse_attribute_body ();
     145         8811 :   if (!body_res)
     146              :     return Parse::Error::Attribute::make_malformed_body ();
     147         8804 :   auto body = std::move (body_res.value ());
     148              : 
     149         8804 :   auto actual_attribute
     150        17608 :     = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
     151              :                       false);
     152              : 
     153        17608 :   if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
     154              :     return Parse::Error::Attribute::make_malformed ();
     155              : 
     156         8804 :   lexer.skip_token ();
     157              : 
     158         8804 :   return actual_attribute;
     159        17615 : }
     160              : 
     161              : // Parses the body of an attribute (inner or outer).
     162              : template <typename ManagedTokenSource>
     163              : tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
     164        20539 : Parser<ManagedTokenSource>::parse_attribute_body ()
     165              : {
     166        20539 :   location_t locus = lexer.peek_token ()->get_locus ();
     167              : 
     168        20539 :   auto attr_path = parse_simple_path ();
     169              :   // ensure path is valid to parse attribute input
     170        20539 :   if (!attr_path)
     171              :     {
     172            0 :       Error error (lexer.peek_token ()->get_locus (),
     173              :                    "empty simple path in attribute");
     174            0 :       add_error (std::move (error));
     175              : 
     176              :       // Skip past potential further info in attribute (i.e. attr_input)
     177            0 :       skip_after_end_attribute ();
     178            0 :       return Parse::Error::AttributeBody::make_invalid_path ();
     179            0 :     }
     180              : 
     181        20539 :   auto attr_input = parse_attr_input ();
     182              :   // AttrInput is allowed to be null, so no checks here
     183        20539 :   if (attr_input)
     184        14142 :     return Parse::AttributeBody{std::move (attr_path.value ()),
     185        28284 :                                 std::move (attr_input.value ()), locus};
     186         6397 :   else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
     187        12780 :     return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
     188              :   else
     189              :     return Parse::Error::AttributeBody::make_invalid_attrinput ();
     190        20539 : }
     191              : 
     192              : // Parse a contiguous block of inner attributes.
     193              : template <typename ManagedTokenSource>
     194              : AST::AttrVec
     195        52889 : Parser<ManagedTokenSource>::parse_inner_attributes ()
     196              : {
     197        52889 :   AST::AttrVec inner_attributes;
     198              : 
     199       117630 :   auto has_valid_inner_attribute_prefix = [&] () {
     200        64741 :     auto id = lexer.peek_token ()->get_id ();
     201              :     /* Outer attribute `#[` is not allowed, only accepts `#!` */
     202        81354 :     return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
     203        67207 :            || id == INNER_DOC_COMMENT;
     204              :   };
     205              : 
     206        64741 :   while (has_valid_inner_attribute_prefix ())
     207              :     {
     208        11852 :       auto inner_attr = parse_inner_attribute ();
     209              : 
     210              :       /* Ensure only valid inner attributes are added to the inner_attributes
     211              :        * list */
     212        11852 :       if (inner_attr)
     213              :         {
     214        11852 :           inner_attributes.push_back (std::move (inner_attr.value ()));
     215              :         }
     216              :       else
     217              :         {
     218              :           /* If no more valid inner attributes, break out of loop (only
     219              :            * contiguous inner attributes parsed). */
     220              :           break;
     221              :         }
     222              :     }
     223              : 
     224        52889 :   inner_attributes.shrink_to_fit ();
     225        52889 :   return inner_attributes;
     226              : }
     227              : 
     228              : // Parses a contiguous block of outer attributes.
     229              : template <typename ManagedTokenSource>
     230              : AST::AttrVec
     231       105381 : Parser<ManagedTokenSource>::parse_outer_attributes ()
     232              : {
     233       105381 :   AST::AttrVec outer_attributes;
     234              : 
     235       227571 :   auto has_valid_attribute_prefix = [&] () {
     236       122190 :     auto id = lexer.peek_token ()->get_id ();
     237              :     /* We allow inner attributes `#!` and catch the error later  */
     238       122190 :     return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
     239              :   };
     240              : 
     241       122199 :   while (has_valid_attribute_prefix ()) /* For error handling.  */
     242              :     {
     243        16818 :       auto outer_attr = parse_outer_attribute ();
     244              : 
     245              :       /* Ensure only valid outer attributes are added to the outer_attributes
     246              :        * list */
     247        16818 :       if (outer_attr)
     248              :         {
     249        16809 :           outer_attributes.push_back (std::move (outer_attr.value ()));
     250              :         }
     251              :       else
     252              :         {
     253              :           /* If no more valid outer attributes, break out of loop (only
     254              :            * contiguous outer attributes parsed). */
     255              :           break;
     256              :         }
     257              :     }
     258              : 
     259       105381 :   outer_attributes.shrink_to_fit ();
     260       105381 :   return outer_attributes;
     261              : 
     262              :   /* TODO: this shares basically all code with parse_inner_attributes except
     263              :    * function call - find way of making it more modular? function pointer? */
     264              : }
     265              : 
     266              : // Parses an AttrInput AST node (polymorphic, as AttrInput is abstract)
     267              : template <typename ManagedTokenSource>
     268              : tl::expected<std::unique_ptr<AST::AttrInput>, Parse::Error::AttrInput>
     269        20539 : Parser<ManagedTokenSource>::parse_attr_input ()
     270              : {
     271        20539 :   const_TokenPtr t = lexer.peek_token ();
     272        20539 :   switch (t->get_id ())
     273              :     {
     274        10574 :     case LEFT_PAREN:
     275              :     case LEFT_SQUARE:
     276              :     case LEFT_CURLY:
     277              :       {
     278        10574 :         auto dtoken_tree = parse_delim_token_tree ();
     279        10574 :         if (!dtoken_tree)
     280              :           return Parse::Error::AttrInput::make_bad_token_tree ();
     281              : 
     282              :         // must be a delimited token tree, so parse that
     283        10567 :         std::unique_ptr<AST::AttrInput> input_tree (
     284        10567 :           new AST::DelimTokenTree (dtoken_tree.value ()));
     285              : 
     286              :         return tl::expected<std::unique_ptr<AST::AttrInput>,
     287        10567 :                             Parse::Error::AttrInput>{std::move (input_tree)};
     288        21141 :       }
     289         3575 :     case EQUAL:
     290              :       {
     291              :         // = LiteralExpr
     292         3575 :         lexer.skip_token ();
     293              : 
     294         3575 :         t = lexer.peek_token ();
     295              : 
     296              :         /* Ensure token is a "literal expression" (literally only a literal
     297              :          * token of any type) */
     298         3579 :         if (!t->is_literal ())
     299              :           {
     300            4 :             Error error (
     301              :               t->get_locus (),
     302              :               "arbitrary expressions in key-value attributes are unstable");
     303            4 :             collect_potential_gating_error (
     304              :               Feature::Name::EXTENDED_KEY_VALUE_ATTRIBUTES, std::move (error));
     305            4 :           }
     306              :         // attempt to parse macro
     307              :         // TODO: macros may/may not be allowed in attributes
     308              :         // this is needed for "#[doc = include_str!(...)]"
     309         3575 :         if (Parse::Utils::is_simple_path_segment (t->get_id ()))
     310              :           {
     311            4 :             std::unique_ptr<AST::MacroInvocation> invoke
     312            4 :               = parse_macro_invocation ({});
     313              : 
     314            4 :             if (!invoke)
     315              :               return Parse::Error::AttrInput::make_bad_macro_invocation ();
     316              : 
     317            4 :             return std::unique_ptr<AST::AttrInput> (
     318            4 :               new AST::AttrInputMacro (std::move (invoke)));
     319            4 :           }
     320              : 
     321         3571 :         AST::Literal::LitType lit_type = AST::Literal::STRING;
     322              :         // Crappy mapping of token type to literal type
     323         3571 :         switch (t->get_id ())
     324              :           {
     325              :           case INT_LITERAL:
     326              :             lit_type = AST::Literal::INT;
     327              :             break;
     328              :           case FLOAT_LITERAL:
     329              :             lit_type = AST::Literal::FLOAT;
     330              :             break;
     331              :           case CHAR_LITERAL:
     332              :             lit_type = AST::Literal::CHAR;
     333              :             break;
     334              :           case BYTE_CHAR_LITERAL:
     335              :             lit_type = AST::Literal::BYTE;
     336              :             break;
     337              :           case BYTE_STRING_LITERAL:
     338              :             lit_type = AST::Literal::BYTE_STRING;
     339              :             break;
     340              :           case RAW_STRING_LITERAL:
     341              :             lit_type = AST::Literal::RAW_STRING;
     342              :             break;
     343              :           case STRING_LITERAL:
     344              :             lit_type = AST::Literal::STRING;
     345              :             break; // TODO: raw string? don't eliminate it from lexer?
     346            0 :           default:
     347            0 :             rust_sorry_at (t->get_locus (),
     348              :                            "Unsupported attribute input, only literals and "
     349              :                            "macros are supported for now");
     350            0 :             skip_after_end_attribute ();
     351              :             return Parse::Error::AttrInput::make_malformed ();
     352              :           }
     353              : 
     354              :         // create actual LiteralExpr
     355        10711 :         AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
     356              :                                    {}, t->get_locus ());
     357         3571 :         lexer.skip_token ();
     358              : 
     359         3571 :         std::unique_ptr<AST::AttrInput> attr_input_lit (
     360         3571 :           new AST::AttrInputLiteral (std::move (lit_expr)));
     361              : 
     362              :         // do checks or whatever? none required, really
     363              : 
     364              :         // FIXME: shouldn't a skip token be required here?
     365              : 
     366              :         return tl::expected<std::unique_ptr<AST::AttrInput>,
     367              :                             Parse::Error::AttrInput>{
     368         3571 :           std::move (attr_input_lit)};
     369         3571 :       }
     370              :       break;
     371         6390 :     case RIGHT_PAREN:
     372              :     case RIGHT_SQUARE:
     373              :     case RIGHT_CURLY:
     374              :     case END_OF_FILE:
     375              :       // means AttrInput is missing, which is allowed
     376              :       return Parse::Error::AttrInput::make_missing_attrinput ();
     377            0 :     default:
     378            0 :       add_error (
     379            0 :         Error (t->get_locus (),
     380              :                "unknown token %qs in attribute body - attribute input or "
     381              :                "none expected",
     382              :                t->get_token_description ()));
     383              : 
     384            0 :       skip_after_end_attribute ();
     385              :       return Parse::Error::AttrInput::make_malformed ();
     386              :     }
     387              :   rust_unreachable ();
     388              :   // TODO: find out how to stop gcc error on "no return value"
     389        20539 : }
     390              : 
     391              : } // 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.