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