| // Copyright (C) 2025-2026 Free Software Foundation, Inc. | |
| // This file is part of GCC. | |
| // GCC is free software; you can redistribute it and/or modify it under | |
| // the terms of the GNU General Public License as published by the Free | |
| // Software Foundation; either version 3, or (at your option) any later | |
| // version. | |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| // for more details. | |
| // You should have received a copy of the GNU General Public License | |
| // along with GCC; see the file COPYING3. If not see | |
| // <http://www.gnu.org/licenses/>. | |
| /* DO NOT INCLUDE ANYWHERE - this is automatically included | |
| * by rust-parse-impl.h | |
| * This is also the reason why there are no include guards. */ | |
| #include "rust-parse.h" | |
| namespace Rust { | |
| // Parses a SimplePath AST node, if it exists. Does nothing otherwise. | |
| template <typename ManagedTokenSource> | |
| tl::expected<AST::SimplePath, Parse::Error::Node> | |
| Parser<ManagedTokenSource>::parse_simple_path () | |
| { | |
| bool has_opening_scope_resolution = false; | |
| location_t locus = UNKNOWN_LOCATION; | |
| using Parse::Utils::is_simple_path_segment; | |
| // don't parse anything if not a path upfront | |
| if (!is_simple_path_segment (lexer.peek_token ()->get_id ()) | |
| && !is_simple_path_segment (lexer.peek_token (1)->get_id ())) | |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::MALFORMED); | |
| /* Checks for opening scope resolution (i.e. global scope fully-qualified | |
| * path) */ | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| locus = lexer.peek_token ()->get_locus (); | |
| lexer.skip_token (); | |
| } | |
| // Parse single required simple path segment | |
| auto segment = parse_simple_path_segment (); | |
| if (!segment) | |
| return tl::unexpected<Parse::Error::Node> (Parse::Error::Node::CHILD_ERROR); | |
| // get location if not gotten already | |
| if (locus == UNKNOWN_LOCATION) | |
| locus = segment->get_locus (); | |
| std::vector<AST::SimplePathSegment> segments; | |
| segments.push_back (std::move (segment.value ())); | |
| // Parse all other simple path segments | |
| while (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| auto new_segment = parse_simple_path_segment (1); | |
| using Error = Parse::Error::SimplePathSegment::Kind; | |
| // Return path as currently constructed if segment in error state. | |
| if (!new_segment) | |
| { | |
| if (new_segment.error ().kind == Error::INVALID_SIMPLE_PATH_TOKEN) | |
| break; /* Could be end of path */ | |
| else /* Any other error is an hard error */ | |
| return tl::unexpected<Parse::Error::Node> ( | |
| Parse::Error::Node::CHILD_ERROR); | |
| } | |
| segments.push_back (std::move (new_segment.value ())); | |
| } | |
| return AST::SimplePath (std::move (segments), has_opening_scope_resolution, | |
| locus); | |
| /* TODO: now that is_simple_path_segment exists, could probably start | |
| * actually making errors upon parse failure of segments and whatever */ | |
| } | |
| /* Parses a single SimplePathSegment (does not handle the scope resolution | |
| * operators) | |
| * Starts parsing at an offset of base_peek */ | |
| template <typename ManagedTokenSource> | |
| tl::expected<AST::SimplePathSegment, Parse::Error::SimplePathSegment> | |
| Parser<ManagedTokenSource>::parse_simple_path_segment (int base_peek) | |
| { | |
| using namespace Values; | |
| const_TokenPtr t = lexer.peek_token (base_peek); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| lexer.skip_token (base_peek); | |
| return AST::SimplePathSegment (t->get_str (), t->get_locus ()); | |
| case SUPER: | |
| lexer.skip_token (base_peek); | |
| return AST::SimplePathSegment (Keywords::SUPER, t->get_locus ()); | |
| case SELF: | |
| lexer.skip_token (base_peek); | |
| return AST::SimplePathSegment (Keywords::SELF, t->get_locus ()); | |
| case CRATE: | |
| lexer.skip_token (base_peek); | |
| return AST::SimplePathSegment (Keywords::CRATE, t->get_locus ()); | |
| case DOLLAR_SIGN: | |
| if (lexer.peek_token (base_peek + 1)->get_id () == CRATE) | |
| { | |
| lexer.skip_token (base_peek + 1); | |
| return AST::SimplePathSegment ("$crate", t->get_locus ()); | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| // do nothing but inactivates warning from gcc when compiling | |
| /* could put the rust_error_at thing here but fallthrough (from failing | |
| * $crate condition) isn't completely obvious if it is. */ | |
| return Parse::Error::SimplePathSegment::make_invalid_token_or_path_end (); | |
| } | |
| rust_unreachable (); | |
| } | |
| // Parses a PathIdentSegment - an identifier segment of a non-SimplePath path. | |
| template <typename ManagedTokenSource> | |
| tl::expected<AST::PathIdentSegment, Parse::Error::PathIdentSegment> | |
| Parser<ManagedTokenSource>::parse_path_ident_segment () | |
| { | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case IDENTIFIER: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (t->get_str (), t->get_locus ()); | |
| case SUPER: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (Values::Keywords::SUPER, t->get_locus ()); | |
| case SELF: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (Values::Keywords::SELF, t->get_locus ()); | |
| case SELF_ALIAS: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (Values::Keywords::SELF_ALIAS, | |
| t->get_locus ()); | |
| case CRATE: | |
| lexer.skip_token (); | |
| return AST::PathIdentSegment (Values::Keywords::CRATE, t->get_locus ()); | |
| case DOLLAR_SIGN: | |
| if (lexer.peek_token (1)->get_id () == CRATE) | |
| { | |
| lexer.skip_token (1); | |
| return AST::PathIdentSegment ("$crate", t->get_locus ()); | |
| } | |
| gcc_fallthrough (); | |
| default: | |
| /* do nothing but inactivates warning from gcc when compiling | |
| * could put the error_at thing here but fallthrough (from failing $crate | |
| * condition) isn't completely obvious if it is. */ | |
| // test prevent error | |
| return Parse::Error::PathIdentSegment::make_invalid_token (); | |
| } | |
| rust_unreachable (); | |
| } | |
| // Parses a type path. | |
| template <typename ManagedTokenSource> | |
| AST::TypePath | |
| Parser<ManagedTokenSource>::parse_type_path () | |
| { | |
| bool has_opening_scope_resolution = false; | |
| location_t locus = lexer.peek_token ()->get_locus (); | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| lexer.skip_token (); | |
| } | |
| // create segment vector | |
| std::vector<std::unique_ptr<AST::TypePathSegment>> segments; | |
| // parse required initial segment | |
| std::unique_ptr<AST::TypePathSegment> initial_segment | |
| = parse_type_path_segment (); | |
| if (initial_segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| // don't necessarily throw error but yeah | |
| return AST::TypePath::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| std::unique_ptr<AST::TypePathSegment> segment | |
| = parse_type_path_segment (); | |
| if (segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), "could not parse type path segment"); | |
| add_error (std::move (error)); | |
| return AST::TypePath::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::TypePath (std::move (segments), locus, | |
| has_opening_scope_resolution); | |
| } | |
| /* Parses a single type path segment (not including opening scope resolution, | |
| * but includes any internal ones). Includes generic args or type path | |
| * functions too. */ | |
| template <typename ManagedTokenSource> | |
| std::unique_ptr<AST::TypePathSegment> | |
| Parser<ManagedTokenSource>::parse_type_path_segment () | |
| { | |
| location_t locus = lexer.peek_token ()->get_locus (); | |
| // parse ident segment part | |
| auto ident_segment_res = parse_path_ident_segment (); | |
| if (!ident_segment_res) | |
| { | |
| // not necessarily an error | |
| return nullptr; | |
| } | |
| auto ident_segment = ident_segment_res.value (); | |
| /* lookahead to determine if variants exist - only consume scope resolution | |
| * then */ | |
| bool has_separating_scope_resolution = false; | |
| const_TokenPtr next = lexer.peek_token (1); | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION | |
| && (next->get_id () == LEFT_ANGLE || next->get_id () == LEFT_PAREN)) | |
| { | |
| has_separating_scope_resolution = true; | |
| lexer.skip_token (); | |
| } | |
| // branch into variants on next token | |
| const_TokenPtr t = lexer.peek_token (); | |
| switch (t->get_id ()) | |
| { | |
| case LEFT_SHIFT: | |
| case LEFT_ANGLE: | |
| { | |
| // parse generic args | |
| AST::GenericArgs generic_args = parse_path_generic_args (); | |
| return std::unique_ptr<AST::TypePathSegmentGeneric> ( | |
| new AST::TypePathSegmentGeneric (std::move (ident_segment), | |
| has_separating_scope_resolution, | |
| std::move (generic_args), locus)); | |
| } | |
| case LEFT_PAREN: | |
| { | |
| // parse type path function | |
| AST::TypePathFunction type_path_function | |
| = parse_type_path_function (locus); | |
| if (type_path_function.is_error ()) | |
| { | |
| // skip after somewhere? | |
| return nullptr; | |
| } | |
| return std::unique_ptr<AST::TypePathSegmentFunction> ( | |
| new AST::TypePathSegmentFunction (std::move (ident_segment), | |
| has_separating_scope_resolution, | |
| std::move (type_path_function), | |
| locus)); | |
| } | |
| default: | |
| // neither of them | |
| return std::unique_ptr<AST::TypePathSegment> ( | |
| new AST::TypePathSegment (std::move (ident_segment), | |
| has_separating_scope_resolution, locus)); | |
| } | |
| rust_unreachable (); | |
| } | |
| // Parses a function call representation inside a type path. | |
| template <typename ManagedTokenSource> | |
| AST::TypePathFunction | |
| Parser<ManagedTokenSource>::parse_type_path_function (location_t id_location) | |
| { | |
| if (!skip_token (LEFT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| // parse function inputs | |
| std::vector<std::unique_ptr<AST::Type>> inputs; | |
| while (lexer.peek_token ()->get_id () != RIGHT_PAREN) | |
| { | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| /* this is an error as there should've been a ')' there if there | |
| * wasn't a type */ | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "failed to parse type in parameters of type path function"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| inputs.push_back (std::move (type)); | |
| // skip commas, including trailing commas | |
| if (lexer.peek_token ()->get_id () != COMMA) | |
| break; | |
| lexer.skip_token (); | |
| } | |
| if (!skip_token (RIGHT_PAREN)) | |
| { | |
| // skip somewhere? | |
| return AST::TypePathFunction::create_error (); | |
| } | |
| // parse optional return type | |
| std::unique_ptr<AST::Type> return_type = parse_function_return_type (); | |
| inputs.shrink_to_fit (); | |
| return AST::TypePathFunction (std::move (inputs), id_location, | |
| std::move (return_type)); | |
| } | |
| // Parses a path inside an expression that allows generic arguments. | |
| template <typename ManagedTokenSource> | |
| AST::PathInExpression | |
| Parser<ManagedTokenSource>::parse_path_in_expression () | |
| { | |
| location_t locus = UNKNOWN_LOCATION; | |
| bool has_opening_scope_resolution = false; | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION) | |
| { | |
| has_opening_scope_resolution = true; | |
| locus = lexer.peek_token ()->get_locus (); | |
| lexer.skip_token (); | |
| } | |
| // create segment vector | |
| std::vector<AST::PathExprSegment> segments; | |
| if (locus == UNKNOWN_LOCATION) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| } | |
| // parse required initial segment | |
| AST::PathExprSegment initial_segment = parse_path_expr_segment (); | |
| if (initial_segment.is_error ()) | |
| { | |
| if (has_opening_scope_resolution) | |
| { | |
| Error error (locus, "expected identifier"); | |
| add_error (std::move (error)); | |
| } | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), | |
| "could not parse path expression segment"); | |
| add_error (std::move (error)); | |
| return AST::PathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::PathInExpression (std::move (segments), {}, locus, | |
| has_opening_scope_resolution); | |
| } | |
| /* Parses a single path in expression path segment (including generic | |
| * arguments). */ | |
| template <typename ManagedTokenSource> | |
| AST::PathExprSegment | |
| Parser<ManagedTokenSource>::parse_path_expr_segment () | |
| { | |
| location_t locus = lexer.peek_token ()->get_locus (); | |
| // parse ident segment | |
| auto ident_result = parse_path_ident_segment (); | |
| if (!ident_result) | |
| { | |
| // not necessarily an error? | |
| return AST::PathExprSegment::create_error (); | |
| } | |
| auto ident = ident_result.value (); | |
| // parse generic args (and turbofish), if they exist | |
| /* use lookahead to determine if they actually exist (don't want to | |
| * accidently parse over next ident segment) */ | |
| if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION | |
| && (lexer.peek_token (1)->get_id () == LEFT_ANGLE | |
| || lexer.peek_token (1)->get_id () == LEFT_SHIFT)) | |
| { | |
| // skip scope resolution | |
| lexer.skip_token (); | |
| // Let parse_path_generic_args split "<<" tokens | |
| AST::GenericArgs generic_args = parse_path_generic_args (); | |
| return AST::PathExprSegment (std::move (ident), locus, | |
| std::move (generic_args)); | |
| } | |
| // return a generic parameter-less expr segment if not found | |
| return AST::PathExprSegment (std::move (ident), locus); | |
| } | |
| /* Parses a fully qualified path in expression (i.e. a pattern). FIXME does | |
| * not parse outer attrs. */ | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathInExpression | |
| Parser<ManagedTokenSource>::parse_qualified_path_in_expression ( | |
| location_t pratt_parsed_loc) | |
| { | |
| /* Note: the Rust grammar is defined in such a way that it is impossible to | |
| * determine whether a prospective qualified path is a | |
| * QualifiedPathInExpression or QualifiedPathInType in all cases by the | |
| * rules themselves (the only possible difference is a TypePathSegment with | |
| * function, and lookahead to find this is too difficult). However, as this | |
| * is a pattern and QualifiedPathInType is a type, I believe it that their | |
| * construction will not be confused (due to rules regarding patterns vs | |
| * types). | |
| * As such, this function will not attempt to minimise errors created by | |
| * their confusion. */ | |
| // parse the qualified path type (required) | |
| AST::QualifiedPathType qual_path_type | |
| = parse_qualified_path_type (pratt_parsed_loc); | |
| if (qual_path_type.is_error ()) | |
| { | |
| // TODO: should this create a parse error? | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| location_t locus = qual_path_type.get_locus (); | |
| // parse path segments | |
| std::vector<AST::PathExprSegment> segments; | |
| // parse initial required segment | |
| if (!expect_token (SCOPE_RESOLUTION)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| AST::PathExprSegment initial_segment = parse_path_expr_segment (); | |
| if (initial_segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "required initial path expression segment in " | |
| "qualified path in expression could not be parsed"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (initial_segment)); | |
| // parse optional segments (as long as scope resolution operator exists) | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| AST::PathExprSegment segment = parse_path_expr_segment (); | |
| if (segment.is_error ()) | |
| { | |
| // skip after somewhere? | |
| Error error (t->get_locus (), | |
| "could not parse path expression segment in qualified " | |
| "path in expression"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInExpression::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| // FIXME: outer attr parsing | |
| return AST::QualifiedPathInExpression (std::move (qual_path_type), | |
| std::move (segments), {}, locus); | |
| } | |
| // Parses the type syntactical construction at the start of a qualified path. | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathType | |
| Parser<ManagedTokenSource>::parse_qualified_path_type ( | |
| location_t pratt_parsed_loc) | |
| { | |
| location_t locus = pratt_parsed_loc; | |
| /* TODO: should this actually be error? is there anywhere where this could | |
| * be valid? */ | |
| if (locus == UNKNOWN_LOCATION) | |
| { | |
| locus = lexer.peek_token ()->get_locus (); | |
| if (lexer.peek_token ()->get_id () == LEFT_SHIFT) | |
| lexer.split_current_token (LEFT_ANGLE, LEFT_ANGLE); | |
| // skip after somewhere? | |
| if (!skip_token (LEFT_ANGLE)) | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| // parse type (required) | |
| std::unique_ptr<AST::Type> type = parse_type (); | |
| if (type == nullptr) | |
| { | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "could not parse type in qualified path type"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| // parse optional as clause | |
| AST::TypePath as_type_path = AST::TypePath::create_error (); | |
| if (lexer.peek_token ()->get_id () == AS) | |
| { | |
| lexer.skip_token (); | |
| // parse type path, which is required now | |
| as_type_path = parse_type_path (); | |
| if (as_type_path.is_error ()) | |
| { | |
| Error error ( | |
| lexer.peek_token ()->get_locus (), | |
| "could not parse type path in as clause in qualified path type"); | |
| add_error (std::move (error)); | |
| // skip somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| } | |
| /* NOTE: should actually be a right-angle token, so | |
| * skip_generics_right_angle shouldn't be required */ | |
| if (!skip_token (RIGHT_ANGLE)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathType::create_error (); | |
| } | |
| return AST::QualifiedPathType (std::move (type), locus, | |
| std::move (as_type_path)); | |
| } | |
| // Parses a fully qualified path in type (i.e. a type). | |
| template <typename ManagedTokenSource> | |
| AST::QualifiedPathInType | |
| Parser<ManagedTokenSource>::parse_qualified_path_in_type () | |
| { | |
| location_t locus = lexer.peek_token ()->get_locus (); | |
| // parse the qualified path type (required) | |
| AST::QualifiedPathType qual_path_type = parse_qualified_path_type (); | |
| if (qual_path_type.is_error ()) | |
| { | |
| // TODO: should this create a parse error? | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| // parse initial required segment | |
| if (!expect_token (SCOPE_RESOLUTION)) | |
| { | |
| // skip after somewhere? | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| std::unique_ptr<AST::TypePathSegment> initial_segment | |
| = parse_type_path_segment (); | |
| if (initial_segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error (lexer.peek_token ()->get_locus (), | |
| "required initial type path segment in qualified path in " | |
| "type could not be parsed"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| // parse optional segments (as long as scope resolution operator exists) | |
| std::vector<std::unique_ptr<AST::TypePathSegment>> segments; | |
| const_TokenPtr t = lexer.peek_token (); | |
| while (t->get_id () == SCOPE_RESOLUTION) | |
| { | |
| // skip scope resolution operator | |
| lexer.skip_token (); | |
| // parse the actual segment - it is an error if it doesn't exist now | |
| std::unique_ptr<AST::TypePathSegment> segment | |
| = parse_type_path_segment (); | |
| if (segment == nullptr) | |
| { | |
| // skip after somewhere? | |
| Error error ( | |
| t->get_locus (), | |
| "could not parse type path segment in qualified path in type"); | |
| add_error (std::move (error)); | |
| return AST::QualifiedPathInType::create_error (); | |
| } | |
| segments.push_back (std::move (segment)); | |
| t = lexer.peek_token (); | |
| } | |
| segments.shrink_to_fit (); | |
| return AST::QualifiedPathInType (std::move (qual_path_type), | |
| std::move (initial_segment), | |
| std::move (segments), locus); | |
| } | |
| } // namespace Rust |