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
|