mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
391 lines
12 KiB
C++
391 lines
12 KiB
C++
// 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"
|
|
#include "rust-parse-error.h"
|
|
#include "rust-attribute-values.h"
|
|
#include "expected.h"
|
|
|
|
namespace Rust {
|
|
|
|
// Parse a inner or outer doc comment into an doc attribute
|
|
template <typename ManagedTokenSource>
|
|
Parse::AttributeBody
|
|
Parser<ManagedTokenSource>::parse_doc_comment ()
|
|
{
|
|
const_TokenPtr token = lexer.peek_token ();
|
|
location_t locus = token->get_locus ();
|
|
AST::SimplePathSegment segment (Values::Attributes::DOC, locus);
|
|
std::vector<AST::SimplePathSegment> segments;
|
|
segments.push_back (std::move (segment));
|
|
AST::SimplePath attr_path (std::move (segments), false, locus);
|
|
AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
|
|
PrimitiveCoreType::CORETYPE_STR, {}, locus);
|
|
std::unique_ptr<AST::AttrInput> attr_input (
|
|
new AST::AttrInputLiteral (std::move (lit_expr)));
|
|
lexer.skip_token ();
|
|
|
|
return Parse::AttributeBody{std::move (attr_path), std::move (attr_input),
|
|
locus};
|
|
}
|
|
|
|
// Parse a single inner attribute.
|
|
template <typename ManagedTokenSource>
|
|
tl::expected<AST::Attribute, Parse::Error::Attribute>
|
|
Parser<ManagedTokenSource>::parse_inner_attribute ()
|
|
{
|
|
if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
|
|
{
|
|
auto body = parse_doc_comment ();
|
|
return AST::Attribute (std::move (body.path), std::move (body.input),
|
|
body.locus, true);
|
|
}
|
|
|
|
rust_assert (lexer.peek_token ()->get_id () == HASH);
|
|
|
|
lexer.skip_token ();
|
|
|
|
if (lexer.peek_token ()->get_id () != EXCLAM)
|
|
{
|
|
Error error (lexer.peek_token ()->get_locus (),
|
|
"expected %<!%> or %<[%> for inner attribute");
|
|
add_error (std::move (error));
|
|
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
}
|
|
lexer.skip_token ();
|
|
|
|
if (!skip_token (LEFT_SQUARE))
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
|
|
auto body_res = parse_attribute_body ();
|
|
if (!body_res)
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
auto body = std::move (body_res.value ());
|
|
|
|
auto actual_attribute
|
|
= AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
|
|
true);
|
|
|
|
if (!skip_token (RIGHT_SQUARE))
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
|
|
return actual_attribute;
|
|
}
|
|
|
|
// Parse a single outer attribute.
|
|
template <typename ManagedTokenSource>
|
|
tl::expected<AST::Attribute, Parse::Error::Attribute>
|
|
Parser<ManagedTokenSource>::parse_outer_attribute ()
|
|
{
|
|
if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
|
|
{
|
|
auto body = parse_doc_comment ();
|
|
return AST::Attribute (std::move (body.path), std::move (body.input),
|
|
body.locus, false);
|
|
}
|
|
|
|
if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
|
|
{
|
|
Error error (
|
|
lexer.peek_token ()->get_locus (), ErrorCode::E0753,
|
|
"expected outer doc comment, inner doc (%<//!%> or %</*!%>) only "
|
|
"allowed at start of item "
|
|
"and before any outer attribute or doc (%<#[%>, %<///%> or %</**%>)");
|
|
add_error (std::move (error));
|
|
lexer.skip_token ();
|
|
return Parse::Error::Attribute::make_unexpected_inner ();
|
|
}
|
|
|
|
/* OuterAttribute -> '#' '[' Attr ']' */
|
|
|
|
if (lexer.peek_token ()->get_id () != HASH)
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
|
|
lexer.skip_token ();
|
|
|
|
TokenId id = lexer.peek_token ()->get_id ();
|
|
if (id != LEFT_SQUARE)
|
|
{
|
|
if (id == EXCLAM)
|
|
{
|
|
// this is inner attribute syntax, so throw error
|
|
// inner attributes were either already parsed or not allowed here.
|
|
Error error (
|
|
lexer.peek_token ()->get_locus (),
|
|
"token %<!%> found, indicating inner attribute definition. Inner "
|
|
"attributes are not possible at this location");
|
|
add_error (std::move (error));
|
|
}
|
|
return Parse::Error::Attribute::make_unexpected_inner ();
|
|
}
|
|
|
|
lexer.skip_token ();
|
|
|
|
auto body_res = parse_attribute_body ();
|
|
if (!body_res)
|
|
return Parse::Error::Attribute::make_malformed_body ();
|
|
auto body = std::move (body_res.value ());
|
|
|
|
auto actual_attribute
|
|
= AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
|
|
false);
|
|
|
|
if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
|
|
return Parse::Error::Attribute::make_malformed ();
|
|
|
|
lexer.skip_token ();
|
|
|
|
return actual_attribute;
|
|
}
|
|
|
|
// Parses the body of an attribute (inner or outer).
|
|
template <typename ManagedTokenSource>
|
|
tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
|
|
Parser<ManagedTokenSource>::parse_attribute_body ()
|
|
{
|
|
location_t locus = lexer.peek_token ()->get_locus ();
|
|
|
|
auto attr_path = parse_simple_path ();
|
|
// ensure path is valid to parse attribute input
|
|
if (!attr_path)
|
|
{
|
|
Error error (lexer.peek_token ()->get_locus (),
|
|
"empty simple path in attribute");
|
|
add_error (std::move (error));
|
|
|
|
// Skip past potential further info in attribute (i.e. attr_input)
|
|
skip_after_end_attribute ();
|
|
return Parse::Error::AttributeBody::make_invalid_path ();
|
|
}
|
|
|
|
auto attr_input = parse_attr_input ();
|
|
// AttrInput is allowed to be null, so no checks here
|
|
if (attr_input)
|
|
return Parse::AttributeBody{std::move (attr_path.value ()),
|
|
std::move (attr_input.value ()), locus};
|
|
else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
|
|
return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
|
|
else
|
|
return Parse::Error::AttributeBody::make_invalid_attrinput ();
|
|
}
|
|
|
|
// Parse a contiguous block of inner attributes.
|
|
template <typename ManagedTokenSource>
|
|
AST::AttrVec
|
|
Parser<ManagedTokenSource>::parse_inner_attributes ()
|
|
{
|
|
AST::AttrVec inner_attributes;
|
|
|
|
auto has_valid_inner_attribute_prefix = [&] () {
|
|
auto id = lexer.peek_token ()->get_id ();
|
|
/* Outer attribute `#[` is not allowed, only accepts `#!` */
|
|
return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
|
|
|| id == INNER_DOC_COMMENT;
|
|
};
|
|
|
|
while (has_valid_inner_attribute_prefix ())
|
|
{
|
|
auto inner_attr = parse_inner_attribute ();
|
|
|
|
/* Ensure only valid inner attributes are added to the inner_attributes
|
|
* list */
|
|
if (inner_attr)
|
|
{
|
|
inner_attributes.push_back (std::move (inner_attr.value ()));
|
|
}
|
|
else
|
|
{
|
|
/* If no more valid inner attributes, break out of loop (only
|
|
* contiguous inner attributes parsed). */
|
|
break;
|
|
}
|
|
}
|
|
|
|
inner_attributes.shrink_to_fit ();
|
|
return inner_attributes;
|
|
}
|
|
|
|
// Parses a contiguous block of outer attributes.
|
|
template <typename ManagedTokenSource>
|
|
AST::AttrVec
|
|
Parser<ManagedTokenSource>::parse_outer_attributes ()
|
|
{
|
|
AST::AttrVec outer_attributes;
|
|
|
|
auto has_valid_attribute_prefix = [&] () {
|
|
auto id = lexer.peek_token ()->get_id ();
|
|
/* We allow inner attributes `#!` and catch the error later */
|
|
return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
|
|
};
|
|
|
|
while (has_valid_attribute_prefix ()) /* For error handling. */
|
|
{
|
|
auto outer_attr = parse_outer_attribute ();
|
|
|
|
/* Ensure only valid outer attributes are added to the outer_attributes
|
|
* list */
|
|
if (outer_attr)
|
|
{
|
|
outer_attributes.push_back (std::move (outer_attr.value ()));
|
|
}
|
|
else
|
|
{
|
|
/* If no more valid outer attributes, break out of loop (only
|
|
* contiguous outer attributes parsed). */
|
|
break;
|
|
}
|
|
}
|
|
|
|
outer_attributes.shrink_to_fit ();
|
|
return outer_attributes;
|
|
|
|
/* TODO: this shares basically all code with parse_inner_attributes except
|
|
* function call - find way of making it more modular? function pointer? */
|
|
}
|
|
|
|
// Parses an AttrInput AST node (polymorphic, as AttrInput is abstract)
|
|
template <typename ManagedTokenSource>
|
|
tl::expected<std::unique_ptr<AST::AttrInput>, Parse::Error::AttrInput>
|
|
Parser<ManagedTokenSource>::parse_attr_input ()
|
|
{
|
|
const_TokenPtr t = lexer.peek_token ();
|
|
switch (t->get_id ())
|
|
{
|
|
case LEFT_PAREN:
|
|
case LEFT_SQUARE:
|
|
case LEFT_CURLY:
|
|
{
|
|
auto dtoken_tree = parse_delim_token_tree ();
|
|
if (!dtoken_tree)
|
|
return Parse::Error::AttrInput::make_bad_token_tree ();
|
|
|
|
// must be a delimited token tree, so parse that
|
|
std::unique_ptr<AST::AttrInput> input_tree (
|
|
new AST::DelimTokenTree (dtoken_tree.value ()));
|
|
|
|
return tl::expected<std::unique_ptr<AST::AttrInput>,
|
|
Parse::Error::AttrInput>{std::move (input_tree)};
|
|
}
|
|
case EQUAL:
|
|
{
|
|
// = LiteralExpr
|
|
lexer.skip_token ();
|
|
|
|
t = lexer.peek_token ();
|
|
|
|
// attempt to parse macro
|
|
// TODO: macros may/may not be allowed in attributes
|
|
// this is needed for "#[doc = include_str!(...)]"
|
|
if (Parse::Utils::is_simple_path_segment (t->get_id ()))
|
|
{
|
|
std::unique_ptr<AST::MacroInvocation> invoke
|
|
= parse_macro_invocation ({});
|
|
|
|
if (!invoke)
|
|
return Parse::Error::AttrInput::make_bad_macro_invocation ();
|
|
|
|
return std::unique_ptr<AST::AttrInput> (
|
|
new AST::AttrInputMacro (std::move (invoke)));
|
|
}
|
|
|
|
/* Ensure token is a "literal expression" (literally only a literal
|
|
* token of any type) */
|
|
if (!t->is_literal ())
|
|
{
|
|
Error error (
|
|
t->get_locus (),
|
|
"unknown token %qs in attribute body - literal expected",
|
|
t->get_token_description ());
|
|
add_error (std::move (error));
|
|
|
|
skip_after_end_attribute ();
|
|
return Parse::Error::AttrInput::make_malformed ();
|
|
}
|
|
|
|
AST::Literal::LitType lit_type = AST::Literal::STRING;
|
|
// Crappy mapping of token type to literal type
|
|
switch (t->get_id ())
|
|
{
|
|
case INT_LITERAL:
|
|
lit_type = AST::Literal::INT;
|
|
break;
|
|
case FLOAT_LITERAL:
|
|
lit_type = AST::Literal::FLOAT;
|
|
break;
|
|
case CHAR_LITERAL:
|
|
lit_type = AST::Literal::CHAR;
|
|
break;
|
|
case BYTE_CHAR_LITERAL:
|
|
lit_type = AST::Literal::BYTE;
|
|
break;
|
|
case BYTE_STRING_LITERAL:
|
|
lit_type = AST::Literal::BYTE_STRING;
|
|
break;
|
|
case RAW_STRING_LITERAL:
|
|
lit_type = AST::Literal::RAW_STRING;
|
|
break;
|
|
case STRING_LITERAL:
|
|
default:
|
|
lit_type = AST::Literal::STRING;
|
|
break; // TODO: raw string? don't eliminate it from lexer?
|
|
}
|
|
|
|
// create actual LiteralExpr
|
|
AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
|
|
{}, t->get_locus ());
|
|
lexer.skip_token ();
|
|
|
|
std::unique_ptr<AST::AttrInput> attr_input_lit (
|
|
new AST::AttrInputLiteral (std::move (lit_expr)));
|
|
|
|
// do checks or whatever? none required, really
|
|
|
|
// FIXME: shouldn't a skip token be required here?
|
|
|
|
return tl::expected<std::unique_ptr<AST::AttrInput>,
|
|
Parse::Error::AttrInput>{
|
|
std::move (attr_input_lit)};
|
|
}
|
|
break;
|
|
case RIGHT_PAREN:
|
|
case RIGHT_SQUARE:
|
|
case RIGHT_CURLY:
|
|
case END_OF_FILE:
|
|
// means AttrInput is missing, which is allowed
|
|
return Parse::Error::AttrInput::make_missing_attrinput ();
|
|
default:
|
|
add_error (
|
|
Error (t->get_locus (),
|
|
"unknown token %qs in attribute body - attribute input or "
|
|
"none expected",
|
|
t->get_token_description ()));
|
|
|
|
skip_after_end_attribute ();
|
|
return Parse::Error::AttrInput::make_malformed ();
|
|
}
|
|
rust_unreachable ();
|
|
// TODO: find out how to stop gcc error on "no return value"
|
|
}
|
|
|
|
} // namespace Rust
|