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