]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Rollup merge of #85766 - workingjubilee:file-options, 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, BuiltinAttribute, 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(BuiltinAttribute { type_: 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#inline-and-no_inline 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::cfg_hide
942                         | sym::hidden
943                         | sym::html_favicon_url
944                         | sym::html_logo_url
945                         | sym::html_no_source
946                         | sym::html_playground_url
947                         | sym::html_root_url
948                         | sym::inline
949                         | sym::issue_tracker_base_url
950                         | sym::keyword
951                         | sym::masked
952                         | sym::no_default_passes
953                         | sym::no_inline
954                         | sym::notable_trait
955                         | sym::passes
956                         | sym::plugins => {}
957
958                         sym::test => {
959                             if !self.check_test_attr(&meta, hir_id) {
960                                 is_valid = false;
961                             }
962                         }
963
964                         sym::primitive => {
965                             if !self.tcx.features().doc_primitive {
966                                 self.tcx.struct_span_lint_hir(
967                                     INVALID_DOC_ATTRIBUTES,
968                                     hir_id,
969                                     i_meta.span,
970                                     |lint| {
971                                         let mut diag = lint.build(
972                                             "`doc(primitive)` should never have been stable",
973                                         );
974                                         diag.emit();
975                                     },
976                                 );
977                             }
978                         }
979
980                         _ => {
981                             self.tcx.struct_span_lint_hir(
982                                 INVALID_DOC_ATTRIBUTES,
983                                 hir_id,
984                                 i_meta.span,
985                                 |lint| {
986                                     let mut diag = lint.build(&format!(
987                                         "unknown `doc` attribute `{}`",
988                                         rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
989                                     ));
990                                     if i_meta.has_name(sym::spotlight) {
991                                         diag.note(
992                                             "`doc(spotlight)` was renamed to `doc(notable_trait)`",
993                                         );
994                                         diag.span_suggestion_short(
995                                             i_meta.span,
996                                             "use `notable_trait` instead",
997                                             String::from("notable_trait"),
998                                             Applicability::MachineApplicable,
999                                         );
1000                                         diag.note("`doc(spotlight)` is now a no-op");
1001                                     }
1002                                     if i_meta.has_name(sym::include) {
1003                                         if let Some(value) = i_meta.value_str() {
1004                                             // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
1005                                             let applicability = if list.len() == 1 {
1006                                                 Applicability::MachineApplicable
1007                                             } else {
1008                                                 Applicability::MaybeIncorrect
1009                                             };
1010                                             let inner = if attr.style == AttrStyle::Inner {
1011                                                 "!"
1012                                             } else {
1013                                                 ""
1014                                             };
1015                                             diag.span_suggestion(
1016                                                 attr.meta().unwrap().span,
1017                                                 "use `doc = include_str!` instead",
1018                                                 format!(
1019                                                     "#{}[doc = include_str!(\"{}\")]",
1020                                                     inner, value
1021                                                 ),
1022                                                 applicability,
1023                                             );
1024                                         }
1025                                     }
1026                                     diag.emit();
1027                                 },
1028                             );
1029                             is_valid = false;
1030                         }
1031                     }
1032                 } else {
1033                     self.tcx.struct_span_lint_hir(
1034                         INVALID_DOC_ATTRIBUTES,
1035                         hir_id,
1036                         meta.span(),
1037                         |lint| {
1038                             lint.build(&"invalid `doc` attribute").emit();
1039                         },
1040                     );
1041                     is_valid = false;
1042                 }
1043             }
1044         }
1045
1046         is_valid
1047     }
1048
1049     /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
1050     fn check_must_not_suspend(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
1051         match target {
1052             Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
1053             _ => {
1054                 self.tcx
1055                     .sess
1056                     .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
1057                         .span_label(*span, "is not a struct, enum, or trait")
1058                         .emit();
1059                 false
1060             }
1061         }
1062     }
1063
1064     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
1065     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1066         match target {
1067             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1068             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1069             // `#[cold]` attribute with just a lint, because we previously
1070             // erroneously allowed it and some crates used it accidentally, to to be compatible
1071             // with crates depending on them, we can't throw an error here.
1072             Target::Field | Target::Arm | Target::MacroDef => {
1073                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1074             }
1075             _ => {
1076                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
1077                 // this, so only emit a warning.
1078                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1079                     lint.build("attribute should be applied to a function")
1080                         .warn(
1081                             "this was previously accepted by the compiler but is \
1082                              being phased out; it will become a hard error in \
1083                              a future release!",
1084                         )
1085                         .span_label(*span, "not a function")
1086                         .emit();
1087                 });
1088             }
1089         }
1090     }
1091
1092     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1093     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1094         match target {
1095             Target::ForeignFn | Target::ForeignStatic => {}
1096             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1097             // `#[link_name]` attribute with just a lint, because we previously
1098             // erroneously allowed it and some crates used it accidentally, to to be compatible
1099             // with crates depending on them, we can't throw an error here.
1100             Target::Field | Target::Arm | Target::MacroDef => {
1101                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1102             }
1103             _ => {
1104                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1105                 // used this, so only emit a warning.
1106                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1107                     let mut diag =
1108                         lint.build("attribute should be applied to a foreign function or static");
1109                     diag.warn(
1110                         "this was previously accepted by the compiler but is \
1111                          being phased out; it will become a hard error in \
1112                          a future release!",
1113                     );
1114
1115                     // See issue #47725
1116                     if let Target::ForeignMod = target {
1117                         if let Some(value) = attr.value_str() {
1118                             diag.span_help(
1119                                 attr.span,
1120                                 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
1121                             );
1122                         } else {
1123                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
1124                         }
1125                     }
1126
1127                     diag.span_label(*span, "not a foreign function or static");
1128                     diag.emit();
1129                 });
1130             }
1131         }
1132     }
1133
1134     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
1135     fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
1136         match target {
1137             Target::ExternCrate => true,
1138             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1139             // `#[no_link]` attribute with just a lint, because we previously
1140             // erroneously allowed it and some crates used it accidentally, to to be compatible
1141             // with crates depending on them, we can't throw an error here.
1142             Target::Field | Target::Arm | Target::MacroDef => {
1143                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1144                 true
1145             }
1146             _ => {
1147                 self.tcx
1148                     .sess
1149                     .struct_span_err(
1150                         attr.span,
1151                         "attribute should be applied to an `extern crate` item",
1152                     )
1153                     .span_label(*span, "not an `extern crate` item")
1154                     .emit();
1155                 false
1156             }
1157         }
1158     }
1159
1160     fn is_impl_item(&self, hir_id: HirId) -> bool {
1161         matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
1162     }
1163
1164     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
1165     fn check_export_name(
1166         &self,
1167         hir_id: HirId,
1168         attr: &Attribute,
1169         span: &Span,
1170         target: Target,
1171     ) -> bool {
1172         match target {
1173             Target::Static | Target::Fn => true,
1174             Target::Method(..) if self.is_impl_item(hir_id) => true,
1175             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1176             // `#[export_name]` attribute with just a lint, because we previously
1177             // erroneously allowed it and some crates used it accidentally, to to be compatible
1178             // with crates depending on them, we can't throw an error here.
1179             Target::Field | Target::Arm | Target::MacroDef => {
1180                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1181                 true
1182             }
1183             _ => {
1184                 self.tcx
1185                     .sess
1186                     .struct_span_err(
1187                         attr.span,
1188                         "attribute should be applied to a free function, impl method or static",
1189                     )
1190                     .span_label(*span, "not a free function, impl method or static")
1191                     .emit();
1192                 false
1193             }
1194         }
1195     }
1196
1197     fn check_rustc_layout_scalar_valid_range(
1198         &self,
1199         attr: &Attribute,
1200         span: &Span,
1201         target: Target,
1202     ) -> bool {
1203         if target != Target::Struct {
1204             self.tcx
1205                 .sess
1206                 .struct_span_err(attr.span, "attribute should be applied to a struct")
1207                 .span_label(*span, "not a struct")
1208                 .emit();
1209             return false;
1210         }
1211
1212         let list = match attr.meta_item_list() {
1213             None => return false,
1214             Some(it) => it,
1215         };
1216
1217         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
1218             true
1219         } else {
1220             self.tcx
1221                 .sess
1222                 .struct_span_err(attr.span, "expected exactly one integer literal argument")
1223                 .emit();
1224             false
1225         }
1226     }
1227
1228     /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1229     fn check_rustc_legacy_const_generics(
1230         &self,
1231         attr: &Attribute,
1232         span: &Span,
1233         target: Target,
1234         item: Option<ItemLike<'_>>,
1235     ) -> bool {
1236         let is_function = matches!(target, Target::Fn | Target::Method(..));
1237         if !is_function {
1238             self.tcx
1239                 .sess
1240                 .struct_span_err(attr.span, "attribute should be applied to a function")
1241                 .span_label(*span, "not a function")
1242                 .emit();
1243             return false;
1244         }
1245
1246         let list = match attr.meta_item_list() {
1247             // The attribute form is validated on AST.
1248             None => return false,
1249             Some(it) => it,
1250         };
1251
1252         let (decl, generics) = match item {
1253             Some(ItemLike::Item(Item {
1254                 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1255                 ..
1256             })) => (decl, generics),
1257             _ => bug!("should be a function item"),
1258         };
1259
1260         for param in generics.params {
1261             match param.kind {
1262                 hir::GenericParamKind::Const { .. } => {}
1263                 _ => {
1264                     self.tcx
1265                         .sess
1266                         .struct_span_err(
1267                             attr.span,
1268                             "#[rustc_legacy_const_generics] functions must \
1269                              only have const generics",
1270                         )
1271                         .span_label(param.span, "non-const generic parameter")
1272                         .emit();
1273                     return false;
1274                 }
1275             }
1276         }
1277
1278         if list.len() != generics.params.len() {
1279             self.tcx
1280                 .sess
1281                 .struct_span_err(
1282                     attr.span,
1283                     "#[rustc_legacy_const_generics] must have one index for each generic parameter",
1284                 )
1285                 .span_label(generics.span, "generic parameters")
1286                 .emit();
1287             return false;
1288         }
1289
1290         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1291         let mut invalid_args = vec![];
1292         for meta in list {
1293             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1294                 if *val >= arg_count {
1295                     let span = meta.span();
1296                     self.tcx
1297                         .sess
1298                         .struct_span_err(span, "index exceeds number of arguments")
1299                         .span_label(
1300                             span,
1301                             format!(
1302                                 "there {} only {} argument{}",
1303                                 if arg_count != 1 { "are" } else { "is" },
1304                                 arg_count,
1305                                 pluralize!(arg_count)
1306                             ),
1307                         )
1308                         .emit();
1309                     return false;
1310                 }
1311             } else {
1312                 invalid_args.push(meta.span());
1313             }
1314         }
1315
1316         if !invalid_args.is_empty() {
1317             self.tcx
1318                 .sess
1319                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1320                 .emit();
1321             false
1322         } else {
1323             true
1324         }
1325     }
1326
1327     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1328     /// option is passed to the compiler.
1329     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1330         if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1331             true
1332         } else {
1333             self.tcx
1334                 .sess
1335                 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1336                 .emit();
1337             false
1338         }
1339     }
1340
1341     /// Checks if `#[link_section]` is applied to a function or static.
1342     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1343         match target {
1344             Target::Static | Target::Fn | Target::Method(..) => {}
1345             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1346             // `#[link_section]` attribute with just a lint, because we previously
1347             // erroneously allowed it and some crates used it accidentally, to to be compatible
1348             // with crates depending on them, we can't throw an error here.
1349             Target::Field | Target::Arm | Target::MacroDef => {
1350                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1351             }
1352             _ => {
1353                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1354                 // crates used this, so only emit a warning.
1355                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1356                     lint.build("attribute should be applied to a function or static")
1357                         .warn(
1358                             "this was previously accepted by the compiler but is \
1359                              being phased out; it will become a hard error in \
1360                              a future release!",
1361                         )
1362                         .span_label(*span, "not a function or static")
1363                         .emit();
1364                 });
1365             }
1366         }
1367     }
1368
1369     /// Checks if `#[no_mangle]` is applied to a function or static.
1370     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1371         match target {
1372             Target::Static | Target::Fn => {}
1373             Target::Method(..) if self.is_impl_item(hir_id) => {}
1374             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1375             // `#[no_mangle]` attribute with just a lint, because we previously
1376             // erroneously allowed it and some crates used it accidentally, to to be compatible
1377             // with crates depending on them, we can't throw an error here.
1378             Target::Field | Target::Arm | Target::MacroDef => {
1379                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1380             }
1381             // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
1382             // The error should specify that the item that is wrong is specifically a *foreign* fn/static
1383             // otherwise the error seems odd
1384             Target::ForeignFn | Target::ForeignStatic => {
1385                 let foreign_item_kind = match target {
1386                     Target::ForeignFn => "function",
1387                     Target::ForeignStatic => "static",
1388                     _ => unreachable!(),
1389                 };
1390                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1391                     lint.build(&format!(
1392                         "`#[no_mangle]` has no effect on a foreign {}",
1393                         foreign_item_kind
1394                     ))
1395                     .warn(
1396                         "this was previously accepted by the compiler but is \
1397                             being phased out; it will become a hard error in \
1398                             a future release!",
1399                     )
1400                     .span_label(*span, format!("foreign {}", foreign_item_kind))
1401                     .note("symbol names in extern blocks are not mangled")
1402                     .span_suggestion(
1403                         attr.span,
1404                         "remove this attribute",
1405                         String::new(),
1406                         Applicability::MachineApplicable,
1407                     )
1408                     .emit();
1409                 });
1410             }
1411             _ => {
1412                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1413                 // crates used this, so only emit a warning.
1414                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1415                     lint.build(
1416                         "attribute should be applied to a free function, impl method or static",
1417                     )
1418                     .warn(
1419                         "this was previously accepted by the compiler but is \
1420                          being phased out; it will become a hard error in \
1421                          a future release!",
1422                     )
1423                     .span_label(*span, "not a free function, impl method or static")
1424                     .emit();
1425                 });
1426             }
1427         }
1428     }
1429
1430     /// Checks if the `#[repr]` attributes on `item` are valid.
1431     fn check_repr(
1432         &self,
1433         attrs: &'hir [Attribute],
1434         span: &Span,
1435         target: Target,
1436         item: Option<ItemLike<'_>>,
1437         hir_id: HirId,
1438     ) {
1439         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1440         // ```
1441         // #[repr(foo)]
1442         // #[repr(bar, align(8))]
1443         // ```
1444         let hints: Vec<_> = attrs
1445             .iter()
1446             .filter(|attr| attr.has_name(sym::repr))
1447             .filter_map(|attr| attr.meta_item_list())
1448             .flatten()
1449             .collect();
1450
1451         let mut int_reprs = 0;
1452         let mut is_c = false;
1453         let mut is_simd = false;
1454         let mut is_transparent = false;
1455
1456         for hint in &hints {
1457             if !hint.is_meta_item() {
1458                 struct_span_err!(
1459                     self.tcx.sess,
1460                     hint.span(),
1461                     E0565,
1462                     "meta item in `repr` must be an identifier"
1463                 )
1464                 .emit();
1465                 continue;
1466             }
1467
1468             let (article, allowed_targets) = match hint.name_or_empty() {
1469                 sym::C => {
1470                     is_c = true;
1471                     match target {
1472                         Target::Struct | Target::Union | Target::Enum => continue,
1473                         _ => ("a", "struct, enum, or union"),
1474                     }
1475                 }
1476                 sym::align => {
1477                     if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) {
1478                         feature_err(
1479                             &self.tcx.sess.parse_sess,
1480                             sym::fn_align,
1481                             hint.span(),
1482                             "`repr(align)` attributes on functions are unstable",
1483                         )
1484                         .emit();
1485                     }
1486
1487                     match target {
1488                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1489                         _ => ("a", "struct, enum, function, or union"),
1490                     }
1491                 }
1492                 sym::packed => {
1493                     if target != Target::Struct && target != Target::Union {
1494                         ("a", "struct or union")
1495                     } else {
1496                         continue;
1497                     }
1498                 }
1499                 sym::simd => {
1500                     is_simd = true;
1501                     if target != Target::Struct {
1502                         ("a", "struct")
1503                     } else {
1504                         continue;
1505                     }
1506                 }
1507                 sym::transparent => {
1508                     is_transparent = true;
1509                     match target {
1510                         Target::Struct | Target::Union | Target::Enum => continue,
1511                         _ => ("a", "struct, enum, or union"),
1512                     }
1513                 }
1514                 sym::no_niche => {
1515                     if !self.tcx.features().enabled(sym::no_niche) {
1516                         feature_err(
1517                             &self.tcx.sess.parse_sess,
1518                             sym::no_niche,
1519                             hint.span(),
1520                             "the attribute `repr(no_niche)` is currently unstable",
1521                         )
1522                         .emit();
1523                     }
1524                     match target {
1525                         Target::Struct | Target::Enum => continue,
1526                         _ => ("a", "struct or enum"),
1527                     }
1528                 }
1529                 sym::i8
1530                 | sym::u8
1531                 | sym::i16
1532                 | sym::u16
1533                 | sym::i32
1534                 | sym::u32
1535                 | sym::i64
1536                 | sym::u64
1537                 | sym::i128
1538                 | sym::u128
1539                 | sym::isize
1540                 | sym::usize => {
1541                     int_reprs += 1;
1542                     if target != Target::Enum {
1543                         ("an", "enum")
1544                     } else {
1545                         continue;
1546                     }
1547                 }
1548                 _ => {
1549                     struct_span_err!(
1550                         self.tcx.sess,
1551                         hint.span(),
1552                         E0552,
1553                         "unrecognized representation hint"
1554                     )
1555                     .emit();
1556
1557                     continue;
1558                 }
1559             };
1560
1561             struct_span_err!(
1562                 self.tcx.sess,
1563                 hint.span(),
1564                 E0517,
1565                 "{}",
1566                 &format!("attribute should be applied to {} {}", article, allowed_targets)
1567             )
1568             .span_label(*span, &format!("not {} {}", article, allowed_targets))
1569             .emit();
1570         }
1571
1572         // Just point at all repr hints if there are any incompatibilities.
1573         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1574         let hint_spans = hints.iter().map(|hint| hint.span());
1575
1576         // Error on repr(transparent, <anything else apart from no_niche>).
1577         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1578         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1579         if is_transparent && non_no_niche_count > 1 {
1580             let hint_spans: Vec<_> = hint_spans.clone().collect();
1581             struct_span_err!(
1582                 self.tcx.sess,
1583                 hint_spans,
1584                 E0692,
1585                 "transparent {} cannot have other repr hints",
1586                 target
1587             )
1588             .emit();
1589         }
1590         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1591         if (int_reprs > 1)
1592             || (is_simd && is_c)
1593             || (int_reprs == 1
1594                 && is_c
1595                 && item.map_or(false, |item| {
1596                     if let ItemLike::Item(item) = item {
1597                         return is_c_like_enum(item);
1598                     }
1599                     return false;
1600                 }))
1601         {
1602             self.tcx.struct_span_lint_hir(
1603                 CONFLICTING_REPR_HINTS,
1604                 hir_id,
1605                 hint_spans.collect::<Vec<Span>>(),
1606                 |lint| {
1607                     lint.build("conflicting representation hints")
1608                         .code(rustc_errors::error_code!(E0566))
1609                         .emit();
1610                 },
1611             );
1612         }
1613     }
1614
1615     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1616         for attr in attrs {
1617             if attr.has_name(sym::used) && target != Target::Static {
1618                 self.tcx
1619                     .sess
1620                     .span_err(attr.span, "attribute must be applied to a `static` variable");
1621             }
1622         }
1623     }
1624
1625     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1626     /// (Allows proc_macro functions)
1627     fn check_allow_internal_unstable(
1628         &self,
1629         hir_id: HirId,
1630         attr: &Attribute,
1631         span: &Span,
1632         target: Target,
1633         attrs: &[Attribute],
1634     ) -> bool {
1635         debug!("Checking target: {:?}", target);
1636         match target {
1637             Target::Fn => {
1638                 for attr in attrs {
1639                     if self.tcx.sess.is_proc_macro_attr(attr) {
1640                         debug!("Is proc macro attr");
1641                         return true;
1642                     }
1643                 }
1644                 debug!("Is not proc macro attr");
1645                 false
1646             }
1647             Target::MacroDef => true,
1648             // FIXME(#80564): We permit struct fields and match arms to have an
1649             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1650             // erroneously allowed it and some crates used it accidentally, to to be compatible
1651             // with crates depending on them, we can't throw an error here.
1652             Target::Field | Target::Arm => {
1653                 self.inline_attr_str_error_without_macro_def(
1654                     hir_id,
1655                     attr,
1656                     "allow_internal_unstable",
1657                 );
1658                 true
1659             }
1660             _ => {
1661                 self.tcx
1662                     .sess
1663                     .struct_span_err(attr.span, "attribute should be applied to a macro")
1664                     .span_label(*span, "not a macro")
1665                     .emit();
1666                 false
1667             }
1668         }
1669     }
1670
1671     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1672     /// (Allows proc_macro functions)
1673     fn check_rustc_allow_const_fn_unstable(
1674         &self,
1675         hir_id: HirId,
1676         attr: &Attribute,
1677         span: &Span,
1678         target: Target,
1679     ) -> bool {
1680         match target {
1681             Target::Fn | Target::Method(_)
1682                 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1683             {
1684                 true
1685             }
1686             // FIXME(#80564): We permit struct fields and match arms to have an
1687             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1688             // erroneously allowed it and some crates used it accidentally, to to be compatible
1689             // with crates depending on them, we can't throw an error here.
1690             Target::Field | Target::Arm | Target::MacroDef => {
1691                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1692                 true
1693             }
1694             _ => {
1695                 self.tcx
1696                     .sess
1697                     .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1698                     .span_label(*span, "not a `const fn`")
1699                     .emit();
1700                 false
1701             }
1702         }
1703     }
1704
1705     /// default_method_body_is_const should only be applied to trait methods with default bodies.
1706     fn check_default_method_body_is_const(
1707         &self,
1708         attr: &Attribute,
1709         span: &Span,
1710         target: Target,
1711     ) -> bool {
1712         match target {
1713             Target::Method(MethodKind::Trait { body: true }) => true,
1714             _ => {
1715                 self.tcx
1716                     .sess
1717                     .struct_span_err(
1718                         attr.span,
1719                         "attribute should be applied to a trait method with body",
1720                     )
1721                     .span_label(*span, "not a trait method or missing a body")
1722                     .emit();
1723                 false
1724             }
1725         }
1726     }
1727
1728     fn check_stability_promotable(&self, attr: &Attribute, _span: &Span, target: Target) -> bool {
1729         match target {
1730             Target::Expression => {
1731                 self.tcx
1732                     .sess
1733                     .struct_span_err(attr.span, "attribute cannot be applied to an expression")
1734                     .emit();
1735                 false
1736             }
1737             _ => true,
1738         }
1739     }
1740
1741     fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: &Span, target: Target) {
1742         match target {
1743             Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
1744                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1745                     lint.build("attribute is ignored here").emit();
1746                 });
1747             }
1748             _ => {}
1749         }
1750     }
1751
1752     fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1753         let name = attr.name_or_empty();
1754         match target {
1755             Target::ExternCrate | Target::Mod => {}
1756             _ => {
1757                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1758                     lint.build(&format!(
1759                         "`#[{name}]` only has an effect on `extern crate` and modules"
1760                     ))
1761                     .emit();
1762                 });
1763             }
1764         }
1765     }
1766
1767     fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1768         if target != Target::MacroDef {
1769             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1770                 lint.build("`#[macro_export]` only has an effect on macro definitions").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 }