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 66040 : Parser<ManagedTokenSource>::parse_doc_comment ()
34 : {
35 66040 : const_TokenPtr token = lexer.peek_token ();
36 66040 : location_t locus = token->get_locus ();
37 66040 : AST::SimplePathSegment segment (Values::Attributes::DOC, locus);
38 66040 : std::vector<AST::SimplePathSegment> segments;
39 66040 : segments.push_back (std::move (segment));
40 66040 : AST::SimplePath attr_path (std::move (segments), false, locus);
41 132080 : AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
42 : PrimitiveCoreType::CORETYPE_STR, {}, locus);
43 66040 : std::unique_ptr<AST::AttrInput> attr_input (
44 66040 : new AST::AttrInputLiteral (std::move (lit_expr)));
45 66040 : lexer.skip_token ();
46 :
47 : return Parse::AttributeBody{std::move (attr_path), std::move (attr_input),
48 66040 : locus};
49 66040 : }
50 :
51 : // Parse a single inner attribute.
52 : template <typename ManagedTokenSource>
53 : tl::expected<AST::Attribute, Parse::Error::Attribute>
54 17417 : Parser<ManagedTokenSource>::parse_inner_attribute ()
55 : {
56 34834 : if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
57 : {
58 3968 : auto body = parse_doc_comment ();
59 7936 : return AST::Attribute (std::move (body.path), std::move (body.input),
60 3968 : body.locus, true);
61 3968 : }
62 :
63 13449 : rust_assert (lexer.peek_token ()->get_id () == HASH);
64 :
65 13449 : lexer.skip_token ();
66 :
67 26898 : 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 13449 : lexer.skip_token ();
76 :
77 13449 : if (!skip_token (LEFT_SQUARE))
78 0 : return Parse::Error::Attribute::make_malformed ();
79 :
80 13449 : auto body_res = parse_attribute_body ();
81 13449 : if (!body_res)
82 0 : return Parse::Error::Attribute::make_malformed ();
83 13449 : auto body = std::move (body_res.value ());
84 :
85 13449 : auto actual_attribute
86 26898 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
87 : true);
88 :
89 13449 : if (!skip_token (RIGHT_SQUARE))
90 0 : return Parse::Error::Attribute::make_malformed ();
91 :
92 13449 : return actual_attribute;
93 26898 : }
94 :
95 : // Parse a single outer attribute.
96 : template <typename ManagedTokenSource>
97 : tl::expected<AST::Attribute, Parse::Error::Attribute>
98 105743 : Parser<ManagedTokenSource>::parse_outer_attribute ()
99 : {
100 211486 : if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
101 : {
102 62072 : auto body = parse_doc_comment ();
103 124144 : return AST::Attribute (std::move (body.path), std::move (body.input),
104 62072 : body.locus, false);
105 62072 : }
106 :
107 87342 : 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 87338 : if (lexer.peek_token ()->get_id () != HASH)
122 0 : return Parse::Error::Attribute::make_malformed ();
123 :
124 43669 : lexer.skip_token ();
125 :
126 43669 : TokenId id = lexer.peek_token ()->get_id ();
127 43669 : 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 43669 : lexer.skip_token ();
143 :
144 43669 : auto body_res = parse_attribute_body ();
145 43669 : if (!body_res)
146 7 : return Parse::Error::Attribute::make_malformed_body ();
147 43662 : auto body = std::move (body_res.value ());
148 :
149 43662 : auto actual_attribute
150 87324 : = AST::Attribute (std::move (body.path), std::move (body.input), body.locus,
151 : false);
152 :
153 87324 : if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
154 0 : return Parse::Error::Attribute::make_malformed ();
155 :
156 43662 : lexer.skip_token ();
157 :
158 43662 : return actual_attribute;
159 87331 : }
160 :
161 : // Parses the body of an attribute (inner or outer).
162 : template <typename ManagedTokenSource>
163 : tl::expected<Parse::AttributeBody, Parse::Error::AttributeBody>
164 58917 : Parser<ManagedTokenSource>::parse_attribute_body ()
165 : {
166 58917 : location_t locus = lexer.peek_token ()->get_locus ();
167 :
168 58917 : auto attr_path = parse_simple_path ();
169 : // ensure path is valid to parse attribute input
170 58917 : 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 58917 : auto attr_input = parse_attr_input ();
182 : // AttrInput is allowed to be null, so no checks here
183 58917 : if (attr_input)
184 40459 : return Parse::AttributeBody{std::move (attr_path.value ()),
185 80918 : std::move (attr_input.value ()), locus};
186 18458 : else if (attr_input.error ().kind == Parse::Error::AttrInput::Kind::MISSING)
187 36902 : return Parse::AttributeBody{std::move (attr_path.value ()), nullptr, locus};
188 : else
189 7 : return Parse::Error::AttributeBody::make_invalid_attrinput ();
190 58917 : }
191 :
192 : // Parse a contiguous block of inner attributes.
193 : template <typename ManagedTokenSource>
194 : AST::AttrVec
195 189500 : Parser<ManagedTokenSource>::parse_inner_attributes ()
196 : {
197 189500 : AST::AttrVec inner_attributes;
198 :
199 396417 : auto has_valid_inner_attribute_prefix = [&] () {
200 206917 : auto id = lexer.peek_token ()->get_id ();
201 : /* Outer attribute `#[` is not allowed, only accepts `#!` */
202 231836 : return (id == HASH && lexer.peek_token (1)->get_id () == EXCLAM)
203 212652 : || id == INNER_DOC_COMMENT;
204 : };
205 :
206 206917 : while (has_valid_inner_attribute_prefix ())
207 : {
208 17417 : auto inner_attr = parse_inner_attribute ();
209 :
210 : /* Ensure only valid inner attributes are added to the inner_attributes
211 : * list */
212 17417 : if (inner_attr)
213 : {
214 17417 : 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 189500 : inner_attributes.shrink_to_fit ();
225 189500 : return inner_attributes;
226 : }
227 :
228 : // Parses a contiguous block of outer attributes.
229 : template <typename ManagedTokenSource>
230 : AST::AttrVec
231 318498 : Parser<ManagedTokenSource>::parse_outer_attributes ()
232 : {
233 318498 : AST::AttrVec outer_attributes;
234 :
235 742730 : auto has_valid_attribute_prefix = [&] () {
236 424232 : auto id = lexer.peek_token ()->get_id ();
237 : /* We allow inner attributes `#!` and catch the error later */
238 424232 : return id == HASH || id == OUTER_DOC_COMMENT || id == INNER_DOC_COMMENT;
239 : };
240 :
241 424241 : while (has_valid_attribute_prefix ()) /* For error handling. */
242 : {
243 105743 : auto outer_attr = parse_outer_attribute ();
244 :
245 : /* Ensure only valid outer attributes are added to the outer_attributes
246 : * list */
247 105743 : if (outer_attr)
248 : {
249 105734 : 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 318498 : outer_attributes.shrink_to_fit ();
260 318498 : 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 58917 : Parser<ManagedTokenSource>::parse_attr_input ()
270 : {
271 58917 : const_TokenPtr t = lexer.peek_token ();
272 58917 : switch (t->get_id ())
273 : {
274 33060 : case LEFT_PAREN:
275 : case LEFT_SQUARE:
276 : case LEFT_CURLY:
277 : {
278 33060 : auto dtoken_tree = parse_delim_token_tree ();
279 33060 : 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 33053 : std::unique_ptr<AST::AttrInput> input_tree (
284 33053 : new AST::DelimTokenTree (dtoken_tree.value ()));
285 :
286 : return tl::expected<std::unique_ptr<AST::AttrInput>,
287 33053 : Parse::Error::AttrInput>{std::move (input_tree)};
288 66113 : }
289 7406 : case EQUAL:
290 : {
291 : // = LiteralExpr
292 7406 : lexer.skip_token ();
293 :
294 7406 : 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 7406 : if (Parse::Utils::is_simple_path_segment (t->get_id ()))
300 : {
301 1327 : std::unique_ptr<AST::MacroInvocation> invoke
302 1327 : = parse_macro_invocation ({});
303 :
304 1327 : if (!invoke)
305 0 : return Parse::Error::AttrInput::make_bad_macro_invocation ();
306 :
307 1327 : return std::make_unique<AST::AttrInputExpr> (std::move (invoke));
308 1327 : }
309 :
310 6079 : AST::Literal::LitType lit_type = AST::Literal::STRING;
311 : // Crappy mapping of token type to literal type
312 6079 : switch (t->get_id ())
313 : {
314 : case INT_LITERAL:
315 : lit_type = AST::Literal::INT;
316 : break;
317 : case FLOAT_LITERAL:
318 : lit_type = AST::Literal::FLOAT;
319 : break;
320 : case CHAR_LITERAL:
321 : lit_type = AST::Literal::CHAR;
322 : break;
323 : case BYTE_CHAR_LITERAL:
324 : lit_type = AST::Literal::BYTE;
325 : break;
326 : case BYTE_STRING_LITERAL:
327 : lit_type = AST::Literal::BYTE_STRING;
328 : break;
329 : case C_STRING_LITERAL:
330 : lit_type = AST::Literal::C_STRING;
331 : break;
332 : case RAW_STRING_LITERAL:
333 : lit_type = AST::Literal::RAW_STRING;
334 : break;
335 : case STRING_LITERAL:
336 : lit_type = AST::Literal::STRING;
337 : break; // TODO: raw string? don't eliminate it from lexer?
338 0 : default:
339 0 : rust_sorry_at (t->get_locus (),
340 : "Unsupported attribute input, only literals and "
341 : "macros are supported for now");
342 0 : skip_after_end_attribute ();
343 0 : return Parse::Error::AttrInput::make_malformed ();
344 : }
345 :
346 : // create actual LiteralExpr
347 18235 : AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
348 : {}, t->get_locus ());
349 6079 : lexer.skip_token ();
350 :
351 6079 : std::unique_ptr<AST::AttrInput> attr_input_lit (
352 6079 : new AST::AttrInputLiteral (std::move (lit_expr)));
353 :
354 : // do checks or whatever? none required, really
355 :
356 : // FIXME: shouldn't a skip token be required here?
357 :
358 : return tl::expected<std::unique_ptr<AST::AttrInput>,
359 : Parse::Error::AttrInput>{
360 6079 : std::move (attr_input_lit)};
361 6079 : }
362 : break;
363 18451 : case RIGHT_PAREN:
364 : case RIGHT_SQUARE:
365 : case RIGHT_CURLY:
366 : case END_OF_FILE:
367 : // means AttrInput is missing, which is allowed
368 18451 : return Parse::Error::AttrInput::make_missing_attrinput ();
369 0 : default:
370 0 : add_error (
371 0 : Error (t->get_locus (),
372 : "unknown token %qs in attribute body - attribute input or "
373 : "none expected",
374 : t->get_token_description ()));
375 :
376 0 : skip_after_end_attribute ();
377 0 : return Parse::Error::AttrInput::make_malformed ();
378 : }
379 : rust_unreachable ();
380 : // TODO: find out how to stop gcc error on "no return value"
381 58917 : }
382 :
383 : } // namespace Rust
|