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