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: 84.4 % 147 124
Test Date: 2026-02-28 14:20:25 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        11813 : Parser<ManagedTokenSource>::parse_inner_attribute ()
      55              : {
      56        23626 :   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        11642 :   rust_assert (lexer.peek_token ()->get_id () == HASH);
      64              : 
      65        11642 :   lexer.skip_token ();
      66              : 
      67        23284 :   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        11642 :   lexer.skip_token ();
      76              : 
      77        11642 :   if (!skip_token (LEFT_SQUARE))
      78              :     return Parse::Error::Attribute::make_malformed ();
      79              : 
      80        11642 :   auto body_res = parse_attribute_body ();
      81        11642 :   if (!body_res)
      82              :     return Parse::Error::Attribute::make_malformed ();
      83        11642 :   auto body = std::move (body_res.value ());
      84              : 
      85        11642 :   auto actual_attribute
      86        11642 :     = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
      87              :                       true);
      88              : 
      89        11642 :   if (!skip_token (RIGHT_SQUARE))
      90              :     return Parse::Error::Attribute::make_malformed ();
      91              : 
      92        11642 :   return actual_attribute;
      93        23284 : }
      94              : 
      95              : // Parse a single outer attribute.
      96              : template <typename ManagedTokenSource>
      97              : tl::expected<AST::Attribute, Parse::Error::Attribute>
      98        16799 : Parser<ManagedTokenSource>::parse_outer_attribute ()
      99              : {
     100        33598 :   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        17588 :   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        17584 :   if (lexer.peek_token ()->get_id () != HASH)
     122              :     return Parse::Error::Attribute::make_malformed ();
     123              : 
     124         8792 :   lexer.skip_token ();
     125              : 
     126         8792 :   TokenId id = lexer.peek_token ()->get_id ();
     127         8792 :   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         8792 :   lexer.skip_token ();
     143              : 
     144         8792 :   auto body_res = parse_attribute_body ();
     145         8792 :   if (!body_res)
     146              :     return Parse::Error::Attribute::make_malformed_body ();
     147         8785 :   auto body = std::move (body_res.value ());
     148              : 
     149         8785 :   auto actual_attribute
     150         8785 :     = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
     151              :                       false);
     152              : 
     153        17570 :   if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
     154              :     return Parse::Error::Attribute::make_malformed ();
     155              : 
     156         8785 :   lexer.skip_token ();
     157              : 
     158         8785 :   return actual_attribute;
     159        17577 : }
     160              : 
     161              : // Parses the body of an attribute (inner or outer).
     162              : template <typename ManagedTokenSource>
     163              : tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
     164        20480 : Parser<ManagedTokenSource>::parse_attribute_body ()
     165              : {
     166        20480 :   location_t locus = lexer.peek_token ()->get_locus ();
     167              : 
     168        20480 :   auto attr_path = parse_simple_path ();
     169              :   // ensure path is valid to parse attribute input
     170        20480 :   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        20480 :   auto attr_input = parse_attr_input ();
     182              :   // AttrInput is allowed to be null, so no checks here
     183        20480 :   if (attr_input)
     184        14101 :     return Parse::AttributeBody{std::move (attr_path.value ()),
     185        28202 :                                 std::move (attr_input.value ()), locus};
     186         6379 :   else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
     187        12744 :     return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
     188              :   else
     189              :     return Parse::Error::AttributeBody::make_invalid_attrinput ();
     190        20480 : }
     191              : 
     192              : // Parse a contiguous block of inner attributes.
     193              : template <typename ManagedTokenSource>
     194              : AST::AttrVec
     195        52710 : Parser<ManagedTokenSource>::parse_inner_attributes ()
     196              : {
     197        52710 :   AST::AttrVec inner_attributes;
     198              : 
     199       117233 :   auto has_valid_inner_attribute_prefix = [&] () {
     200        64523 :     auto id = lexer.peek_token ()->get_id ();
     201              :     /* Outer attribute `#[` is not allowed, only accepts `#!` */
     202        81085 :     return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
     203        66983 :            || id == INNER_DOC_COMMENT;
     204              :   };
     205              : 
     206        64523 :   while (has_valid_inner_attribute_prefix ())
     207              :     {
     208        11813 :       auto inner_attr = parse_inner_attribute ();
     209              : 
     210              :       /* Ensure only valid inner attributes are added to the inner_attributes
     211              :        * list */
     212        11813 :       if (inner_attr)
     213              :         {
     214        11813 :           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        52710 :   inner_attributes.shrink_to_fit ();
     225        52710 :   return inner_attributes;
     226              : }
     227              : 
     228              : // Parses a contiguous block of outer attributes.
     229              : template <typename ManagedTokenSource>
     230              : AST::AttrVec
     231       105152 : Parser<ManagedTokenSource>::parse_outer_attributes ()
     232              : {
     233       105152 :   AST::AttrVec outer_attributes;
     234              : 
     235       227094 :   auto has_valid_attribute_prefix = [&] () {
     236       121942 :     auto id = lexer.peek_token ()->get_id ();
     237              :     /* We allow inner attributes `#!` and catch the error later  */
     238       121942 :     return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
     239              :   };
     240              : 
     241       121951 :   while (has_valid_attribute_prefix ()) /* For error handling.  */
     242              :     {
     243        16799 :       auto outer_attr = parse_outer_attribute ();
     244              : 
     245              :       /* Ensure only valid outer attributes are added to the outer_attributes
     246              :        * list */
     247        16799 :       if (outer_attr)
     248              :         {
     249        16790 :           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       105152 :   outer_attributes.shrink_to_fit ();
     260       105152 :   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        20480 : Parser<ManagedTokenSource>::parse_attr_input ()
     270              : {
     271        20480 :   const_TokenPtr t = lexer.peek_token ();
     272        20480 :   switch (t->get_id ())
     273              :     {
     274        10542 :     case LEFT_PAREN:
     275              :     case LEFT_SQUARE:
     276              :     case LEFT_CURLY:
     277              :       {
     278        10542 :         auto dtoken_tree = parse_delim_token_tree ();
     279        10542 :         if (!dtoken_tree)
     280              :           return Parse::Error::AttrInput::make_bad_token_tree ();
     281              : 
     282              :         // must be a delimited token tree, so parse that
     283        10535 :         std::unique_ptr<AST::AttrInput> input_tree (
     284        10535 :           new AST::DelimTokenTree (dtoken_tree.value ()));
     285              : 
     286              :         return tl::expected<std::unique_ptr<AST::AttrInput>,
     287        10535 :                             Parse::Error::AttrInput>{std::move (input_tree)};
     288        21077 :       }
     289         3566 :     case EQUAL:
     290              :       {
     291              :         // = LiteralExpr
     292         3566 :         lexer.skip_token ();
     293              : 
     294         3566 :         t = lexer.peek_token ();
     295              : 
     296              :         // attempt to parse macro
     297              :         // TODO: macros may/may not be allowed in attributes
     298              :         // this is needed for "#[doc = include_str!(...)]"
     299         3566 :         if (Parse::Utils::is_simple_path_segment (t->get_id ()))
     300              :           {
     301            2 :             std::unique_ptr<AST::MacroInvocation> invoke
     302            2 :               = parse_macro_invocation ({});
     303              : 
     304            2 :             if (!invoke)
     305              :               return Parse::Error::AttrInput::make_bad_macro_invocation ();
     306              : 
     307            2 :             return std::unique_ptr<AST::AttrInput> (
     308            2 :               new AST::AttrInputMacro (std::move (invoke)));
     309            2 :           }
     310              : 
     311              :         /* Ensure token is a "literal expression" (literally only a literal
     312              :          * token of any type) */
     313         3564 :         if (!t->is_literal ())
     314              :           {
     315            0 :             Error error (
     316              :               t->get_locus (),
     317              :               "unknown token %qs in attribute body - literal expected",
     318              :               t->get_token_description ());
     319            0 :             add_error (std::move (error));
     320              : 
     321            0 :             skip_after_end_attribute ();
     322            0 :             return Parse::Error::AttrInput::make_malformed ();
     323            0 :           }
     324              : 
     325         3564 :         AST::Literal::LitType lit_type = AST::Literal::STRING;
     326              :         // Crappy mapping of token type to literal type
     327         3564 :         switch (t->get_id ())
     328              :           {
     329              :           case INT_LITERAL:
     330              :             lit_type = AST::Literal::INT;
     331              :             break;
     332              :           case FLOAT_LITERAL:
     333              :             lit_type = AST::Literal::FLOAT;
     334              :             break;
     335              :           case CHAR_LITERAL:
     336              :             lit_type = AST::Literal::CHAR;
     337              :             break;
     338              :           case BYTE_CHAR_LITERAL:
     339              :             lit_type = AST::Literal::BYTE;
     340              :             break;
     341              :           case BYTE_STRING_LITERAL:
     342              :             lit_type = AST::Literal::BYTE_STRING;
     343              :             break;
     344              :           case RAW_STRING_LITERAL:
     345              :             lit_type = AST::Literal::RAW_STRING;
     346              :             break;
     347              :           case STRING_LITERAL:
     348              :           default:
     349              :             lit_type = AST::Literal::STRING;
     350              :             break; // TODO: raw string? don't eliminate it from lexer?
     351              :           }
     352              : 
     353              :         // create actual LiteralExpr
     354        10690 :         AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
     355              :                                    {}, t->get_locus ());
     356         3564 :         lexer.skip_token ();
     357              : 
     358         3564 :         std::unique_ptr<AST::AttrInput> attr_input_lit (
     359         3564 :           new AST::AttrInputLiteral (std::move (lit_expr)));
     360              : 
     361              :         // do checks or whatever? none required, really
     362              : 
     363              :         // FIXME: shouldn't a skip token be required here?
     364              : 
     365              :         return tl::expected<std::unique_ptr<AST::AttrInput>,
     366              :                             Parse::Error::AttrInput>{
     367         3564 :           std::move (attr_input_lit)};
     368         3564 :       }
     369              :       break;
     370         6372 :     case RIGHT_PAREN:
     371              :     case RIGHT_SQUARE:
     372              :     case RIGHT_CURLY:
     373              :     case END_OF_FILE:
     374              :       // means AttrInput is missing, which is allowed
     375              :       return Parse::Error::AttrInput::make_missing_attrinput ();
     376            0 :     default:
     377            0 :       add_error (
     378            0 :         Error (t->get_locus (),
     379              :                "unknown token %qs in attribute body - attribute input or "
     380              :                "none expected",
     381              :                t->get_token_description ()));
     382              : 
     383            0 :       skip_after_end_attribute ();
     384              :       return Parse::Error::AttrInput::make_malformed ();
     385              :     }
     386              :   rust_unreachable ();
     387              :   // TODO: find out how to stop gcc error on "no return value"
     388        20480 : }
     389              : 
     390              : } // 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.