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 12687 : Parser<ManagedTokenSource>::parse_inner_attribute ()
55 : {
56 25374 : 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 12516 : rust_assert (lexer.peek_token ()->get_id () == HASH);
64 :
65 12516 : lexer.skip_token ();
66 :
67 25032 : 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 12516 : lexer.skip_token ();
76 :
77 12516 : if (!skip_token (LEFT_SQUARE))
78 : return Parse::Error::Attribute::make_malformed ();
79 :
80 12516 : auto body_res = parse_attribute_body ();
81 12516 : if (!body_res)
82 : return Parse::Error::Attribute::make_malformed ();
83 12516 : auto body = std::move (body_res.value ());
84 :
85 12516 : auto actual_attribute
86 25032 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
87 : true);
88 :
89 12516 : if (!skip_token (RIGHT_SQUARE))
90 : return Parse::Error::Attribute::make_malformed ();
91 :
92 12516 : return actual_attribute;
93 25032 : }
94 :
95 : // Parse a single outer attribute.
96 : template <typename ManagedTokenSource>
97 : tl::expected<AST::Attribute, Parse::Error::Attribute>
98 17080 : Parser<ManagedTokenSource>::parse_outer_attribute ()
99 : {
100 34160 : 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 18150 : 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 18146 : if (lexer.peek_token ()->get_id () != HASH)
122 : return Parse::Error::Attribute::make_malformed ();
123 :
124 9073 : lexer.skip_token ();
125 :
126 9073 : TokenId id = lexer.peek_token ()->get_id ();
127 9073 : 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 9073 : lexer.skip_token ();
143 :
144 9073 : auto body_res = parse_attribute_body ();
145 9073 : if (!body_res)
146 : return Parse::Error::Attribute::make_malformed_body ();
147 9066 : auto body = std::move (body_res.value ());
148 :
149 9066 : auto actual_attribute
150 18132 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
151 : false);
152 :
153 18132 : if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
154 : return Parse::Error::Attribute::make_malformed ();
155 :
156 9066 : lexer.skip_token ();
157 :
158 9066 : return actual_attribute;
159 18139 : }
160 :
161 : // Parses the body of an attribute (inner or outer).
162 : template <typename ManagedTokenSource>
163 : tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
164 21636 : Parser<ManagedTokenSource>::parse_attribute_body ()
165 : {
166 21636 : location_t locus = lexer.peek_token ()->get_locus ();
167 :
168 21636 : auto attr_path = parse_simple_path ();
169 : // ensure path is valid to parse attribute input
170 21636 : 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 21636 : auto attr_input = parse_attr_input ();
182 : // AttrInput is allowed to be null, so no checks here
183 21636 : if (attr_input)
184 14968 : return Parse::AttributeBody{std::move (attr_path.value ()),
185 29936 : std::move (attr_input.value ()), locus};
186 6668 : else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
187 13322 : return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
188 : else
189 : return Parse::Error::AttributeBody::make_invalid_attrinput ();
190 21636 : }
191 :
192 : // Parse a contiguous block of inner attributes.
193 : template <typename ManagedTokenSource>
194 : AST::AttrVec
195 55848 : Parser<ManagedTokenSource>::parse_inner_attributes ()
196 : {
197 55848 : AST::AttrVec inner_attributes;
198 :
199 124383 : auto has_valid_inner_attribute_prefix = [&] () {
200 68535 : auto id = lexer.peek_token ()->get_id ();
201 : /* Outer attribute `#[` is not allowed, only accepts `#!` */
202 86393 : return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
203 71206 : || id == INNER_DOC_COMMENT;
204 : };
205 :
206 68535 : while (has_valid_inner_attribute_prefix ())
207 : {
208 12687 : auto inner_attr = parse_inner_attribute ();
209 :
210 : /* Ensure only valid inner attributes are added to the inner_attributes
211 : * list */
212 12687 : if (inner_attr)
213 : {
214 12687 : 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 55848 : inner_attributes.shrink_to_fit ();
225 55848 : return inner_attributes;
226 : }
227 :
228 : // Parses a contiguous block of outer attributes.
229 : template <typename ManagedTokenSource>
230 : AST::AttrVec
231 109704 : Parser<ManagedTokenSource>::parse_outer_attributes ()
232 : {
233 109704 : AST::AttrVec outer_attributes;
234 :
235 236479 : auto has_valid_attribute_prefix = [&] () {
236 126775 : auto id = lexer.peek_token ()->get_id ();
237 : /* We allow inner attributes `#!` and catch the error later */
238 126775 : return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
239 : };
240 :
241 126784 : while (has_valid_attribute_prefix ()) /* For error handling. */
242 : {
243 17080 : auto outer_attr = parse_outer_attribute ();
244 :
245 : /* Ensure only valid outer attributes are added to the outer_attributes
246 : * list */
247 17080 : if (outer_attr)
248 : {
249 17071 : 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 109704 : outer_attributes.shrink_to_fit ();
260 109704 : 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 21636 : Parser<ManagedTokenSource>::parse_attr_input ()
270 : {
271 21636 : const_TokenPtr t = lexer.peek_token ();
272 21636 : switch (t->get_id ())
273 : {
274 11168 : case LEFT_PAREN:
275 : case LEFT_SQUARE:
276 : case LEFT_CURLY:
277 : {
278 11168 : auto dtoken_tree = parse_delim_token_tree ();
279 11168 : if (!dtoken_tree)
280 : return Parse::Error::AttrInput::make_bad_token_tree ();
281 :
282 : // must be a delimited token tree, so parse that
283 11161 : std::unique_ptr<AST::AttrInput> input_tree (
284 11161 : new AST::DelimTokenTree (dtoken_tree.value ()));
285 :
286 : return tl::expected<std::unique_ptr<AST::AttrInput>,
287 11161 : Parse::Error::AttrInput>{std::move (input_tree)};
288 22329 : }
289 3807 : case EQUAL:
290 : {
291 : // = LiteralExpr
292 3807 : lexer.skip_token ();
293 :
294 3807 : t = lexer.peek_token ();
295 :
296 : /* Ensure token is a "literal expression" (literally only a literal
297 : * token of any type) */
298 3811 : if (!t->is_literal ())
299 : {
300 4 : Error error (
301 : t->get_locus (),
302 : "arbitrary expressions in key-value attributes are unstable");
303 4 : collect_potential_gating_error (
304 : Feature::Name::EXTENDED_KEY_VALUE_ATTRIBUTES, std::move (error));
305 4 : }
306 : // attempt to parse macro
307 : // TODO: macros may/may not be allowed in attributes
308 : // this is needed for "#[doc = include_str!(...)]"
309 3807 : if (Parse::Utils::is_simple_path_segment (t->get_id ()))
310 : {
311 4 : std::unique_ptr<AST::MacroInvocation> invoke
312 4 : = parse_macro_invocation ({});
313 :
314 4 : if (!invoke)
315 : return Parse::Error::AttrInput::make_bad_macro_invocation ();
316 :
317 4 : return std::make_unique<AST::AttrInputExpr> (std::move (invoke));
318 4 : }
319 :
320 3803 : AST::Literal::LitType lit_type = AST::Literal::STRING;
321 : // Crappy mapping of token type to literal type
322 3803 : 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 : return Parse::Error::AttrInput::make_malformed ();
354 : }
355 :
356 : // create actual LiteralExpr
357 11407 : AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
358 : {}, t->get_locus ());
359 3803 : lexer.skip_token ();
360 :
361 3803 : std::unique_ptr<AST::AttrInput> attr_input_lit (
362 3803 : 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 3803 : std::move (attr_input_lit)};
371 3803 : }
372 : break;
373 6661 : 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 : 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 : return Parse::Error::AttrInput::make_malformed ();
388 : }
389 : rust_unreachable ();
390 : // TODO: find out how to stop gcc error on "no return value"
391 21636 : }
392 :
393 : } // namespace Rust
|