Line data Source code
1 : // Copyright (C) 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 : #include "rust-builtin-attribute-checker.h"
20 : #include "optional.h"
21 : #include "rust-attributes.h"
22 : #include "rust-attribute-values.h"
23 : #include "rust-session-manager.h"
24 :
25 : namespace Rust {
26 : namespace Analysis {
27 :
28 : using Attrs = Values::Attributes;
29 :
30 : void
31 12947 : check_inner_attribute (const AST::Attribute &attribute)
32 : {
33 12947 : auto result_opt = lookup_builtin (attribute);
34 12947 : if (!result_opt.has_value ())
35 0 : return;
36 12947 : auto result = result_opt.value ();
37 :
38 12947 : if (Attributes::valid_outer_attribute (result.name))
39 3 : rust_error_at (attribute.get_locus (),
40 : "attribute cannot be used at crate level");
41 :
42 12947 : if (result.name == Values::Attributes::NEEDS_ALLOCATOR)
43 : {
44 2 : rust_warning_at (attribute.get_locus (), 0,
45 : "%<#[%s]%> is not implemented yet and has no effect",
46 4 : attribute.as_string ().c_str ());
47 : }
48 25894 : }
49 :
50 : /**
51 : * Check that the string given to #[doc(alias = ...)] or #[doc(alias(...))] is
52 : * valid.
53 : *
54 : * This means no whitespace characters other than spaces and no quoting
55 : * characters.
56 : */
57 : static void
58 761 : check_doc_alias (const std::string &alias_input, const location_t locus)
59 : {
60 : // FIXME: The locus here is for the whole attribute. Can we get the locus
61 : // of the alias input instead?
62 2052 : for (auto c : alias_input)
63 1291 : if ((ISSPACE (c) && c != ' ') || c == '\'' || c == '\"')
64 : {
65 7 : auto to_print = std::string (1, c);
66 7 : switch (c)
67 : {
68 7 : case '\n':
69 7 : to_print = "\\n";
70 7 : break;
71 0 : case '\t':
72 0 : to_print = "\\t";
73 0 : break;
74 : default:
75 : break;
76 : }
77 7 : rust_error_at (locus,
78 : "invalid character used in %<#[doc(alias)]%> input: %qs",
79 : to_print.c_str ());
80 7 : }
81 :
82 761 : if (alias_input.empty ())
83 : return;
84 :
85 1522 : if (alias_input.front () == ' ' || alias_input.back () == ' ')
86 0 : rust_error_at (locus,
87 : "%<#[doc(alias)]%> input cannot start or end with a space");
88 : }
89 :
90 : // This namespace contains handlers for the builtin attribute checker,
91 : // those handlers must verify the attribute internal structure and emit the
92 : // appropriate error message if the structure is incorrect.
93 : //
94 : // They DO NOT check the attribute validity on the parent item.
95 : namespace handlers {
96 :
97 : void
98 67828 : doc (const AST::Attribute &attribute)
99 : {
100 67828 : if (!attribute.has_attr_input ())
101 : {
102 2 : rust_error_at (
103 : attribute.get_locus (),
104 : "valid forms for the attribute are "
105 : "%<#[doc(hidden|inline|...)]%> and %<#[doc = \" string \"]%>");
106 2 : return;
107 : }
108 :
109 67826 : switch (attribute.get_attr_input ().get_attr_input_type ())
110 : {
111 : case AST::AttrInput::LITERAL:
112 : case AST::AttrInput::META_ITEM:
113 : case AST::AttrInput::EXPR:
114 : break;
115 : // FIXME: Handle them as well
116 :
117 1005 : case AST::AttrInput::TOKEN_TREE:
118 1005 : {
119 : // FIXME: This doesn't check for #[doc(alias(...))]
120 1005 : const auto &option = static_cast<const AST::DelimTokenTree &> (
121 1005 : attribute.get_attr_input ());
122 1005 : auto *meta_item = option.parse_to_meta_item ();
123 :
124 2024 : for (auto &item : meta_item->get_items ())
125 : {
126 1019 : if (item->is_key_value_pair ())
127 : {
128 776 : auto name_value
129 776 : = static_cast<AST::MetaNameValueStr *> (item.get ())
130 776 : ->get_name_value_pair ();
131 :
132 : // FIXME: Check for other stuff than #[doc(alias = ...)]
133 776 : if (name_value.first.as_string () == "alias")
134 761 : check_doc_alias (name_value.second, attribute.get_locus ());
135 776 : }
136 : }
137 : break;
138 : }
139 : }
140 : }
141 :
142 : void
143 6 : deprecated (const AST::Attribute &attribute)
144 : {
145 6 : if (!attribute.has_attr_input ())
146 : return;
147 :
148 4 : const auto &input = attribute.get_attr_input ();
149 :
150 4 : if (input.get_attr_input_type () != AST::AttrInput::META_ITEM)
151 : return;
152 :
153 0 : auto &meta = static_cast<const AST::AttrInputMetaItemContainer &> (input);
154 :
155 0 : for (auto ¤t : meta.get_items ())
156 : {
157 0 : switch (current->get_kind ())
158 : {
159 0 : case AST::MetaItemInner::Kind::MetaItem:
160 0 : {
161 0 : auto *meta_item = static_cast<AST::MetaItem *> (current.get ());
162 :
163 0 : switch (meta_item->get_item_kind ())
164 : {
165 0 : case AST::MetaItem::ItemKind::NameValueStr:
166 0 : {
167 0 : auto *nv = static_cast<AST::MetaNameValueStr *> (meta_item);
168 :
169 0 : const std::string key = nv->get_name ().as_string ();
170 :
171 0 : if (key != "since" && key != "note")
172 : {
173 0 : rust_error_at (nv->get_locus (), "unknown meta item %qs",
174 : key.c_str ());
175 0 : rust_inform (nv->get_locus (),
176 : "expected one of %<since%>, %<note%>");
177 : }
178 0 : }
179 0 : break;
180 :
181 0 : case AST::MetaItem::ItemKind::Path:
182 0 : {
183 : // #[deprecated(a,a)]
184 0 : auto *p = static_cast<AST::MetaItemPath *> (meta_item);
185 :
186 0 : std::string ident = p->get_path ().as_string ();
187 :
188 0 : rust_error_at (p->get_locus (), "unknown meta item %qs",
189 : ident.c_str ());
190 0 : rust_inform (p->get_locus (),
191 : "expected one of %<since%>, %<note%>");
192 0 : }
193 0 : break;
194 :
195 0 : case AST::MetaItem::ItemKind::Word:
196 0 : {
197 : // #[deprecated("a")]
198 0 : auto *w = static_cast<AST::MetaWord *> (meta_item);
199 :
200 0 : rust_error_at (
201 0 : w->get_locus (),
202 : "item in %<deprecated%> must be a key/value pair");
203 : }
204 0 : break;
205 :
206 0 : case AST::MetaItem::ItemKind::PathExpr:
207 0 : {
208 : // #[deprecated(since=a)]
209 0 : auto *px = static_cast<AST::MetaItemPathExpr *> (meta_item);
210 :
211 0 : rust_error_at (
212 0 : px->get_locus (),
213 : "expected unsuffixed literal or identifier, found %qs",
214 0 : px->get_expr ().as_string ().c_str ());
215 : }
216 0 : break;
217 :
218 0 : case AST::MetaItem::ItemKind::Seq:
219 0 : case AST::MetaItem::ItemKind::ListPaths:
220 0 : case AST::MetaItem::ItemKind::ListNameValueStr:
221 0 : default:
222 0 : gcc_unreachable ();
223 0 : break;
224 : }
225 : }
226 0 : break;
227 :
228 0 : case AST::MetaItemInner::Kind::LitExpr:
229 0 : default:
230 0 : gcc_unreachable ();
231 0 : break;
232 : }
233 : }
234 : }
235 :
236 : void
237 2 : link_section (const AST::Attribute &attribute)
238 : {
239 2 : if (!attribute.has_attr_input ())
240 : {
241 1 : rust_error_at (attribute.get_locus (),
242 : "malformed %<link_section%> attribute input");
243 1 : rust_inform (attribute.get_locus (),
244 : "must be of the form: %<#[link_section = \"name\"]%>");
245 : }
246 2 : }
247 :
248 : void
249 9 : export_name (const AST::Attribute &attribute)
250 : {
251 9 : if (!attribute.has_attr_input ())
252 : {
253 2 : rust_error_at (attribute.get_locus (),
254 : "malformed %<export_name%> attribute input");
255 2 : rust_inform (attribute.get_locus (),
256 : "must be of the form: %<#[export_name = \"name\"]%>");
257 2 : return;
258 : }
259 :
260 7 : auto &attr_input = attribute.get_attr_input ();
261 7 : if (attr_input.get_attr_input_type ()
262 : == AST::AttrInput::AttrInputType::LITERAL)
263 : {
264 5 : auto &literal_expr
265 5 : = static_cast<AST::AttrInputLiteral &> (attr_input).get_literal ();
266 5 : auto lit_type = literal_expr.get_lit_type ();
267 5 : switch (lit_type)
268 : {
269 : case AST::Literal::LitType::STRING:
270 : case AST::Literal::LitType::RAW_STRING:
271 : case AST::Literal::LitType::BYTE_STRING:
272 : return;
273 : default:
274 : break;
275 : }
276 : }
277 :
278 4 : rust_error_at (attribute.get_locus (), "attribute must be a string literal");
279 : }
280 :
281 : void
282 811 : lint (const AST::Attribute &attribute)
283 : {
284 811 : if (!attribute.has_attr_input ())
285 : {
286 1 : auto name = attribute.get_path ().as_string ();
287 1 : rust_error_at (attribute.get_locus (), "malformed %qs attribute input",
288 : name.c_str ());
289 1 : rust_inform (attribute.get_locus (),
290 : "must be of the form: %<#[%s(lint1, lint2, ...)]%>",
291 : name.c_str ());
292 1 : }
293 811 : }
294 :
295 : void
296 557 : link_name (const AST::Attribute &attribute)
297 : {
298 557 : if (!attribute.has_attr_input ())
299 : {
300 1 : rust_error_at (attribute.get_locus (),
301 : "malformed %<link_name%> attribute input");
302 1 : rust_inform (attribute.get_locus (),
303 : "must be of the form: %<#[link_name = \"name\"]%>");
304 : }
305 557 : }
306 :
307 : namespace {
308 : void
309 58 : check_crate_type (const AST::Attribute &attribute)
310 : {
311 58 : if (!Session::get_instance ().options.is_proc_macro ())
312 : {
313 3 : auto name = attribute.get_path ().as_string ();
314 :
315 3 : rust_error_at (attribute.get_locus (),
316 : "the %<#[%s]%> attribute is only usable with crates of "
317 : "the %<proc-macro%> crate type",
318 : name.c_str ());
319 3 : }
320 58 : }
321 : } // namespace
322 :
323 : static void
324 20 : proc_macro_derive (const AST::Attribute &attribute)
325 : {
326 20 : if (!attribute.has_attr_input ())
327 : {
328 1 : auto name = attribute.get_path ().as_string ();
329 1 : rust_error_at (attribute.get_locus (), "malformed %qs attribute input",
330 : name.c_str ());
331 1 : rust_inform (attribute.get_locus (),
332 : "must be of the form: %<#[proc_macro_derive(TraitName, "
333 : "/*opt*/ attributes(name1, name2, ...))]%>");
334 1 : }
335 20 : check_crate_type (attribute);
336 20 : }
337 :
338 : static void
339 38 : proc_macro (const AST::Attribute &attribute)
340 : {
341 38 : check_crate_type (attribute);
342 38 : }
343 :
344 : static void
345 1710 : target_feature (const AST::Attribute &attribute)
346 : {
347 1710 : if (!attribute.has_attr_input ())
348 : {
349 1 : rust_error_at (attribute.get_locus (),
350 : "malformed %<target_feature%> attribute input");
351 1 : rust_inform (attribute.get_locus (),
352 : "must be of the form: %<#[target_feature(enable = "
353 : "\"name\")]%>");
354 : }
355 1710 : }
356 :
357 : static void
358 12 : used (const AST::Attribute &attribute)
359 : {
360 12 : auto is_invalid = false;
361 12 : if (attribute.has_attr_input ())
362 : {
363 7 : switch (attribute.get_attr_input ().get_attr_input_type ())
364 : {
365 7 : case AST::AttrInput::TOKEN_TREE:
366 7 : {
367 7 : auto input = static_cast<const AST::DelimTokenTree &> (
368 7 : attribute.get_attr_input ())
369 7 : .parse_to_meta_item ();
370 :
371 14 : for (const auto &item : input->get_items ())
372 : {
373 7 : if (item->as_string () != "compiler"
374 11 : && item->as_string () != "linker")
375 1 : is_invalid = true;
376 : }
377 : }
378 :
379 7 : break;
380 : case AST::AttrInput::EXPR:
381 : case AST::AttrInput::LITERAL:
382 : case AST::AttrInput::META_ITEM:
383 : is_invalid = true;
384 : break;
385 : }
386 : }
387 :
388 7 : if (is_invalid)
389 : {
390 1 : rust_error_at (attribute.get_locus (),
391 : "malformed %<used%> attribute input");
392 1 : rust_inform (attribute.get_locus (),
393 : "must be of the form: %<#[used(compiler)]%> or "
394 : "%<#[used(linker)]%>");
395 : }
396 12 : }
397 :
398 : void
399 28 : expect_no_input (const AST::Attribute &attribute)
400 : {
401 28 : if (attribute.has_attr_input ())
402 : {
403 5 : std::string attr_name = attribute.get_path ().as_string ();
404 5 : rust_error_at (attribute.get_locus (), "malformed %<%s%> attribute input",
405 : attr_name.c_str ());
406 5 : rust_inform (attribute.get_locus (), "must be of the form: %<#[%s]%>",
407 : attr_name.c_str ());
408 5 : }
409 28 : }
410 :
411 : } // namespace handlers
412 :
413 : const std::unordered_map<std::string, std::function<void (AST::Attribute &)>>
414 : attribute_checking_handlers = {
415 : {Attrs::DOC, handlers::doc},
416 : {Attrs::DEPRECATED, handlers::deprecated},
417 : {Attrs::LINK_SECTION, handlers::link_section},
418 : {Attrs::EXPORT_NAME, handlers::export_name},
419 : {Attrs::NO_MANGLE, handlers::expect_no_input},
420 : {Attrs::ALLOW, handlers::lint},
421 : {Attrs::DENY, handlers::lint},
422 : {Attrs::WARN, handlers::lint},
423 : {Attrs::FORBID, handlers::lint},
424 : {Attrs::LINK_NAME, handlers::link_name},
425 : {Attrs::PROC_MACRO_DERIVE, handlers::proc_macro_derive},
426 : {Attrs::PROC_MACRO, handlers::proc_macro},
427 : {Attrs::PROC_MACRO_ATTRIBUTE, handlers::proc_macro},
428 : {Attrs::TARGET_FEATURE, handlers::target_feature},
429 : {Attrs::USED, handlers::used},
430 : {Attrs::RUSTC_STD_INTERNAL_SYMBOL, handlers::expect_no_input},
431 : {Attrs::RUSTC_ALLOCATOR, handlers::expect_no_input},
432 : {Attrs::RUSTC_ALLOCATOR_NOUNWIND, handlers::expect_no_input},
433 : {Attrs::GLOBAL_ALLOCATOR, handlers::expect_no_input},
434 : {Attrs::RUSTC_CONVERSION_SUGGESTION, handlers::expect_no_input},
435 : };
436 :
437 : tl::optional<std::function<void (AST::Attribute &)>>
438 116097 : lookup_handler (std::string attr_name)
439 : {
440 116097 : auto res = attribute_checking_handlers.find (attr_name);
441 116097 : if (res != attribute_checking_handlers.cend ())
442 71021 : return res->second;
443 45076 : return tl::nullopt;
444 : }
445 :
446 : static void
447 3 : check_no_mangle_function (const AST::Attribute &attribute,
448 : const AST::Function &fun)
449 : {
450 3 : if (!is_ascii_only (fun.get_function_name ().as_string ()))
451 0 : rust_error_at (fun.get_function_name ().get_locus (),
452 : "the %<#[no_mangle]%> attribute requires ASCII identifier");
453 3 : }
454 :
455 : /**
456 : * Emit an error when an attribute is attached
457 : * to an incompatable item type. e.g.:
458 : *
459 : * #[cold]
460 : * struct A(u8, u8);
461 : *
462 : * Note that "#[derive]" is handled
463 : * explicitly in rust-derive.cc
464 : */
465 : void
466 95817 : check_valid_attribute_for_item (const AST::Attribute &attr,
467 : const AST::Item &item)
468 : {
469 191634 : if (item.get_item_kind () != AST::Item::Kind::Function
470 215324 : && (attr.get_path () == Values::Attributes::TARGET_FEATURE
471 143195 : || attr.get_path () == Values::Attributes::COLD
472 119506 : || attr.get_path () == Values::Attributes::INLINE))
473 : {
474 1 : rust_error_at (attr.get_locus (),
475 : "the %<#[%s]%> attribute may only be applied to functions",
476 2 : attr.get_path ().as_string ().c_str ());
477 : }
478 191632 : else if (attr.get_path () == Values::Attributes::REPR
479 189 : && item.get_item_kind () != AST::Item::Kind::Enum
480 183 : && item.get_item_kind () != AST::Item::Kind::Union
481 95983 : && item.get_item_kind () != AST::Item::Kind::Struct)
482 : {
483 1 : rust_error_at (attr.get_locus (),
484 : "the %<#[%s]%> attribute may only be applied "
485 : "to structs, enums and unions",
486 2 : attr.get_path ().as_string ().c_str ());
487 : }
488 191630 : else if (item.get_item_kind () != AST::Item::Kind::StaticItem
489 287396 : && (attr.get_path () == Values::Attributes::GLOBAL_ALLOCATOR
490 191605 : || attr.get_path () == Values::Attributes::USED))
491 :
492 : {
493 2 : rust_error_at (attr.get_locus (),
494 : "the %<#[%s]%> attribute may only be applied "
495 : "to static items",
496 4 : attr.get_path ().as_string ().c_str ());
497 : }
498 95813 : else if (attr.get_path () == Values::Attributes::RUSTC_CONVERSION_SUGGESTION)
499 : {
500 6 : auto attr_path = attr.get_path ().as_string ();
501 6 : rust_warning_at (attr.get_locus (), 0,
502 : "%<#[%s]%> is not implemented yet and has no effect",
503 : attr_path.c_str ());
504 6 : if (item.get_item_kind () != AST::Item::Kind::Function)
505 : {
506 3 : rust_error_at (item.get_locus (),
507 : "%<#[%s]%> can only be applied to functions",
508 : attr_path.c_str ());
509 : }
510 6 : }
511 95817 : }
512 :
513 5050 : BuiltinAttributeChecker::BuiltinAttributeChecker () {}
514 :
515 : void
516 5050 : BuiltinAttributeChecker::go (AST::Crate &crate)
517 : {
518 5050 : visit (crate);
519 5050 : }
520 :
521 : void
522 5050 : BuiltinAttributeChecker::visit (AST::Crate &crate)
523 : {
524 17997 : for (auto &attr : crate.get_inner_attrs ())
525 : {
526 12947 : check_inner_attribute (attr);
527 : }
528 :
529 5050 : AST::DefaultASTVisitor::visit (crate);
530 5050 : }
531 :
532 : void
533 116097 : BuiltinAttributeChecker::visit (AST::Attribute &attribute)
534 : {
535 187118 : lookup_handler (attribute.get_path ().as_string ()).map ([&] (auto handler) {
536 71021 : handler (attribute);
537 : });
538 116097 : AST::DefaultASTVisitor::visit (attribute);
539 116097 : }
540 :
541 : void
542 1732 : BuiltinAttributeChecker::visit (AST::Module &module)
543 : {
544 1732 : default_outer_attribute_check (module);
545 1732 : }
546 :
547 : void
548 68 : BuiltinAttributeChecker::visit (AST::ExternCrate &extern_crate)
549 : {
550 68 : default_outer_attribute_check (extern_crate);
551 68 : }
552 :
553 : void
554 1882 : BuiltinAttributeChecker::visit (AST::UseDeclaration &declaration)
555 : {
556 1882 : default_outer_attribute_check (declaration);
557 1882 : }
558 :
559 : void
560 35046 : BuiltinAttributeChecker::visit (AST::Function &function)
561 : {
562 35046 : BuiltinAttrDefinition result;
563 107169 : for (auto &attribute : function.get_outer_attrs ())
564 : {
565 72127 : check_valid_attribute_for_item (attribute, function);
566 :
567 72127 : auto result = lookup_builtin (attribute);
568 72127 : if (!result)
569 4 : return;
570 :
571 72123 : if (result->name == Attrs::TARGET_FEATURE)
572 : {
573 1709 : if (!function.get_qualifiers ().is_unsafe ())
574 : {
575 1 : rust_error_at (
576 : attribute.get_locus (),
577 : "the %<#[target_feature]%> attribute can only be applied "
578 : "to %<unsafe%> functions");
579 : }
580 : }
581 70414 : else if (result->name == Attrs::NO_MANGLE)
582 : {
583 3 : check_no_mangle_function (attribute, function);
584 : }
585 72127 : }
586 :
587 35042 : AST::DefaultASTVisitor::visit (function);
588 35046 : }
589 :
590 : void
591 4352 : BuiltinAttributeChecker::visit (AST::TypeAlias &alias)
592 : {
593 4352 : default_outer_attribute_check (alias);
594 4352 : }
595 :
596 : void
597 2646 : BuiltinAttributeChecker::visit (AST::StructStruct &struct_item)
598 : {
599 2646 : default_outer_attribute_check (struct_item);
600 2646 : }
601 :
602 : void
603 1181 : BuiltinAttributeChecker::visit (AST::TupleStruct &tuple_struct)
604 : {
605 1181 : default_outer_attribute_check (tuple_struct);
606 1181 : }
607 :
608 : void
609 665 : BuiltinAttributeChecker::visit (AST::Enum &enumeration)
610 : {
611 665 : default_outer_attribute_check (enumeration);
612 665 : }
613 :
614 : void
615 116 : BuiltinAttributeChecker::visit (AST::Union &u)
616 : {
617 116 : default_outer_attribute_check (u);
618 116 : }
619 :
620 : void
621 1357 : BuiltinAttributeChecker::visit (AST::ConstantItem &item)
622 : {
623 1357 : default_outer_attribute_check (item);
624 1357 : }
625 :
626 : void
627 132 : BuiltinAttributeChecker::visit (AST::StaticItem &item)
628 : {
629 156 : for (auto &attr : item.get_outer_attrs ())
630 24 : check_valid_attribute_for_item (attr, item);
631 :
632 132 : AST::DefaultASTVisitor::visit (item);
633 132 : }
634 :
635 : void
636 4546 : BuiltinAttributeChecker::visit (AST::Trait &trait)
637 : {
638 4546 : default_outer_attribute_check (trait);
639 4546 : }
640 :
641 : void
642 1544 : BuiltinAttributeChecker::visit (AST::InherentImpl &impl)
643 : {
644 1544 : default_outer_attribute_check (impl);
645 1544 : }
646 :
647 : void
648 13182 : BuiltinAttributeChecker::visit (AST::TraitImpl &impl)
649 : {
650 13182 : default_outer_attribute_check (impl);
651 13182 : }
652 :
653 : void
654 1784 : BuiltinAttributeChecker::visit (AST::ExternBlock &block)
655 : {
656 1784 : default_outer_attribute_check (block);
657 1784 : }
658 :
659 : } // namespace Analysis
660 : } // namespace Rust
|