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 12363 : check_inner_attribute (const AST::Attribute &attribute)
32 : {
33 12363 : auto result_opt = lookup_builtin (attribute);
34 12363 : if (!result_opt.has_value ())
35 0 : return;
36 12363 : auto result = result_opt.value ();
37 :
38 12363 : 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 12363 : if (result.name == Values::Attributes::NEEDS_ALLOCATOR)
43 : {
44 1 : rust_warning_at (attribute.get_locus (), 0,
45 : "%<#[%s]%> is not implemented yet and has no effect",
46 2 : attribute.as_string ().c_str ());
47 : }
48 24726 : }
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 55564 : doc (const AST::Attribute &attribute)
99 : {
100 55564 : 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 55562 : 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 994 : case AST::AttrInput::TOKEN_TREE:
118 994 : {
119 : // FIXME: This doesn't check for #[doc(alias(...))]
120 994 : const auto &option = static_cast<const AST::DelimTokenTree &> (
121 994 : attribute.get_attr_input ());
122 994 : auto *meta_item = option.parse_to_meta_item ();
123 :
124 1999 : for (auto &item : meta_item->get_items ())
125 : {
126 1005 : if (item->is_key_value_pair ())
127 : {
128 773 : auto name_value
129 773 : = static_cast<AST::MetaNameValueStr *> (item.get ())
130 773 : ->get_name_value_pair ();
131 :
132 : // FIXME: Check for other stuff than #[doc(alias = ...)]
133 773 : if (name_value.first.as_string () == "alias")
134 761 : check_doc_alias (name_value.second, attribute.get_locus ());
135 773 : }
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 11 : export_name (const AST::Attribute &attribute)
250 : {
251 11 : 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 9 : auto &attr_input = attribute.get_attr_input ();
261 9 : if (attr_input.get_attr_input_type ()
262 : == AST::AttrInput::AttrInputType::LITERAL)
263 : {
264 7 : auto &literal_expr
265 7 : = static_cast<AST::AttrInputLiteral &> (attr_input).get_literal ();
266 7 : auto lit_type = literal_expr.get_lit_type ();
267 7 : 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 775 : lint (const AST::Attribute &attribute)
283 : {
284 775 : 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 775 : }
294 :
295 : void
296 552 : link_name (const AST::Attribute &attribute)
297 : {
298 552 : 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 552 : }
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 : void
358 17 : expect_no_input (const AST::Attribute &attribute)
359 : {
360 17 : if (attribute.has_attr_input ())
361 : {
362 5 : std::string attr_name = attribute.get_path ().as_string ();
363 5 : rust_error_at (attribute.get_locus (), "malformed %<%s%> attribute input",
364 : attr_name.c_str ());
365 5 : rust_inform (attribute.get_locus (), "must be of the form: %<#[%s]%>",
366 : attr_name.c_str ());
367 5 : }
368 17 : }
369 :
370 : } // namespace handlers
371 :
372 : const std::unordered_map<std::string, std::function<void (AST::Attribute &)>>
373 : attribute_checking_handlers = {
374 : {Attrs::DOC, handlers::doc},
375 : {Attrs::DEPRECATED, handlers::deprecated},
376 : {Attrs::LINK_SECTION, handlers::link_section},
377 : {Attrs::EXPORT_NAME, handlers::export_name},
378 : {Attrs::NO_MANGLE, handlers::expect_no_input},
379 : {Attrs::ALLOW, handlers::lint},
380 : {Attrs::DENY, handlers::lint},
381 : {Attrs::WARN, handlers::lint},
382 : {Attrs::FORBID, handlers::lint},
383 : {Attrs::LINK_NAME, handlers::link_name},
384 : {Attrs::PROC_MACRO_DERIVE, handlers::proc_macro_derive},
385 : {Attrs::PROC_MACRO, handlers::proc_macro},
386 : {Attrs::PROC_MACRO_ATTRIBUTE, handlers::proc_macro},
387 : {Attrs::TARGET_FEATURE, handlers::target_feature},
388 : {Attrs::RUSTC_STD_INTERNAL_SYMBOL, handlers::expect_no_input},
389 : {Attrs::RUSTC_ALLOCATOR, handlers::expect_no_input},
390 : {Attrs::RUSTC_ALLOCATOR_NOUNWIND, handlers::expect_no_input},
391 : {Attrs::GLOBAL_ALLOCATOR, handlers::expect_no_input},
392 : {Attrs::RUSTC_CONVERSION_SUGGESTION, handlers::expect_no_input},
393 : };
394 :
395 : tl::optional<std::function<void (AST::Attribute &)>>
396 100854 : lookup_handler (std::string attr_name)
397 : {
398 100854 : auto res = attribute_checking_handlers.find (attr_name);
399 100854 : if (res != attribute_checking_handlers.cend ())
400 58695 : return res->second;
401 42159 : return tl::nullopt;
402 : }
403 :
404 : static void
405 3 : check_no_mangle_function (const AST::Attribute &attribute,
406 : const AST::Function &fun)
407 : {
408 3 : if (!is_ascii_only (fun.get_function_name ().as_string ()))
409 0 : rust_error_at (fun.get_function_name ().get_locus (),
410 : "the %<#[no_mangle]%> attribute requires ASCII identifier");
411 3 : }
412 :
413 : /**
414 : * Emit an error when an attribute is attached
415 : * to an incompatable item type. e.g.:
416 : *
417 : * #[cold]
418 : * struct A(u8, u8);
419 : *
420 : * Note that "#[derive]" is handled
421 : * explicitly in rust-derive.cc
422 : */
423 : void
424 82656 : check_valid_attribute_for_item (const AST::Attribute &attr,
425 : const AST::Item &item)
426 : {
427 165312 : if (item.get_item_kind () != AST::Item::Kind::Function
428 186265 : && (attr.get_path () == Values::Attributes::TARGET_FEATURE
429 124560 : || attr.get_path () == Values::Attributes::COLD
430 103608 : || attr.get_path () == Values::Attributes::INLINE))
431 : {
432 1 : rust_error_at (attr.get_locus (),
433 : "the %<#[%s]%> attribute may only be applied to functions",
434 2 : attr.get_path ().as_string ().c_str ());
435 : }
436 165310 : else if (attr.get_path () == Values::Attributes::REPR
437 161 : && item.get_item_kind () != AST::Item::Kind::Enum
438 157 : && item.get_item_kind () != AST::Item::Kind::Union
439 82797 : && item.get_item_kind () != AST::Item::Kind::Struct)
440 : {
441 1 : rust_error_at (attr.get_locus (),
442 : "the %<#[%s]%> attribute may only be applied "
443 : "to structs, enums and unions",
444 2 : attr.get_path ().as_string ().c_str ());
445 : }
446 165308 : else if (attr.get_path () == Values::Attributes::GLOBAL_ALLOCATOR
447 82654 : && item.get_item_kind () != AST::Item::Kind::StaticItem)
448 : {
449 1 : rust_error_at (attr.get_locus (),
450 : "the %<#[%s]%> attribute may only be applied "
451 : "to static items",
452 2 : attr.get_path ().as_string ().c_str ());
453 : }
454 82653 : else if (attr.get_path () == Values::Attributes::RUSTC_CONVERSION_SUGGESTION)
455 : {
456 4 : auto attr_path = attr.get_path ().as_string ();
457 4 : rust_warning_at (attr.get_locus (), 0,
458 : "%<#[%s]%> is not implemented yet and has no effect",
459 : attr_path.c_str ());
460 4 : if (item.get_item_kind () != AST::Item::Kind::Function)
461 : {
462 3 : rust_error_at (item.get_locus (),
463 : "%<#[%s]%> can only be applied to functions",
464 : attr_path.c_str ());
465 : }
466 4 : }
467 82656 : }
468 :
469 4845 : BuiltinAttributeChecker::BuiltinAttributeChecker () {}
470 :
471 : void
472 4845 : BuiltinAttributeChecker::go (AST::Crate &crate)
473 : {
474 4845 : visit (crate);
475 4845 : }
476 :
477 : void
478 4845 : BuiltinAttributeChecker::visit (AST::Crate &crate)
479 : {
480 17208 : for (auto &attr : crate.get_inner_attrs ())
481 : {
482 12363 : check_inner_attribute (attr);
483 : }
484 :
485 4845 : AST::DefaultASTVisitor::visit (crate);
486 4845 : }
487 :
488 : void
489 100854 : BuiltinAttributeChecker::visit (AST::Attribute &attribute)
490 : {
491 159549 : lookup_handler (attribute.get_path ().as_string ()).map ([&] (auto handler) {
492 58695 : handler (attribute);
493 : });
494 100854 : AST::DefaultASTVisitor::visit (attribute);
495 100854 : }
496 :
497 : void
498 1537 : BuiltinAttributeChecker::visit (AST::Module &module)
499 : {
500 1537 : default_outer_attribute_check (module);
501 1537 : }
502 :
503 : void
504 28 : BuiltinAttributeChecker::visit (AST::ExternCrate &extern_crate)
505 : {
506 28 : default_outer_attribute_check (extern_crate);
507 28 : }
508 :
509 : void
510 1376 : BuiltinAttributeChecker::visit (AST::UseDeclaration &declaration)
511 : {
512 1376 : default_outer_attribute_check (declaration);
513 1376 : }
514 :
515 : void
516 31475 : BuiltinAttributeChecker::visit (AST::Function &function)
517 : {
518 31475 : BuiltinAttrDefinition result;
519 93178 : for (auto &attribute : function.get_outer_attrs ())
520 : {
521 61703 : check_valid_attribute_for_item (attribute, function);
522 :
523 61703 : auto result = lookup_builtin (attribute);
524 61703 : if (!result)
525 0 : return;
526 :
527 61703 : if (result->name == Attrs::TARGET_FEATURE)
528 : {
529 1709 : if (!function.get_qualifiers ().is_unsafe ())
530 : {
531 1 : rust_error_at (
532 : attribute.get_locus (),
533 : "the %<#[target_feature]%> attribute can only be applied "
534 : "to %<unsafe%> functions");
535 : }
536 : }
537 59994 : else if (result->name == Attrs::NO_MANGLE)
538 : {
539 3 : check_no_mangle_function (attribute, function);
540 : }
541 61703 : }
542 :
543 31475 : AST::DefaultASTVisitor::visit (function);
544 31475 : }
545 :
546 : void
547 3979 : BuiltinAttributeChecker::visit (AST::TypeAlias &alias)
548 : {
549 3979 : default_outer_attribute_check (alias);
550 3979 : }
551 :
552 : void
553 2286 : BuiltinAttributeChecker::visit (AST::StructStruct &struct_item)
554 : {
555 2286 : default_outer_attribute_check (struct_item);
556 2286 : }
557 :
558 : void
559 1088 : BuiltinAttributeChecker::visit (AST::TupleStruct &tuple_struct)
560 : {
561 1088 : default_outer_attribute_check (tuple_struct);
562 1088 : }
563 :
564 : void
565 597 : BuiltinAttributeChecker::visit (AST::Enum &enumeration)
566 : {
567 597 : default_outer_attribute_check (enumeration);
568 597 : }
569 :
570 : void
571 114 : BuiltinAttributeChecker::visit (AST::Union &u)
572 : {
573 114 : default_outer_attribute_check (u);
574 114 : }
575 :
576 : void
577 1304 : BuiltinAttributeChecker::visit (AST::ConstantItem &item)
578 : {
579 1304 : default_outer_attribute_check (item);
580 1304 : }
581 :
582 : void
583 103 : BuiltinAttributeChecker::visit (AST::StaticItem &item)
584 : {
585 116 : for (auto &attr : item.get_outer_attrs ())
586 13 : check_valid_attribute_for_item (attr, item);
587 :
588 103 : AST::DefaultASTVisitor::visit (item);
589 103 : }
590 :
591 : void
592 4139 : BuiltinAttributeChecker::visit (AST::Trait &trait)
593 : {
594 4139 : default_outer_attribute_check (trait);
595 4139 : }
596 :
597 : void
598 1298 : BuiltinAttributeChecker::visit (AST::InherentImpl &impl)
599 : {
600 1298 : default_outer_attribute_check (impl);
601 1298 : }
602 :
603 : void
604 11566 : BuiltinAttributeChecker::visit (AST::TraitImpl &impl)
605 : {
606 11566 : default_outer_attribute_check (impl);
607 11566 : }
608 :
609 : void
610 1729 : BuiltinAttributeChecker::visit (AST::ExternBlock &block)
611 : {
612 1729 : default_outer_attribute_check (block);
613 1729 : }
614 :
615 : } // namespace Analysis
616 : } // namespace Rust
|