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 53772 : Parser<ManagedTokenSource>::parse_doc_comment ()
34 : {
35 53772 : const_TokenPtr token = lexer.peek_token ();
36 53772 : location_t locus = token->get_locus ();
37 53772 : AST::SimplePathSegment segment (Values::Attributes::DOC, locus);
38 53772 : std::vector<AST::SimplePathSegment> segments;
39 53772 : segments.push_back (std::move (segment));
40 53772 : AST::SimplePath attr_path (std::move (segments), false, locus);
41 107544 : AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
42 : PrimitiveCoreType::CORETYPE_STR, {}, locus);
43 53772 : std::unique_ptr<AST::AttrInput> attr_input (
44 53772 : new AST::AttrInputLiteral (std::move (lit_expr)));
45 53772 : lexer.skip_token ();
46 :
47 : return Parse::AttributeBody{std::move (attr_path), std::move (attr_input),
48 53772 : locus};
49 53772 : }
50 :
51 : // Parse a single inner attribute.
52 : template <typename ManagedTokenSource>
53 : tl::expected<AST::Attribute, Parse::Error::Attribute>
54 15489 : Parser<ManagedTokenSource>::parse_inner_attribute ()
55 : {
56 30978 : if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
57 : {
58 2648 : auto body = parse_doc_comment ();
59 5296 : return AST::Attribute (std::move (body.path), std::move (body.input),
60 2648 : body.locus, true);
61 2648 : }
62 :
63 12841 : rust_assert (lexer.peek_token ()->get_id () == HASH);
64 :
65 12841 : lexer.skip_token ();
66 :
67 25682 : 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 12841 : lexer.skip_token ();
76 :
77 12841 : if (!skip_token (LEFT_SQUARE))
78 0 : return Parse::Error::Attribute::make_malformed ();
79 :
80 12841 : auto body_res = parse_attribute_body ();
81 12841 : if (!body_res)
82 0 : return Parse::Error::Attribute::make_malformed ();
83 12841 : auto body = std::move (body_res.value ());
84 :
85 12841 : auto actual_attribute
86 25682 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
87 : true);
88 :
89 12841 : if (!skip_token (RIGHT_SQUARE))
90 0 : return Parse::Error::Attribute::make_malformed ();
91 :
92 12841 : return actual_attribute;
93 25682 : }
94 :
95 : // Parse a single outer attribute.
96 : template <typename ManagedTokenSource>
97 : tl::expected<AST::Attribute, Parse::Error::Attribute>
98 92152 : Parser<ManagedTokenSource>::parse_outer_attribute ()
99 : {
100 184304 : if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
101 : {
102 51124 : auto body = parse_doc_comment ();
103 102248 : return AST::Attribute (std::move (body.path), std::move (body.input),
104 51124 : body.locus, false);
105 51124 : }
106 :
107 82056 : 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 82052 : if (lexer.peek_token ()->get_id () != HASH)
122 0 : return Parse::Error::Attribute::make_malformed ();
123 :
124 41026 : lexer.skip_token ();
125 :
126 41026 : TokenId id = lexer.peek_token ()->get_id ();
127 41026 : 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 0 : return Parse::Error::Attribute::make_unexpected_inner ();
140 : }
141 :
142 41026 : lexer.skip_token ();
143 :
144 41026 : auto body_res = parse_attribute_body ();
145 41026 : if (!body_res)
146 7 : return Parse::Error::Attribute::make_malformed_body ();
147 41019 : auto body = std::move (body_res.value ());
148 :
149 41019 : auto actual_attribute
150 82038 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
151 : false);
152 :
153 82038 : if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
154 0 : return Parse::Error::Attribute::make_malformed ();
155 :
156 41019 : lexer.skip_token ();
157 :
158 41019 : return actual_attribute;
159 82045 : }
160 :
161 : // Parses the body of an attribute (inner or outer).
162 : template <typename ManagedTokenSource>
163 : tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
164 55654 : Parser<ManagedTokenSource>::parse_attribute_body ()
165 : {
166 55654 : location_t locus = lexer.peek_token ()->get_locus ();
167 :
168 55654 : auto attr_path = parse_simple_path ();
169 : // ensure path is valid to parse attribute input
170 55654 : 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 55654 : auto attr_input = parse_attr_input ();
182 : // AttrInput is allowed to be null, so no checks here
183 55654 : if (attr_input)
184 38213 : return Parse::AttributeBody{std::move (attr_path.value ()),
185 76426 : std::move (attr_input.value ()), locus};
186 17441 : else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
187 34868 : return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
188 : else
189 7 : return Parse::Error::AttributeBody::make_invalid_attrinput ();
190 55654 : }
191 :
192 : // Parse a contiguous block of inner attributes.
193 : template <typename ManagedTokenSource>
194 : AST::AttrVec
195 178668 : Parser<ManagedTokenSource>::parse_inner_attributes ()
196 : {
197 178668 : AST::AttrVec inner_attributes;
198 :
199 372825 : auto has_valid_inner_attribute_prefix = [&] () {
200 194157 : auto id = lexer.peek_token ()->get_id ();
201 : /* Outer attribute `#[` is not allowed, only accepts `#!` */
202 217678 : return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
203 199497 : || id == INNER_DOC_COMMENT;
204 : };
205 :
206 194157 : while (has_valid_inner_attribute_prefix ())
207 : {
208 15489 : auto inner_attr = parse_inner_attribute ();
209 :
210 : /* Ensure only valid inner attributes are added to the inner_attributes
211 : * list */
212 15489 : if (inner_attr)
213 : {
214 15489 : 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 178668 : inner_attributes.shrink_to_fit ();
225 178668 : return inner_attributes;
226 : }
227 :
228 : // Parses a contiguous block of outer attributes.
229 : template <typename ManagedTokenSource>
230 : AST::AttrVec
231 296126 : Parser<ManagedTokenSource>::parse_outer_attributes ()
232 : {
233 296126 : AST::AttrVec outer_attributes;
234 :
235 684395 : auto has_valid_attribute_prefix = [&] () {
236 388269 : auto id = lexer.peek_token ()->get_id ();
237 : /* We allow inner attributes `#!` and catch the error later */
238 388269 : return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
239 : };
240 :
241 388278 : while (has_valid_attribute_prefix ()) /* For error handling. */
242 : {
243 92152 : auto outer_attr = parse_outer_attribute ();
244 :
245 : /* Ensure only valid outer attributes are added to the outer_attributes
246 : * list */
247 92152 : if (outer_attr)
248 : {
249 92143 : 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 296126 : outer_attributes.shrink_to_fit ();
260 296126 : 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 55654 : Parser<ManagedTokenSource>::parse_attr_input ()
270 : {
271 55654 : const_TokenPtr t = lexer.peek_token ();
272 55654 : switch (t->get_id ())
273 : {
274 31176 : case LEFT_PAREN:
275 : case LEFT_SQUARE:
276 : case LEFT_CURLY:
277 : {
278 31176 : auto dtoken_tree = parse_delim_token_tree ();
279 31176 : if (!dtoken_tree)
280 7 : return Parse::Error::AttrInput::make_bad_token_tree ();
281 :
282 : // must be a delimited token tree, so parse that
283 31169 : std::unique_ptr<AST::AttrInput> input_tree (
284 31169 : new AST::DelimTokenTree (dtoken_tree.value ()));
285 :
286 : return tl::expected<std::unique_ptr<AST::AttrInput>,
287 31169 : Parse::Error::AttrInput>{std::move (input_tree)};
288 62345 : }
289 7044 : case EQUAL:
290 : {
291 : // = LiteralExpr
292 7044 : lexer.skip_token ();
293 :
294 7044 : t = lexer.peek_token ();
295 :
296 : /* Ensure token is a "literal expression" (literally only a literal
297 : * token of any type) */
298 8372 : if (!t->is_literal ())
299 : {
300 1328 : Error error (
301 : t->get_locus (),
302 : "arbitrary expressions in key-value attributes are unstable");
303 1328 : collect_potential_gating_error (
304 : Feature::Name::EXTENDED_KEY_VALUE_ATTRIBUTES, std::move (error));
305 1328 : }
306 : // attempt to parse macro
307 : // TODO: macros may/may not be allowed in attributes
308 : // this is needed for "#[doc = include_str!(...)]"
309 7044 : if (Parse::Utils::is_simple_path_segment (t->get_id ()))
310 : {
311 1328 : std::unique_ptr<AST::MacroInvocation> invoke
312 1328 : = parse_macro_invocation ({});
313 :
314 1328 : if (!invoke)
315 0 : return Parse::Error::AttrInput::make_bad_macro_invocation ();
316 :
317 1328 : return std::make_unique<AST::AttrInputExpr> (std::move (invoke));
318 1328 : }
319 :
320 5716 : AST::Literal::LitType lit_type = AST::Literal::STRING;
321 : // Crappy mapping of token type to literal type
322 5716 : switch (t->get_id ())
323 : {
324 : case INT_LITERAL:
325 : lit_type = AST::Literal::INT;
326 : break;
327 : case FLOAT_LITERAL:
328 : lit_type = AST::Literal::FLOAT;
329 : break;
330 : case CHAR_LITERAL:
331 : lit_type = AST::Literal::CHAR;
332 : break;
333 : case BYTE_CHAR_LITERAL:
334 : lit_type = AST::Literal::BYTE;
335 : break;
336 : case BYTE_STRING_LITERAL:
337 : lit_type = AST::Literal::BYTE_STRING;
338 : break;
339 : case C_STRING_LITERAL:
340 : lit_type = AST::Literal::C_STRING;
341 : break;
342 : case RAW_STRING_LITERAL:
343 : lit_type = AST::Literal::RAW_STRING;
344 : break;
345 : case STRING_LITERAL:
346 : lit_type = AST::Literal::STRING;
347 : break; // TODO: raw string? don't eliminate it from lexer?
348 0 : default:
349 0 : rust_sorry_at (t->get_locus (),
350 : "Unsupported attribute input, only literals and "
351 : "macros are supported for now");
352 0 : skip_after_end_attribute ();
353 0 : return Parse::Error::AttrInput::make_malformed ();
354 : }
355 :
356 : // create actual LiteralExpr
357 17146 : AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
358 : {}, t->get_locus ());
359 5716 : lexer.skip_token ();
360 :
361 5716 : std::unique_ptr<AST::AttrInput> attr_input_lit (
362 5716 : new AST::AttrInputLiteral (std::move (lit_expr)));
363 :
364 : // do checks or whatever? none required, really
365 :
366 : // FIXME: shouldn't a skip token be required here?
367 :
368 : return tl::expected<std::unique_ptr<AST::AttrInput>,
369 : Parse::Error::AttrInput>{
370 5716 : std::move (attr_input_lit)};
371 5716 : }
372 : break;
373 17434 : case RIGHT_PAREN:
374 : case RIGHT_SQUARE:
375 : case RIGHT_CURLY:
376 : case END_OF_FILE:
377 : // means AttrInput is missing, which is allowed
378 17434 : return Parse::Error::AttrInput::make_missing_attrinput ();
379 0 : default:
380 0 : add_error (
381 0 : Error (t->get_locus (),
382 : "unknown token %qs in attribute body - attribute input or "
383 : "none expected",
384 : t->get_token_description ()));
385 :
386 0 : skip_after_end_attribute ();
387 0 : return Parse::Error::AttrInput::make_malformed ();
388 : }
389 : rust_unreachable ();
390 : // TODO: find out how to stop gcc error on "no return value"
391 55654 : }
392 :
393 : } // namespace Rust
|