]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Rollup merge of #97236 - cjgillot:recover-lifetime-res, r=jackh726
[rust.git] / compiler / rustc_passes / src / check_attr.rs
1 //! This module implements some validity checks for attributes.
2 //! In particular it verifies that `#[inline]` and `#[repr]` attributes are
3 //! attached to items that actually support them and if there are
4 //! conflicts between multiple such attributes attached to the same
5 //! item.
6
7 use rustc_ast::tokenstream::DelimSpan;
8 use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MacArgs, MetaItemKind, NestedMetaItem};
9 use rustc_data_structures::fx::FxHashMap;
10 use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
11 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
12 use rustc_hir as hir;
13 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
14 use rustc_hir::intravisit::{self, Visitor};
15 use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
16 use rustc_hir::{MethodKind, Target};
17 use rustc_middle::hir::nested_filter;
18 use rustc_middle::ty::query::Providers;
19 use rustc_middle::ty::TyCtxt;
20 use rustc_session::lint::builtin::{
21     CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
22 };
23 use rustc_session::parse::feature_err;
24 use rustc_span::symbol::{kw, sym, Symbol};
25 use rustc_span::{Span, DUMMY_SP};
26 use rustc_target::spec::abi::Abi;
27 use std::collections::hash_map::Entry;
28
29 pub(crate) fn target_from_impl_item<'tcx>(
30     tcx: TyCtxt<'tcx>,
31     impl_item: &hir::ImplItem<'_>,
32 ) -> Target {
33     match impl_item.kind {
34         hir::ImplItemKind::Const(..) => Target::AssocConst,
35         hir::ImplItemKind::Fn(..) => {
36             let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id());
37             let containing_item = tcx.hir().expect_item(parent_hir_id);
38             let containing_impl_is_for_trait = match &containing_item.kind {
39                 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
40                 _ => bug!("parent of an ImplItem must be an Impl"),
41             };
42             if containing_impl_is_for_trait {
43                 Target::Method(MethodKind::Trait { body: true })
44             } else {
45                 Target::Method(MethodKind::Inherent)
46             }
47         }
48         hir::ImplItemKind::TyAlias(..) => Target::AssocTy,
49     }
50 }
51
52 #[derive(Clone, Copy)]
53 enum ItemLike<'tcx> {
54     Item(&'tcx Item<'tcx>),
55     ForeignItem(&'tcx ForeignItem<'tcx>),
56 }
57
58 struct CheckAttrVisitor<'tcx> {
59     tcx: TyCtxt<'tcx>,
60 }
61
62 impl CheckAttrVisitor<'_> {
63     /// Checks any attribute.
64     fn check_attributes(
65         &self,
66         hir_id: HirId,
67         span: Span,
68         target: Target,
69         item: Option<ItemLike<'_>>,
70     ) {
71         let mut doc_aliases = FxHashMap::default();
72         let mut is_valid = true;
73         let mut specified_inline = None;
74         let mut seen = FxHashMap::default();
75         let attrs = self.tcx.hir().attrs(hir_id);
76         for attr in attrs {
77             let attr_is_valid = match attr.name_or_empty() {
78                 sym::inline => self.check_inline(hir_id, attr, span, target),
79                 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
80                 sym::marker => self.check_marker(hir_id, attr, span, target),
81                 sym::rustc_must_implement_one_of => {
82                     self.check_rustc_must_implement_one_of(attr, span, target)
83                 }
84                 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
85                 sym::thread_local => self.check_thread_local(attr, span, target),
86                 sym::track_caller => {
87                     self.check_track_caller(hir_id, attr.span, attrs, span, target)
88                 }
89                 sym::doc => self.check_doc_attrs(
90                     attr,
91                     hir_id,
92                     target,
93                     &mut specified_inline,
94                     &mut doc_aliases,
95                 ),
96                 sym::no_link => self.check_no_link(hir_id, &attr, span, target),
97                 sym::export_name => self.check_export_name(hir_id, &attr, span, target),
98                 sym::rustc_layout_scalar_valid_range_start
99                 | sym::rustc_layout_scalar_valid_range_end => {
100                     self.check_rustc_layout_scalar_valid_range(&attr, span, target)
101                 }
102                 sym::allow_internal_unstable => {
103                     self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
104                 }
105                 sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
106                 sym::rustc_allow_const_fn_unstable => {
107                     self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
108                 }
109                 sym::rustc_std_internal_symbol => {
110                     self.check_rustc_std_internal_symbol(&attr, span, target)
111                 }
112                 sym::naked => self.check_naked(hir_id, attr, span, target),
113                 sym::rustc_legacy_const_generics => {
114                     self.check_rustc_legacy_const_generics(&attr, span, target, item)
115                 }
116                 sym::rustc_lint_query_instability => {
117                     self.check_rustc_lint_query_instability(&attr, span, target)
118                 }
119                 sym::rustc_clean
120                 | sym::rustc_dirty
121                 | sym::rustc_if_this_changed
122                 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
123                 sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
124                 sym::default_method_body_is_const => {
125                     self.check_default_method_body_is_const(attr, span, target)
126                 }
127                 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
128                 sym::must_use => self.check_must_use(hir_id, &attr, span, target),
129                 sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
130                 sym::rustc_allow_incoherent_impl => {
131                     self.check_allow_incoherent_impl(&attr, span, target)
132                 }
133                 sym::rustc_has_incoherent_inherent_impls => {
134                     self.check_has_incoherent_inherent_impls(&attr, span, target)
135                 }
136                 sym::rustc_const_unstable
137                 | sym::rustc_const_stable
138                 | sym::unstable
139                 | sym::stable
140                 | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
141                 _ => true,
142             };
143             is_valid &= attr_is_valid;
144
145             // lint-only checks
146             match attr.name_or_empty() {
147                 sym::cold => self.check_cold(hir_id, attr, span, target),
148                 sym::link => self.check_link(hir_id, attr, span, target),
149                 sym::link_name => self.check_link_name(hir_id, attr, span, target),
150                 sym::link_section => self.check_link_section(hir_id, attr, span, target),
151                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
152                 sym::deprecated | sym::rustc_deprecated => {
153                     self.check_deprecated(hir_id, attr, span, target)
154                 }
155                 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
156                 sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
157                 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
158                 sym::macro_export => self.check_macro_export(hir_id, attr, target),
159                 sym::ignore | sym::should_panic | sym::proc_macro_derive => {
160                     self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
161                 }
162                 sym::automatically_derived => {
163                     self.check_generic_attr(hir_id, attr, target, &[Target::Impl])
164                 }
165                 sym::no_implicit_prelude => {
166                     self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
167                 }
168                 _ => {}
169             }
170
171             let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
172
173             if hir_id != CRATE_HIR_ID {
174                 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
175                     attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
176                 {
177                     self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
178                         let msg = match attr.style {
179                             ast::AttrStyle::Outer => {
180                                 "crate-level attribute should be an inner attribute: add an exclamation \
181                                  mark: `#![foo]`"
182                             }
183                             ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
184                         };
185                         lint.build(msg).emit();
186                     });
187                 }
188             }
189
190             if let Some(BuiltinAttribute { duplicates, .. }) = builtin {
191                 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
192             }
193
194             self.check_unused_attribute(hir_id, attr)
195         }
196
197         if !is_valid {
198             return;
199         }
200
201         // FIXME(@lcnr): this doesn't belong here.
202         if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
203             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
204         }
205
206         self.check_repr(attrs, span, target, item, hir_id);
207         self.check_used(attrs, target);
208     }
209
210     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
211         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
212             lint.build(&format!(
213                 "`#[{sym}]` is ignored on struct fields, match arms and macro defs",
214             ))
215             .warn(
216                 "this was previously accepted by the compiler but is \
217                  being phased out; it will become a hard error in \
218                  a future release!",
219             )
220             .note(
221                 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
222                  for more information",
223             )
224             .emit();
225         });
226     }
227
228     fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
229         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
230             lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms"))
231                 .warn(
232                     "this was previously accepted by the compiler but is \
233                  being phased out; it will become a hard error in \
234                  a future release!",
235                 )
236                 .note(
237                     "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
238                  for more information",
239                 )
240                 .emit();
241         });
242     }
243
244     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
245     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
246         match target {
247             Target::Fn
248             | Target::Closure
249             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
250             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
251                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
252                     lint.build("`#[inline]` is ignored on function prototypes").emit();
253                 });
254                 true
255             }
256             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
257             // just a lint, because we previously erroneously allowed it and some crates used it
258             // accidentally, to to be compatible with crates depending on them, we can't throw an
259             // error here.
260             Target::AssocConst => {
261                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
262                     lint.build("`#[inline]` is ignored on constants")
263                         .warn(
264                             "this was previously accepted by the compiler but is \
265                              being phased out; it will become a hard error in \
266                              a future release!",
267                         )
268                         .note(
269                             "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
270                              for more information",
271                         )
272                         .emit();
273                 });
274                 true
275             }
276             // FIXME(#80564): Same for fields, arms, and macro defs
277             Target::Field | Target::Arm | Target::MacroDef => {
278                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
279                 true
280             }
281             _ => {
282                 struct_span_err!(
283                     self.tcx.sess,
284                     attr.span,
285                     E0518,
286                     "attribute should be applied to function or closure",
287                 )
288                 .span_label(span, "not a function or closure")
289                 .emit();
290                 false
291             }
292         }
293     }
294
295     fn check_generic_attr(
296         &self,
297         hir_id: HirId,
298         attr: &Attribute,
299         target: Target,
300         allowed_targets: &[Target],
301     ) {
302         if !allowed_targets.iter().any(|t| t == &target) {
303             let name = attr.name_or_empty();
304             let mut i = allowed_targets.iter();
305             // Pluralize
306             let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
307             let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
308                 if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
309                     b.push_str(", and ");
310                 } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
311                     b.push_str(" and ");
312                 } else {
313                     b.push_str(", ");
314                 }
315                 // Pluralize
316                 b.push_str(&(allowed_target.to_string() + "s"));
317                 b
318             });
319             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
320                 lint.build(&format!("`#[{name}]` only has an effect on {}", supported_names))
321                     .emit();
322             });
323         }
324     }
325
326     /// Checks if `#[naked]` is applied to a function definition.
327     fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
328         match target {
329             Target::Fn
330             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
331             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
332             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
333             // erroneously allowed it and some crates used it accidentally, to to be compatible
334             // with crates depending on them, we can't throw an error here.
335             Target::Field | Target::Arm | Target::MacroDef => {
336                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
337                 true
338             }
339             _ => {
340                 self.tcx
341                     .sess
342                     .struct_span_err(
343                         attr.span,
344                         "attribute should be applied to a function definition",
345                     )
346                     .span_label(span, "not a function definition")
347                     .emit();
348                 false
349             }
350         }
351     }
352
353     /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition.
354     fn check_cmse_nonsecure_entry(&self, attr: &Attribute, span: Span, target: Target) -> bool {
355         match target {
356             Target::Fn
357             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
358             _ => {
359                 self.tcx
360                     .sess
361                     .struct_span_err(
362                         attr.span,
363                         "attribute should be applied to a function definition",
364                     )
365                     .span_label(span, "not a function definition")
366                     .emit();
367                 false
368             }
369         }
370     }
371
372     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
373     fn check_track_caller(
374         &self,
375         hir_id: HirId,
376         attr_span: Span,
377         attrs: &[Attribute],
378         span: Span,
379         target: Target,
380     ) -> bool {
381         match target {
382             _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
383                 struct_span_err!(
384                     self.tcx.sess,
385                     attr_span,
386                     E0736,
387                     "cannot use `#[track_caller]` with `#[naked]`",
388                 )
389                 .emit();
390                 false
391             }
392             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
393             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
394             // `#[track_caller]` attribute with just a lint, because we previously
395             // erroneously allowed it and some crates used it accidentally, to to be compatible
396             // with crates depending on them, we can't throw an error here.
397             Target::Field | Target::Arm | Target::MacroDef => {
398                 for attr in attrs {
399                     self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
400                 }
401                 true
402             }
403             _ => {
404                 struct_span_err!(
405                     self.tcx.sess,
406                     attr_span,
407                     E0739,
408                     "attribute should be applied to function"
409                 )
410                 .span_label(span, "not a function")
411                 .emit();
412                 false
413             }
414         }
415     }
416
417     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
418     fn check_non_exhaustive(
419         &self,
420         hir_id: HirId,
421         attr: &Attribute,
422         span: Span,
423         target: Target,
424     ) -> bool {
425         match target {
426             Target::Struct | Target::Enum | Target::Variant => true,
427             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
428             // `#[non_exhaustive]` attribute with just a lint, because we previously
429             // erroneously allowed it and some crates used it accidentally, to to be compatible
430             // with crates depending on them, we can't throw an error here.
431             Target::Field | Target::Arm | Target::MacroDef => {
432                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
433                 true
434             }
435             _ => {
436                 struct_span_err!(
437                     self.tcx.sess,
438                     attr.span,
439                     E0701,
440                     "attribute can only be applied to a struct or enum"
441                 )
442                 .span_label(span, "not a struct or enum")
443                 .emit();
444                 false
445             }
446         }
447     }
448
449     /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
450     fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
451         match target {
452             Target::Trait => true,
453             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
454             // `#[marker]` attribute with just a lint, because we previously
455             // erroneously allowed it and some crates used it accidentally, to to be compatible
456             // with crates depending on them, we can't throw an error here.
457             Target::Field | Target::Arm | Target::MacroDef => {
458                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
459                 true
460             }
461             _ => {
462                 self.tcx
463                     .sess
464                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
465                     .span_label(span, "not a trait")
466                     .emit();
467                 false
468             }
469         }
470     }
471
472     /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid.
473     fn check_rustc_must_implement_one_of(
474         &self,
475         attr: &Attribute,
476         span: Span,
477         target: Target,
478     ) -> bool {
479         match target {
480             Target::Trait => true,
481             _ => {
482                 self.tcx
483                     .sess
484                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
485                     .span_label(span, "not a trait")
486                     .emit();
487                 false
488             }
489         }
490     }
491
492     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
493     fn check_target_feature(
494         &self,
495         hir_id: HirId,
496         attr: &Attribute,
497         span: Span,
498         target: Target,
499     ) -> bool {
500         match target {
501             Target::Fn
502             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
503             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
504             // crates used this, so only emit a warning.
505             Target::Statement => {
506                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
507                     lint.build("attribute should be applied to a function")
508                         .warn(
509                             "this was previously accepted by the compiler but is \
510                              being phased out; it will become a hard error in \
511                              a future release!",
512                         )
513                         .span_label(span, "not a function")
514                         .emit();
515                 });
516                 true
517             }
518             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
519             // `#[target_feature]` attribute with just a lint, because we previously
520             // erroneously allowed it and some crates used it accidentally, to to be compatible
521             // with crates depending on them, we can't throw an error here.
522             Target::Field | Target::Arm | Target::MacroDef => {
523                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
524                 true
525             }
526             _ => {
527                 self.tcx
528                     .sess
529                     .struct_span_err(attr.span, "attribute should be applied to a function")
530                     .span_label(span, "not a function")
531                     .emit();
532                 false
533             }
534         }
535     }
536
537     /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid.
538     fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool {
539         match target {
540             Target::ForeignStatic | Target::Static => true,
541             _ => {
542                 self.tcx
543                     .sess
544                     .struct_span_err(attr.span, "attribute should be applied to a static")
545                     .span_label(span, "not a static")
546                     .emit();
547                 false
548             }
549         }
550     }
551
552     fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
553         self.tcx
554             .sess
555             .struct_span_err(
556                 meta.span(),
557                 &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
558             )
559             .emit();
560     }
561
562     fn check_doc_alias_value(
563         &self,
564         meta: &NestedMetaItem,
565         doc_alias: Symbol,
566         hir_id: HirId,
567         target: Target,
568         is_list: bool,
569         aliases: &mut FxHashMap<String, Span>,
570     ) -> bool {
571         let tcx = self.tcx;
572         let err_fn = move |span: Span, msg: &str| {
573             tcx.sess.span_err(
574                 span,
575                 &format!(
576                     "`#[doc(alias{})]` {}",
577                     if is_list { "(\"...\")" } else { " = \"...\"" },
578                     msg,
579                 ),
580             );
581             false
582         };
583         if doc_alias == kw::Empty {
584             return err_fn(
585                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
586                 "attribute cannot have empty value",
587             );
588         }
589
590         let doc_alias_str = doc_alias.as_str();
591         if let Some(c) = doc_alias_str
592             .chars()
593             .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
594         {
595             self.tcx.sess.span_err(
596                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
597                 &format!(
598                     "{:?} character isn't allowed in `#[doc(alias{})]`",
599                     c,
600                     if is_list { "(\"...\")" } else { " = \"...\"" },
601                 ),
602             );
603             return false;
604         }
605         if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
606             return err_fn(
607                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
608                 "cannot start or end with ' '",
609             );
610         }
611         if let Some(err) = match target {
612             Target::Impl => Some("implementation block"),
613             Target::ForeignMod => Some("extern block"),
614             Target::AssocTy => {
615                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
616                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
617                 if Target::from_item(containing_item) == Target::Impl {
618                     Some("type alias in implementation block")
619                 } else {
620                     None
621                 }
622             }
623             Target::AssocConst => {
624                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
625                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
626                 // We can't link to trait impl's consts.
627                 let err = "associated constant in trait implementation block";
628                 match containing_item.kind {
629                     ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
630                     _ => None,
631                 }
632             }
633             // we check the validity of params elsewhere
634             Target::Param => return false,
635             _ => None,
636         } {
637             return err_fn(meta.span(), &format!("isn't allowed on {}", err));
638         }
639         let item_name = self.tcx.hir().name(hir_id);
640         if item_name == doc_alias {
641             return err_fn(meta.span(), "is the same as the item's name");
642         }
643         let span = meta.span();
644         if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
645             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
646                 lint.build("doc alias is duplicated")
647                     .span_label(*entry.entry.get(), "first defined here")
648                     .emit();
649             });
650         }
651         true
652     }
653
654     fn check_doc_alias(
655         &self,
656         meta: &NestedMetaItem,
657         hir_id: HirId,
658         target: Target,
659         aliases: &mut FxHashMap<String, Span>,
660     ) -> bool {
661         if let Some(values) = meta.meta_item_list() {
662             let mut errors = 0;
663             for v in values {
664                 match v.literal() {
665                     Some(l) => match l.kind {
666                         LitKind::Str(s, _) => {
667                             if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) {
668                                 errors += 1;
669                             }
670                         }
671                         _ => {
672                             self.tcx
673                                 .sess
674                                 .struct_span_err(
675                                     v.span(),
676                                     "`#[doc(alias(\"a\"))]` expects string literals",
677                                 )
678                                 .emit();
679                             errors += 1;
680                         }
681                     },
682                     None => {
683                         self.tcx
684                             .sess
685                             .struct_span_err(
686                                 v.span(),
687                                 "`#[doc(alias(\"a\"))]` expects string literals",
688                             )
689                             .emit();
690                         errors += 1;
691                     }
692                 }
693             }
694             errors == 0
695         } else if let Some(doc_alias) = meta.value_str() {
696             self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
697         } else {
698             self.tcx
699                 .sess
700                 .struct_span_err(
701                     meta.span(),
702                     "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
703                      strings `#[doc(alias(\"a\", \"b\"))]`",
704                 )
705                 .emit();
706             false
707         }
708     }
709
710     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
711         let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
712         if doc_keyword == kw::Empty {
713             self.doc_attr_str_error(meta, "keyword");
714             return false;
715         }
716         match self.tcx.hir().find(hir_id).and_then(|node| match node {
717             hir::Node::Item(item) => Some(&item.kind),
718             _ => None,
719         }) {
720             Some(ItemKind::Mod(ref module)) => {
721                 if !module.item_ids.is_empty() {
722                     self.tcx
723                         .sess
724                         .struct_span_err(
725                             meta.span(),
726                             "`#[doc(keyword = \"...\")]` can only be used on empty modules",
727                         )
728                         .emit();
729                     return false;
730                 }
731             }
732             _ => {
733                 self.tcx
734                     .sess
735                     .struct_span_err(
736                         meta.span(),
737                         "`#[doc(keyword = \"...\")]` can only be used on modules",
738                     )
739                     .emit();
740                 return false;
741             }
742         }
743         if !rustc_lexer::is_ident(doc_keyword.as_str()) {
744             self.tcx
745                 .sess
746                 .struct_span_err(
747                     meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
748                     &format!("`{doc_keyword}` is not a valid identifier"),
749                 )
750                 .emit();
751             return false;
752         }
753         true
754     }
755
756     /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
757     ///
758     /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
759     /// if there are conflicting attributes for one item.
760     ///
761     /// `specified_inline` is used to keep track of whether we have
762     /// already seen an inlining attribute for this item.
763     /// If so, `specified_inline` holds the value and the span of
764     /// the first `inline`/`no_inline` attribute.
765     fn check_doc_inline(
766         &self,
767         attr: &Attribute,
768         meta: &NestedMetaItem,
769         hir_id: HirId,
770         target: Target,
771         specified_inline: &mut Option<(bool, Span)>,
772     ) -> bool {
773         if target == Target::Use || target == Target::ExternCrate {
774             let do_inline = meta.name_or_empty() == sym::inline;
775             if let Some((prev_inline, prev_span)) = *specified_inline {
776                 if do_inline != prev_inline {
777                     let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
778                     spans.push_span_label(prev_span, String::from("this attribute..."));
779                     spans.push_span_label(
780                         meta.span(),
781                         String::from("...conflicts with this attribute"),
782                     );
783                     self.tcx
784                         .sess
785                         .struct_span_err(spans, "conflicting doc inlining attributes")
786                         .help("remove one of the conflicting attributes")
787                         .emit();
788                     return false;
789                 }
790                 true
791             } else {
792                 *specified_inline = Some((do_inline, meta.span()));
793                 true
794             }
795         } else {
796             self.tcx.struct_span_lint_hir(
797                 INVALID_DOC_ATTRIBUTES,
798                 hir_id,
799                 meta.span(),
800                 |lint| {
801                     let mut err = lint.build(
802                         "this attribute can only be applied to a `use` item",
803                     );
804                     err.span_label(meta.span(), "only applicable on `use` items");
805                     if attr.style == AttrStyle::Outer {
806                         err.span_label(
807                             self.tcx.hir().span(hir_id),
808                             "not a `use` item",
809                         );
810                     }
811                     err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information")
812                         .emit();
813                 },
814             );
815             false
816         }
817     }
818
819     /// Checks `#[doc(hidden)]` attributes. Returns `true` if valid.
820     fn check_doc_hidden(
821         &self,
822         attr: &Attribute,
823         meta_index: usize,
824         meta: &NestedMetaItem,
825         hir_id: HirId,
826         target: Target,
827     ) -> bool {
828         if let Target::AssocConst
829         | Target::AssocTy
830         | Target::Method(MethodKind::Trait { body: true }) = target
831         {
832             let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
833             let containing_item = self.tcx.hir().expect_item(parent_hir_id);
834
835             if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = containing_item.kind {
836                 let meta_items = attr.meta_item_list().unwrap();
837
838                 let (span, replacement_span) = if meta_items.len() == 1 {
839                     (attr.span, attr.span)
840                 } else {
841                     let meta_span = meta.span();
842                     (
843                         meta_span,
844                         meta_span.until(match meta_items.get(meta_index + 1) {
845                             Some(next_item) => next_item.span(),
846                             None => match attr.get_normal_item().args {
847                                 MacArgs::Delimited(DelimSpan { close, .. }, ..) => close,
848                                 _ => unreachable!(),
849                             },
850                         }),
851                     )
852                 };
853
854                 // FIXME: #[doc(hidden)] was previously erroneously allowed on trait impl items,
855                 // so for backward compatibility only emit a warning and do not mark it as invalid.
856                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| {
857                     lint.build("`#[doc(hidden)]` is ignored on trait impl items")
858                         .warn(
859                             "this was previously accepted by the compiler but is \
860                              being phased out; it will become a hard error in \
861                              a future release!",
862                         )
863                         .note(
864                             "whether the impl item is `doc(hidden)` or not \
865                              entirely depends on the corresponding trait item",
866                         )
867                         .span_suggestion(
868                             replacement_span,
869                             "remove this attribute",
870                             String::new(),
871                             Applicability::MachineApplicable,
872                         )
873                         .emit();
874                 });
875             }
876         }
877
878         true
879     }
880
881     /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
882     fn check_attr_not_crate_level(
883         &self,
884         meta: &NestedMetaItem,
885         hir_id: HirId,
886         attr_name: &str,
887     ) -> bool {
888         if CRATE_HIR_ID == hir_id {
889             self.tcx
890                 .sess
891                 .struct_span_err(
892                     meta.span(),
893                     &format!(
894                         "`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute",
895                     ),
896                 )
897                 .emit();
898             return false;
899         }
900         true
901     }
902
903     /// Checks that an attribute is used at the crate level. Returns `true` if valid.
904     fn check_attr_crate_level(
905         &self,
906         attr: &Attribute,
907         meta: &NestedMetaItem,
908         hir_id: HirId,
909     ) -> bool {
910         if hir_id != CRATE_HIR_ID {
911             self.tcx.struct_span_lint_hir(
912                 INVALID_DOC_ATTRIBUTES,
913                 hir_id,
914                 meta.span(),
915                 |lint| {
916                     let mut err = lint.build(
917                         "this attribute can only be applied at the crate level",
918                     );
919                     if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID {
920                         if let Ok(mut src) =
921                             self.tcx.sess.source_map().span_to_snippet(attr.span)
922                         {
923                             src.insert(1, '!');
924                             err.span_suggestion_verbose(
925                                 attr.span,
926                                 "to apply to the crate, use an inner attribute",
927                                 src,
928                                 Applicability::MaybeIncorrect,
929                             );
930                         } else {
931                             err.span_help(
932                                 attr.span,
933                                 "to apply to the crate, use an inner attribute",
934                             );
935                         }
936                     }
937                     err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information")
938                         .emit();
939                 },
940             );
941             return false;
942         }
943         true
944     }
945
946     /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if
947     /// valid.
948     fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
949         let mut is_valid = true;
950         if let Some(metas) = meta.meta_item_list() {
951             for i_meta in metas {
952                 match i_meta.name_or_empty() {
953                     sym::attr | sym::no_crate_inject => {}
954                     _ => {
955                         self.tcx.struct_span_lint_hir(
956                             INVALID_DOC_ATTRIBUTES,
957                             hir_id,
958                             i_meta.span(),
959                             |lint| {
960                                 lint.build(&format!(
961                                     "unknown `doc(test)` attribute `{}`",
962                                     rustc_ast_pretty::pprust::path_to_string(
963                                         &i_meta.meta_item().unwrap().path
964                                     ),
965                                 ))
966                                 .emit();
967                             },
968                         );
969                         is_valid = false;
970                     }
971                 }
972             }
973         } else {
974             self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
975                 lint.build("`#[doc(test(...)]` takes a list of attributes").emit();
976             });
977             is_valid = false;
978         }
979         is_valid
980     }
981
982     /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
983     ///
984     /// `specified_inline` should be initialized to `None` and kept for the scope
985     /// of one item. Read the documentation of [`check_doc_inline`] for more information.
986     ///
987     /// [`check_doc_inline`]: Self::check_doc_inline
988     fn check_doc_attrs(
989         &self,
990         attr: &Attribute,
991         hir_id: HirId,
992         target: Target,
993         specified_inline: &mut Option<(bool, Span)>,
994         aliases: &mut FxHashMap<String, Span>,
995     ) -> bool {
996         let mut is_valid = true;
997
998         if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() {
999             for (meta_index, meta) in list.into_iter().enumerate() {
1000                 if let Some(i_meta) = meta.meta_item() {
1001                     match i_meta.name_or_empty() {
1002                         sym::alias
1003                             if !self.check_attr_not_crate_level(meta, hir_id, "alias")
1004                                 || !self.check_doc_alias(meta, hir_id, target, aliases) =>
1005                         {
1006                             is_valid = false
1007                         }
1008
1009                         sym::keyword
1010                             if !self.check_attr_not_crate_level(meta, hir_id, "keyword")
1011                                 || !self.check_doc_keyword(meta, hir_id) =>
1012                         {
1013                             is_valid = false
1014                         }
1015
1016                         sym::html_favicon_url
1017                         | sym::html_logo_url
1018                         | sym::html_playground_url
1019                         | sym::issue_tracker_base_url
1020                         | sym::html_root_url
1021                         | sym::html_no_source
1022                         | sym::test
1023                             if !self.check_attr_crate_level(attr, meta, hir_id) =>
1024                         {
1025                             is_valid = false;
1026                         }
1027
1028                         sym::inline | sym::no_inline
1029                             if !self.check_doc_inline(
1030                                 attr,
1031                                 meta,
1032                                 hir_id,
1033                                 target,
1034                                 specified_inline,
1035                             ) =>
1036                         {
1037                             is_valid = false;
1038                         }
1039
1040                         sym::hidden if !self.check_doc_hidden(attr,
1041                             meta_index,
1042                             meta,
1043                             hir_id,
1044                             target,
1045                             ) => {
1046                             is_valid = false;
1047                         }
1048
1049                         // no_default_passes: deprecated
1050                         // passes: deprecated
1051                         // plugins: removed, but rustdoc warns about it itself
1052                         sym::alias
1053                         | sym::cfg
1054                         | sym::cfg_hide
1055                         | sym::hidden
1056                         | sym::html_favicon_url
1057                         | sym::html_logo_url
1058                         | sym::html_no_source
1059                         | sym::html_playground_url
1060                         | sym::html_root_url
1061                         | sym::inline
1062                         | sym::issue_tracker_base_url
1063                         | sym::keyword
1064                         | sym::masked
1065                         | sym::no_default_passes
1066                         | sym::no_inline
1067                         | sym::notable_trait
1068                         | sym::passes
1069                         | sym::plugins => {}
1070
1071                         sym::test => {
1072                             if !self.check_test_attr(meta, hir_id) {
1073                                 is_valid = false;
1074                             }
1075                         }
1076
1077                         sym::primitive => {
1078                             if !self.tcx.features().rustdoc_internals {
1079                                 self.tcx.struct_span_lint_hir(
1080                                     INVALID_DOC_ATTRIBUTES,
1081                                     hir_id,
1082                                     i_meta.span,
1083                                     |lint| {
1084                                         let mut diag = lint.build(
1085                                             "`doc(primitive)` should never have been stable",
1086                                         );
1087                                         diag.emit();
1088                                     },
1089                                 );
1090                             }
1091                         }
1092
1093                         _ => {
1094                             self.tcx.struct_span_lint_hir(
1095                                 INVALID_DOC_ATTRIBUTES,
1096                                 hir_id,
1097                                 i_meta.span,
1098                                 |lint| {
1099                                     let mut diag = lint.build(&format!(
1100                                         "unknown `doc` attribute `{}`",
1101                                         rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
1102                                     ));
1103                                     if i_meta.has_name(sym::spotlight) {
1104                                         diag.note(
1105                                             "`doc(spotlight)` was renamed to `doc(notable_trait)`",
1106                                         );
1107                                         diag.span_suggestion_short(
1108                                             i_meta.span,
1109                                             "use `notable_trait` instead",
1110                                             String::from("notable_trait"),
1111                                             Applicability::MachineApplicable,
1112                                         );
1113                                         diag.note("`doc(spotlight)` is now a no-op");
1114                                     }
1115                                     if i_meta.has_name(sym::include) {
1116                                         if let Some(value) = i_meta.value_str() {
1117                                             // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
1118                                             let applicability = if list.len() == 1 {
1119                                                 Applicability::MachineApplicable
1120                                             } else {
1121                                                 Applicability::MaybeIncorrect
1122                                             };
1123                                             let inner = if attr.style == AttrStyle::Inner {
1124                                                 "!"
1125                                             } else {
1126                                                 ""
1127                                             };
1128                                             diag.span_suggestion(
1129                                                 attr.meta().unwrap().span,
1130                                                 "use `doc = include_str!` instead",
1131                                                 format!(
1132                                                     "#{inner}[doc = include_str!(\"{value}\")]",
1133                                                 ),
1134                                                 applicability,
1135                                             );
1136                                         }
1137                                     }
1138                                     diag.emit();
1139                                 },
1140                             );
1141                             is_valid = false;
1142                         }
1143                     }
1144                 } else {
1145                     self.tcx.struct_span_lint_hir(
1146                         INVALID_DOC_ATTRIBUTES,
1147                         hir_id,
1148                         meta.span(),
1149                         |lint| {
1150                             lint.build(&"invalid `doc` attribute").emit();
1151                         },
1152                     );
1153                     is_valid = false;
1154                 }
1155             }
1156         }
1157
1158         is_valid
1159     }
1160
1161     /// Warns against some misuses of `#[pass_by_value]`
1162     fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1163         match target {
1164             Target::Struct | Target::Enum | Target::TyAlias => true,
1165             _ => {
1166                 self.tcx
1167                     .sess
1168                     .struct_span_err(
1169                         attr.span,
1170                         "`pass_by_value` attribute should be applied to a struct, enum or type alias.",
1171                     )
1172                     .span_label(span, "is not a struct, enum or type alias")
1173                     .emit();
1174                 false
1175             }
1176         }
1177     }
1178
1179     fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1180         match target {
1181             Target::Method(MethodKind::Inherent) => true,
1182             _ => {
1183                 self.tcx
1184                     .sess
1185                     .struct_span_err(
1186                         attr.span,
1187                         "`rustc_allow_incoherent_impl` attribute should be applied to impl items.",
1188                     )
1189                     .span_label(span, "the only currently supported targets are inherent methods")
1190                     .emit();
1191                 false
1192             }
1193         }
1194     }
1195
1196     fn check_has_incoherent_inherent_impls(
1197         &self,
1198         attr: &Attribute,
1199         span: Span,
1200         target: Target,
1201     ) -> bool {
1202         match target {
1203             Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
1204                 true
1205             }
1206             _ => {
1207                 self.tcx
1208                     .sess
1209                     .struct_span_err(
1210                         attr.span,
1211                         "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
1212                     )
1213                     .span_label(span, "only adts, extern types and traits are supported")
1214                     .emit();
1215                 false
1216             }
1217         }
1218     }
1219
1220     /// Warns against some misuses of `#[must_use]`
1221     fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1222         let node = self.tcx.hir().get(hir_id);
1223         if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
1224             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1225                 lint.build(
1226                     "`must_use` attribute on `async` functions \
1227                     applies to the anonymous `Future` returned by the \
1228                     function, not the value within",
1229                 )
1230                 .span_label(
1231                     span,
1232                     "this attribute does nothing, the `Future`s \
1233                     returned by async functions are already `must_use`",
1234                 )
1235                 .emit();
1236             });
1237         }
1238
1239         if !matches!(
1240             target,
1241             Target::Fn
1242                 | Target::Enum
1243                 | Target::Struct
1244                 | Target::Union
1245                 | Target::Method(_)
1246                 | Target::ForeignFn
1247                 // `impl Trait` in return position can trip
1248                 // `unused_must_use` if `Trait` is marked as
1249                 // `#[must_use]`
1250                 | Target::Trait
1251         ) {
1252             let article = match target {
1253                 Target::ExternCrate
1254                 | Target::OpaqueTy
1255                 | Target::Enum
1256                 | Target::Impl
1257                 | Target::Expression
1258                 | Target::Arm
1259                 | Target::AssocConst
1260                 | Target::AssocTy => "an",
1261                 _ => "a",
1262             };
1263
1264             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1265                 lint.build(&format!(
1266                     "`#[must_use]` has no effect when applied to {article} {target}"
1267                 ))
1268                 .emit();
1269             });
1270         }
1271
1272         // For now, its always valid
1273         true
1274     }
1275
1276     /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
1277     fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1278         match target {
1279             Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
1280             _ => {
1281                 self.tcx
1282                     .sess
1283                     .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
1284                         .span_label(span, "is not a struct, enum, or trait")
1285                         .emit();
1286                 false
1287             }
1288         }
1289     }
1290
1291     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
1292     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1293         match target {
1294             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1295             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1296             // `#[cold]` attribute with just a lint, because we previously
1297             // erroneously allowed it and some crates used it accidentally, to to be compatible
1298             // with crates depending on them, we can't throw an error here.
1299             Target::Field | Target::Arm | Target::MacroDef => {
1300                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1301             }
1302             _ => {
1303                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
1304                 // this, so only emit a warning.
1305                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1306                     lint.build("attribute should be applied to a function")
1307                         .warn(
1308                             "this was previously accepted by the compiler but is \
1309                              being phased out; it will become a hard error in \
1310                              a future release!",
1311                         )
1312                         .span_label(span, "not a function")
1313                         .emit();
1314                 });
1315             }
1316         }
1317     }
1318
1319     /// Checks if `#[link]` is applied to an item other than a foreign module.
1320     fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1321         if target == Target::ForeignMod
1322             && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
1323             && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1324             && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
1325         {
1326             return;
1327         }
1328
1329         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1330             let mut diag =
1331                 lint.build("attribute should be applied to an `extern` block with non-Rust ABI");
1332             diag.warn(
1333                 "this was previously accepted by the compiler but is \
1334                  being phased out; it will become a hard error in \
1335                  a future release!",
1336             );
1337             if target != Target::ForeignMod {
1338                 diag.span_label(span, "not an `extern` block");
1339             }
1340             diag.emit();
1341         });
1342     }
1343
1344     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1345     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1346         match target {
1347             Target::ForeignFn | Target::ForeignStatic => {}
1348             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1349             // `#[link_name]` attribute with just a lint, because we previously
1350             // erroneously allowed it and some crates used it accidentally, to to be compatible
1351             // with crates depending on them, we can't throw an error here.
1352             Target::Field | Target::Arm | Target::MacroDef => {
1353                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1354             }
1355             _ => {
1356                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1357                 // used this, so only emit a warning.
1358                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1359                     let mut diag =
1360                         lint.build("attribute should be applied to a foreign function or static");
1361                     diag.warn(
1362                         "this was previously accepted by the compiler but is \
1363                          being phased out; it will become a hard error in \
1364                          a future release!",
1365                     );
1366
1367                     // See issue #47725
1368                     if let Target::ForeignMod = target {
1369                         if let Some(value) = attr.value_str() {
1370                             diag.span_help(
1371                                 attr.span,
1372                                 &format!(r#"try `#[link(name = "{value}")]` instead"#),
1373                             );
1374                         } else {
1375                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
1376                         }
1377                     }
1378
1379                     diag.span_label(span, "not a foreign function or static");
1380                     diag.emit();
1381                 });
1382             }
1383         }
1384     }
1385
1386     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
1387     fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1388         match target {
1389             Target::ExternCrate => true,
1390             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1391             // `#[no_link]` attribute with just a lint, because we previously
1392             // erroneously allowed it and some crates used it accidentally, to to be compatible
1393             // with crates depending on them, we can't throw an error here.
1394             Target::Field | Target::Arm | Target::MacroDef => {
1395                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1396                 true
1397             }
1398             _ => {
1399                 self.tcx
1400                     .sess
1401                     .struct_span_err(
1402                         attr.span,
1403                         "attribute should be applied to an `extern crate` item",
1404                     )
1405                     .span_label(span, "not an `extern crate` item")
1406                     .emit();
1407                 false
1408             }
1409         }
1410     }
1411
1412     fn is_impl_item(&self, hir_id: HirId) -> bool {
1413         matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
1414     }
1415
1416     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
1417     fn check_export_name(
1418         &self,
1419         hir_id: HirId,
1420         attr: &Attribute,
1421         span: Span,
1422         target: Target,
1423     ) -> bool {
1424         match target {
1425             Target::Static | Target::Fn => true,
1426             Target::Method(..) if self.is_impl_item(hir_id) => true,
1427             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1428             // `#[export_name]` attribute with just a lint, because we previously
1429             // erroneously allowed it and some crates used it accidentally, to to be compatible
1430             // with crates depending on them, we can't throw an error here.
1431             Target::Field | Target::Arm | Target::MacroDef => {
1432                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1433                 true
1434             }
1435             _ => {
1436                 self.tcx
1437                     .sess
1438                     .struct_span_err(
1439                         attr.span,
1440                         "attribute should be applied to a free function, impl method or static",
1441                     )
1442                     .span_label(span, "not a free function, impl method or static")
1443                     .emit();
1444                 false
1445             }
1446         }
1447     }
1448
1449     fn check_rustc_layout_scalar_valid_range(
1450         &self,
1451         attr: &Attribute,
1452         span: Span,
1453         target: Target,
1454     ) -> bool {
1455         if target != Target::Struct {
1456             self.tcx
1457                 .sess
1458                 .struct_span_err(attr.span, "attribute should be applied to a struct")
1459                 .span_label(span, "not a struct")
1460                 .emit();
1461             return false;
1462         }
1463
1464         let Some(list) = attr.meta_item_list() else {
1465             return false;
1466         };
1467
1468         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
1469             true
1470         } else {
1471             self.tcx
1472                 .sess
1473                 .struct_span_err(attr.span, "expected exactly one integer literal argument")
1474                 .emit();
1475             false
1476         }
1477     }
1478
1479     /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1480     fn check_rustc_legacy_const_generics(
1481         &self,
1482         attr: &Attribute,
1483         span: Span,
1484         target: Target,
1485         item: Option<ItemLike<'_>>,
1486     ) -> bool {
1487         let is_function = matches!(target, Target::Fn);
1488         if !is_function {
1489             self.tcx
1490                 .sess
1491                 .struct_span_err(attr.span, "attribute should be applied to a function")
1492                 .span_label(span, "not a function")
1493                 .emit();
1494             return false;
1495         }
1496
1497         let Some(list) = attr.meta_item_list() else {
1498             // The attribute form is validated on AST.
1499             return false;
1500         };
1501
1502         let Some(ItemLike::Item(Item {
1503             kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1504             ..
1505         }))  = item else {
1506             bug!("should be a function item");
1507         };
1508
1509         for param in generics.params {
1510             match param.kind {
1511                 hir::GenericParamKind::Const { .. } => {}
1512                 _ => {
1513                     self.tcx
1514                         .sess
1515                         .struct_span_err(
1516                             attr.span,
1517                             "#[rustc_legacy_const_generics] functions must \
1518                              only have const generics",
1519                         )
1520                         .span_label(param.span, "non-const generic parameter")
1521                         .emit();
1522                     return false;
1523                 }
1524             }
1525         }
1526
1527         if list.len() != generics.params.len() {
1528             self.tcx
1529                 .sess
1530                 .struct_span_err(
1531                     attr.span,
1532                     "#[rustc_legacy_const_generics] must have one index for each generic parameter",
1533                 )
1534                 .span_label(generics.span, "generic parameters")
1535                 .emit();
1536             return false;
1537         }
1538
1539         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1540         let mut invalid_args = vec![];
1541         for meta in list {
1542             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1543                 if *val >= arg_count {
1544                     let span = meta.span();
1545                     self.tcx
1546                         .sess
1547                         .struct_span_err(span, "index exceeds number of arguments")
1548                         .span_label(
1549                             span,
1550                             format!(
1551                                 "there {} only {} argument{}",
1552                                 pluralize!("is", arg_count),
1553                                 arg_count,
1554                                 pluralize!(arg_count)
1555                             ),
1556                         )
1557                         .emit();
1558                     return false;
1559                 }
1560             } else {
1561                 invalid_args.push(meta.span());
1562             }
1563         }
1564
1565         if !invalid_args.is_empty() {
1566             self.tcx
1567                 .sess
1568                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1569                 .emit();
1570             false
1571         } else {
1572             true
1573         }
1574     }
1575
1576     fn check_rustc_lint_query_instability(
1577         &self,
1578         attr: &Attribute,
1579         span: Span,
1580         target: Target,
1581     ) -> bool {
1582         let is_function = matches!(target, Target::Fn | Target::Method(..));
1583         if !is_function {
1584             self.tcx
1585                 .sess
1586                 .struct_span_err(attr.span, "attribute should be applied to a function")
1587                 .span_label(span, "not a function")
1588                 .emit();
1589             false
1590         } else {
1591             true
1592         }
1593     }
1594
1595     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1596     /// option is passed to the compiler.
1597     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1598         if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1599             true
1600         } else {
1601             self.tcx
1602                 .sess
1603                 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1604                 .emit();
1605             false
1606         }
1607     }
1608
1609     /// Checks if `#[link_section]` is applied to a function or static.
1610     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1611         match target {
1612             Target::Static | Target::Fn | Target::Method(..) => {}
1613             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1614             // `#[link_section]` attribute with just a lint, because we previously
1615             // erroneously allowed it and some crates used it accidentally, to to be compatible
1616             // with crates depending on them, we can't throw an error here.
1617             Target::Field | Target::Arm | Target::MacroDef => {
1618                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1619             }
1620             _ => {
1621                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1622                 // crates used this, so only emit a warning.
1623                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1624                     lint.build("attribute should be applied to a function or static")
1625                         .warn(
1626                             "this was previously accepted by the compiler but is \
1627                              being phased out; it will become a hard error in \
1628                              a future release!",
1629                         )
1630                         .span_label(span, "not a function or static")
1631                         .emit();
1632                 });
1633             }
1634         }
1635     }
1636
1637     /// Checks if `#[no_mangle]` is applied to a function or static.
1638     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1639         match target {
1640             Target::Static | Target::Fn => {}
1641             Target::Method(..) if self.is_impl_item(hir_id) => {}
1642             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1643             // `#[no_mangle]` attribute with just a lint, because we previously
1644             // erroneously allowed it and some crates used it accidentally, to to be compatible
1645             // with crates depending on them, we can't throw an error here.
1646             Target::Field | Target::Arm | Target::MacroDef => {
1647                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1648             }
1649             // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
1650             // The error should specify that the item that is wrong is specifically a *foreign* fn/static
1651             // otherwise the error seems odd
1652             Target::ForeignFn | Target::ForeignStatic => {
1653                 let foreign_item_kind = match target {
1654                     Target::ForeignFn => "function",
1655                     Target::ForeignStatic => "static",
1656                     _ => unreachable!(),
1657                 };
1658                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1659                     lint.build(&format!(
1660                         "`#[no_mangle]` has no effect on a foreign {foreign_item_kind}"
1661                     ))
1662                     .warn(
1663                         "this was previously accepted by the compiler but is \
1664                             being phased out; it will become a hard error in \
1665                             a future release!",
1666                     )
1667                     .span_label(span, format!("foreign {foreign_item_kind}"))
1668                     .note("symbol names in extern blocks are not mangled")
1669                     .span_suggestion(
1670                         attr.span,
1671                         "remove this attribute",
1672                         String::new(),
1673                         Applicability::MachineApplicable,
1674                     )
1675                     .emit();
1676                 });
1677             }
1678             _ => {
1679                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1680                 // crates used this, so only emit a warning.
1681                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1682                     lint.build(
1683                         "attribute should be applied to a free function, impl method or static",
1684                     )
1685                     .warn(
1686                         "this was previously accepted by the compiler but is \
1687                          being phased out; it will become a hard error in \
1688                          a future release!",
1689                     )
1690                     .span_label(span, "not a free function, impl method or static")
1691                     .emit();
1692                 });
1693             }
1694         }
1695     }
1696
1697     /// Checks if the `#[repr]` attributes on `item` are valid.
1698     fn check_repr(
1699         &self,
1700         attrs: &[Attribute],
1701         span: Span,
1702         target: Target,
1703         item: Option<ItemLike<'_>>,
1704         hir_id: HirId,
1705     ) {
1706         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1707         // ```
1708         // #[repr(foo)]
1709         // #[repr(bar, align(8))]
1710         // ```
1711         let hints: Vec<_> = attrs
1712             .iter()
1713             .filter(|attr| attr.has_name(sym::repr))
1714             .filter_map(|attr| attr.meta_item_list())
1715             .flatten()
1716             .collect();
1717
1718         let mut int_reprs = 0;
1719         let mut is_c = false;
1720         let mut is_simd = false;
1721         let mut is_transparent = false;
1722
1723         for hint in &hints {
1724             if !hint.is_meta_item() {
1725                 struct_span_err!(
1726                     self.tcx.sess,
1727                     hint.span(),
1728                     E0565,
1729                     "meta item in `repr` must be an identifier"
1730                 )
1731                 .emit();
1732                 continue;
1733             }
1734
1735             let (article, allowed_targets) = match hint.name_or_empty() {
1736                 sym::C => {
1737                     is_c = true;
1738                     match target {
1739                         Target::Struct | Target::Union | Target::Enum => continue,
1740                         _ => ("a", "struct, enum, or union"),
1741                     }
1742                 }
1743                 sym::align => {
1744                     if let (Target::Fn, false) = (target, self.tcx.features().fn_align) {
1745                         feature_err(
1746                             &self.tcx.sess.parse_sess,
1747                             sym::fn_align,
1748                             hint.span(),
1749                             "`repr(align)` attributes on functions are unstable",
1750                         )
1751                         .emit();
1752                     }
1753
1754                     match target {
1755                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1756                         _ => ("a", "struct, enum, function, or union"),
1757                     }
1758                 }
1759                 sym::packed => {
1760                     if target != Target::Struct && target != Target::Union {
1761                         ("a", "struct or union")
1762                     } else {
1763                         continue;
1764                     }
1765                 }
1766                 sym::simd => {
1767                     is_simd = true;
1768                     if target != Target::Struct {
1769                         ("a", "struct")
1770                     } else {
1771                         continue;
1772                     }
1773                 }
1774                 sym::transparent => {
1775                     is_transparent = true;
1776                     match target {
1777                         Target::Struct | Target::Union | Target::Enum => continue,
1778                         _ => ("a", "struct, enum, or union"),
1779                     }
1780                 }
1781                 sym::no_niche => {
1782                     if !self.tcx.features().enabled(sym::no_niche) {
1783                         feature_err(
1784                             &self.tcx.sess.parse_sess,
1785                             sym::no_niche,
1786                             hint.span(),
1787                             "the attribute `repr(no_niche)` is currently unstable",
1788                         )
1789                         .emit();
1790                     }
1791                     match target {
1792                         Target::Struct | Target::Enum => continue,
1793                         _ => ("a", "struct or enum"),
1794                     }
1795                 }
1796                 sym::i8
1797                 | sym::u8
1798                 | sym::i16
1799                 | sym::u16
1800                 | sym::i32
1801                 | sym::u32
1802                 | sym::i64
1803                 | sym::u64
1804                 | sym::i128
1805                 | sym::u128
1806                 | sym::isize
1807                 | sym::usize => {
1808                     int_reprs += 1;
1809                     if target != Target::Enum {
1810                         ("an", "enum")
1811                     } else {
1812                         continue;
1813                     }
1814                 }
1815                 _ => {
1816                     struct_span_err!(
1817                         self.tcx.sess,
1818                         hint.span(),
1819                         E0552,
1820                         "unrecognized representation hint"
1821                     )
1822                     .emit();
1823
1824                     continue;
1825                 }
1826             };
1827
1828             struct_span_err!(
1829                 self.tcx.sess,
1830                 hint.span(),
1831                 E0517,
1832                 "{}",
1833                 &format!("attribute should be applied to {article} {allowed_targets}")
1834             )
1835             .span_label(span, &format!("not {article} {allowed_targets}"))
1836             .emit();
1837         }
1838
1839         // Just point at all repr hints if there are any incompatibilities.
1840         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1841         let hint_spans = hints.iter().map(|hint| hint.span());
1842
1843         // Error on repr(transparent, <anything else apart from no_niche>).
1844         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1845         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1846         if is_transparent && non_no_niche_count > 1 {
1847             let hint_spans: Vec<_> = hint_spans.clone().collect();
1848             struct_span_err!(
1849                 self.tcx.sess,
1850                 hint_spans,
1851                 E0692,
1852                 "transparent {} cannot have other repr hints",
1853                 target
1854             )
1855             .emit();
1856         }
1857         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1858         if (int_reprs > 1)
1859             || (is_simd && is_c)
1860             || (int_reprs == 1
1861                 && is_c
1862                 && item.map_or(false, |item| {
1863                     if let ItemLike::Item(item) = item {
1864                         return is_c_like_enum(item);
1865                     }
1866                     return false;
1867                 }))
1868         {
1869             self.tcx.struct_span_lint_hir(
1870                 CONFLICTING_REPR_HINTS,
1871                 hir_id,
1872                 hint_spans.collect::<Vec<Span>>(),
1873                 |lint| {
1874                     lint.build("conflicting representation hints")
1875                         .code(rustc_errors::error_code!(E0566))
1876                         .emit();
1877                 },
1878             );
1879         }
1880     }
1881
1882     fn check_used(&self, attrs: &[Attribute], target: Target) {
1883         let mut used_linker_span = None;
1884         let mut used_compiler_span = None;
1885         for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
1886             if target != Target::Static {
1887                 self.tcx
1888                     .sess
1889                     .span_err(attr.span, "attribute must be applied to a `static` variable");
1890             }
1891             let inner = attr.meta_item_list();
1892             match inner.as_deref() {
1893                 Some([item]) if item.has_name(sym::linker) => {
1894                     if used_linker_span.is_none() {
1895                         used_linker_span = Some(attr.span);
1896                     }
1897                 }
1898                 Some([item]) if item.has_name(sym::compiler) => {
1899                     if used_compiler_span.is_none() {
1900                         used_compiler_span = Some(attr.span);
1901                     }
1902                 }
1903                 Some(_) => {
1904                     // This error case is handled in rustc_typeck::collect.
1905                 }
1906                 None => {
1907                     // Default case (compiler) when arg isn't defined.
1908                     if used_compiler_span.is_none() {
1909                         used_compiler_span = Some(attr.span);
1910                     }
1911                 }
1912             }
1913         }
1914         if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
1915             let spans = vec![linker_span, compiler_span];
1916             self.tcx
1917                 .sess
1918                 .struct_span_err(
1919                     spans,
1920                     "`used(compiler)` and `used(linker)` can't be used together",
1921                 )
1922                 .emit();
1923         }
1924     }
1925
1926     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1927     /// (Allows proc_macro functions)
1928     fn check_allow_internal_unstable(
1929         &self,
1930         hir_id: HirId,
1931         attr: &Attribute,
1932         span: Span,
1933         target: Target,
1934         attrs: &[Attribute],
1935     ) -> bool {
1936         debug!("Checking target: {:?}", target);
1937         match target {
1938             Target::Fn => {
1939                 for attr in attrs {
1940                     if self.tcx.sess.is_proc_macro_attr(attr) {
1941                         debug!("Is proc macro attr");
1942                         return true;
1943                     }
1944                 }
1945                 debug!("Is not proc macro attr");
1946                 false
1947             }
1948             Target::MacroDef => true,
1949             // FIXME(#80564): We permit struct fields and match arms to have an
1950             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1951             // erroneously allowed it and some crates used it accidentally, to to be compatible
1952             // with crates depending on them, we can't throw an error here.
1953             Target::Field | Target::Arm => {
1954                 self.inline_attr_str_error_without_macro_def(
1955                     hir_id,
1956                     attr,
1957                     "allow_internal_unstable",
1958                 );
1959                 true
1960             }
1961             _ => {
1962                 self.tcx
1963                     .sess
1964                     .struct_span_err(attr.span, "attribute should be applied to a macro")
1965                     .span_label(span, "not a macro")
1966                     .emit();
1967                 false
1968             }
1969         }
1970     }
1971
1972     /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
1973     fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
1974         match target {
1975             Target::Mod => {}
1976             _ => {
1977                 self.tcx
1978                     .sess
1979                     .struct_span_err(attr.span, "attribute should be applied to a module")
1980                     .emit();
1981                 return false;
1982             }
1983         }
1984
1985         let hints = match attr.meta_item_list() {
1986             Some(meta_item_list) => meta_item_list,
1987             None => {
1988                 self.emit_debugger_visualizer_err(attr);
1989                 return false;
1990             }
1991         };
1992
1993         let hint = match hints.len() {
1994             1 => &hints[0],
1995             _ => {
1996                 self.emit_debugger_visualizer_err(attr);
1997                 return false;
1998             }
1999         };
2000
2001         if !hint.has_name(sym::natvis_file) {
2002             self.emit_debugger_visualizer_err(attr);
2003             return false;
2004         }
2005
2006         let meta_item = match hint.meta_item() {
2007             Some(meta_item) => meta_item,
2008             None => {
2009                 self.emit_debugger_visualizer_err(attr);
2010                 return false;
2011             }
2012         };
2013
2014         match (meta_item.name_or_empty(), meta_item.value_str()) {
2015             (sym::natvis_file, Some(_)) => true,
2016             (_, _) => {
2017                 self.emit_debugger_visualizer_err(attr);
2018                 false
2019             }
2020         }
2021     }
2022
2023     fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
2024         self.tcx
2025             .sess
2026             .struct_span_err(attr.span, "invalid argument")
2027             .note(r#"expected: `natvis_file = "..."`"#)
2028             .emit();
2029     }
2030
2031     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
2032     /// (Allows proc_macro functions)
2033     fn check_rustc_allow_const_fn_unstable(
2034         &self,
2035         hir_id: HirId,
2036         attr: &Attribute,
2037         span: Span,
2038         target: Target,
2039     ) -> bool {
2040         match target {
2041             Target::Fn | Target::Method(_)
2042                 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id).to_def_id()) =>
2043             {
2044                 true
2045             }
2046             // FIXME(#80564): We permit struct fields and match arms to have an
2047             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
2048             // erroneously allowed it and some crates used it accidentally, to to be compatible
2049             // with crates depending on them, we can't throw an error here.
2050             Target::Field | Target::Arm | Target::MacroDef => {
2051                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
2052                 true
2053             }
2054             _ => {
2055                 self.tcx
2056                     .sess
2057                     .struct_span_err(attr.span, "attribute should be applied to `const fn`")
2058                     .span_label(span, "not a `const fn`")
2059                     .emit();
2060                 false
2061             }
2062         }
2063     }
2064
2065     fn check_rustc_std_internal_symbol(
2066         &self,
2067         attr: &Attribute,
2068         span: Span,
2069         target: Target,
2070     ) -> bool {
2071         match target {
2072             Target::Fn | Target::Static => true,
2073             _ => {
2074                 self.tcx
2075                     .sess
2076                     .struct_span_err(attr.span, "attribute should be applied functions or statics")
2077                     .span_label(span, "not a function or static")
2078                     .emit();
2079                 false
2080             }
2081         }
2082     }
2083
2084     /// default_method_body_is_const should only be applied to trait methods with default bodies.
2085     fn check_default_method_body_is_const(
2086         &self,
2087         attr: &Attribute,
2088         span: Span,
2089         target: Target,
2090     ) -> bool {
2091         match target {
2092             Target::Method(MethodKind::Trait { body: true }) => true,
2093             _ => {
2094                 self.tcx
2095                     .sess
2096                     .struct_span_err(
2097                         attr.span,
2098                         "attribute should be applied to a trait method with body",
2099                     )
2100                     .span_label(span, "not a trait method or missing a body")
2101                     .emit();
2102                 false
2103             }
2104         }
2105     }
2106
2107     fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
2108         match target {
2109             Target::Expression => {
2110                 self.tcx
2111                     .sess
2112                     .struct_span_err(attr.span, "attribute cannot be applied to an expression")
2113                     .emit();
2114                 false
2115             }
2116             _ => true,
2117         }
2118     }
2119
2120     fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
2121         match target {
2122             Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
2123                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
2124                     lint.build("attribute is ignored here").emit();
2125                 });
2126             }
2127             _ => {}
2128         }
2129     }
2130
2131     fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2132         let name = attr.name_or_empty();
2133         match target {
2134             Target::ExternCrate | Target::Mod => {}
2135             _ => {
2136                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
2137                     lint.build(&format!(
2138                         "`#[{name}]` only has an effect on `extern crate` and modules"
2139                     ))
2140                     .emit();
2141                 });
2142             }
2143         }
2144     }
2145
2146     fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2147         if target != Target::MacroDef {
2148             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
2149                 lint.build("`#[macro_export]` only has an effect on macro definitions").emit();
2150             });
2151         }
2152     }
2153
2154     fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
2155         if target != Target::Fn {
2156             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
2157                 lint.build("`#[plugin_registrar]` only has an effect on functions").emit();
2158             });
2159         }
2160     }
2161
2162     fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
2163         // Warn on useless empty attributes.
2164         let note = if matches!(
2165             attr.name_or_empty(),
2166             sym::macro_use
2167                 | sym::allow
2168                 | sym::expect
2169                 | sym::warn
2170                 | sym::deny
2171                 | sym::forbid
2172                 | sym::feature
2173                 | sym::repr
2174                 | sym::target_feature
2175         ) && attr.meta_item_list().map_or(false, |list| list.is_empty())
2176         {
2177             format!(
2178                 "attribute `{}` with an empty list has no effect",
2179                 attr.name_or_empty()
2180             )
2181         } else if matches!(
2182                 attr.name_or_empty(),
2183                 sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
2184             ) && let Some(meta) = attr.meta_item_list()
2185             && meta.len() == 1
2186             && let Some(item) = meta[0].meta_item()
2187             && let MetaItemKind::NameValue(_) = &item.kind
2188             && item.path == sym::reason
2189         {
2190             format!(
2191                 "attribute `{}` without any lints has no effect",
2192                 attr.name_or_empty()
2193             )
2194         } else {
2195             return;
2196         };
2197
2198         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
2199             lint.build("unused attribute")
2200                 .span_suggestion(
2201                     attr.span,
2202                     "remove this attribute",
2203                     String::new(),
2204                     Applicability::MachineApplicable,
2205                 )
2206                 .note(&note)
2207                 .emit();
2208         });
2209     }
2210 }
2211
2212 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2213     type NestedFilter = nested_filter::OnlyBodies;
2214
2215     fn nested_visit_map(&mut self) -> Self::Map {
2216         self.tcx.hir()
2217     }
2218
2219     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2220         // Historically we've run more checks on non-exported than exported macros,
2221         // so this lets us continue to run them while maintaining backwards compatibility.
2222         // In the long run, the checks should be harmonized.
2223         if let ItemKind::Macro(ref macro_def, _) = item.kind {
2224             let def_id = item.def_id.to_def_id();
2225             if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
2226                 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2227             }
2228         }
2229
2230         let target = Target::from_item(item);
2231         self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2232         intravisit::walk_item(self, item)
2233     }
2234
2235     fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2236         let target = Target::from_generic_param(generic_param);
2237         self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2238         intravisit::walk_generic_param(self, generic_param)
2239     }
2240
2241     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2242         let target = Target::from_trait_item(trait_item);
2243         self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2244         intravisit::walk_trait_item(self, trait_item)
2245     }
2246
2247     fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2248         self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2249         intravisit::walk_field_def(self, struct_field);
2250     }
2251
2252     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2253         self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2254         intravisit::walk_arm(self, arm);
2255     }
2256
2257     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2258         let target = Target::from_foreign_item(f_item);
2259         self.check_attributes(
2260             f_item.hir_id(),
2261             f_item.span,
2262             target,
2263             Some(ItemLike::ForeignItem(f_item)),
2264         );
2265         intravisit::walk_foreign_item(self, f_item)
2266     }
2267
2268     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2269         let target = target_from_impl_item(self.tcx, impl_item);
2270         self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2271         intravisit::walk_impl_item(self, impl_item)
2272     }
2273
2274     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2275         // When checking statements ignore expressions, they will be checked later.
2276         if let hir::StmtKind::Local(ref l) = stmt.kind {
2277             self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2278         }
2279         intravisit::walk_stmt(self, stmt)
2280     }
2281
2282     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2283         let target = match expr.kind {
2284             hir::ExprKind::Closure(..) => Target::Closure,
2285             _ => Target::Expression,
2286         };
2287
2288         self.check_attributes(expr.hir_id, expr.span, target, None);
2289         intravisit::walk_expr(self, expr)
2290     }
2291
2292     fn visit_variant(
2293         &mut self,
2294         variant: &'tcx hir::Variant<'tcx>,
2295         generics: &'tcx hir::Generics<'tcx>,
2296         item_id: HirId,
2297     ) {
2298         self.check_attributes(variant.id, variant.span, Target::Variant, None);
2299         intravisit::walk_variant(self, variant, generics, item_id)
2300     }
2301
2302     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2303         self.check_attributes(param.hir_id, param.span, Target::Param, None);
2304
2305         intravisit::walk_param(self, param);
2306     }
2307 }
2308
2309 fn is_c_like_enum(item: &Item<'_>) -> bool {
2310     if let ItemKind::Enum(ref def, _) = item.kind {
2311         for variant in def.variants {
2312             match variant.data {
2313                 hir::VariantData::Unit(..) => { /* continue */ }
2314                 _ => return false,
2315             }
2316         }
2317         true
2318     } else {
2319         false
2320     }
2321 }
2322
2323 // FIXME: Fix "Cannot determine resolution" error and remove built-in macros
2324 // from this check.
2325 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2326     // Check for builtin attributes at the crate level
2327     // which were unsuccessfully resolved due to cannot determine
2328     // resolution for the attribute macro error.
2329     const ATTRS_TO_CHECK: &[Symbol] = &[
2330         sym::macro_export,
2331         sym::repr,
2332         sym::path,
2333         sym::automatically_derived,
2334         sym::start,
2335         sym::rustc_main,
2336         sym::derive,
2337         sym::test,
2338         sym::test_case,
2339         sym::global_allocator,
2340         sym::bench,
2341     ];
2342
2343     for attr in attrs {
2344         // This function should only be called with crate attributes
2345         // which are inner attributes always but lets check to make sure
2346         if attr.style == AttrStyle::Inner {
2347             for attr_to_check in ATTRS_TO_CHECK {
2348                 if attr.has_name(*attr_to_check) {
2349                     let mut err = tcx.sess.struct_span_err(
2350                         attr.span,
2351                         &format!(
2352                             "`{}` attribute cannot be used at crate level",
2353                             attr_to_check.to_ident_string()
2354                         ),
2355                     );
2356                     // Only emit an error with a suggestion if we can create a
2357                     // string out of the attribute span
2358                     if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
2359                         let replacement = src.replace("#!", "#");
2360                         err.span_suggestion_verbose(
2361                             attr.span,
2362                             "perhaps you meant to use an outer attribute",
2363                             replacement,
2364                             rustc_errors::Applicability::MachineApplicable,
2365                         );
2366                     }
2367                     err.emit();
2368                 }
2369             }
2370         }
2371     }
2372 }
2373
2374 fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2375     let attrs = tcx.hir().attrs(item.hir_id());
2376
2377     for attr in attrs {
2378         if attr.has_name(sym::inline) {
2379             struct_span_err!(
2380                 tcx.sess,
2381                 attr.span,
2382                 E0518,
2383                 "attribute should be applied to function or closure",
2384             )
2385             .span_label(attr.span, "not a function or closure")
2386             .emit();
2387         }
2388     }
2389 }
2390
2391 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
2392     let check_attr_visitor = &mut CheckAttrVisitor { tcx };
2393     tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor);
2394     if module_def_id.is_top_level_module() {
2395         check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2396         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
2397     }
2398 }
2399
2400 pub(crate) fn provide(providers: &mut Providers) {
2401     *providers = Providers { check_mod_attrs, ..*providers };
2402 }
2403
2404 fn check_duplicates(
2405     tcx: TyCtxt<'_>,
2406     attr: &Attribute,
2407     hir_id: HirId,
2408     duplicates: AttributeDuplicates,
2409     seen: &mut FxHashMap<Symbol, Span>,
2410 ) {
2411     use AttributeDuplicates::*;
2412     if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2413         return;
2414     }
2415     match duplicates {
2416         DuplicatesOk => {}
2417         WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2418             match seen.entry(attr.name_or_empty()) {
2419                 Entry::Occupied(mut entry) => {
2420                     let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2421                         let to_remove = entry.insert(attr.span);
2422                         (to_remove, attr.span)
2423                     } else {
2424                         (attr.span, *entry.get())
2425                     };
2426                     tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, this, |lint| {
2427                         let mut db = lint.build("unused attribute");
2428                         db.span_note(other, "attribute also specified here").span_suggestion(
2429                             this,
2430                             "remove this attribute",
2431                             String::new(),
2432                             Applicability::MachineApplicable,
2433                         );
2434                         if matches!(duplicates, FutureWarnFollowing | FutureWarnPreceding) {
2435                             db.warn(
2436                                 "this was previously accepted by the compiler but is \
2437                                  being phased out; it will become a hard error in \
2438                                  a future release!",
2439                             );
2440                         }
2441                         db.emit();
2442                     });
2443                 }
2444                 Entry::Vacant(entry) => {
2445                     entry.insert(attr.span);
2446                 }
2447             }
2448         }
2449         ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) {
2450             Entry::Occupied(mut entry) => {
2451                 let (this, other) = if matches!(duplicates, ErrorPreceding) {
2452                     let to_remove = entry.insert(attr.span);
2453                     (to_remove, attr.span)
2454                 } else {
2455                     (attr.span, *entry.get())
2456                 };
2457                 tcx.sess
2458                     .struct_span_err(
2459                         this,
2460                         &format!("multiple `{}` attributes", attr.name_or_empty()),
2461                     )
2462                     .span_note(other, "attribute also specified here")
2463                     .span_suggestion(
2464                         this,
2465                         "remove this attribute",
2466                         String::new(),
2467                         Applicability::MachineApplicable,
2468                     )
2469                     .emit();
2470             }
2471             Entry::Vacant(entry) => {
2472                 entry.insert(attr.span);
2473             }
2474         },
2475     }
2476 }