]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Rollup merge of #83829 - petrochenkov:minclean, r=nagisa
[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::{Attribute, Lit, LitKind, NestedMetaItem};
12 use rustc_errors::{pluralize, struct_span_err, Applicability};
13 use rustc_hir as hir;
14 use rustc_hir::def_id::LocalDefId;
15 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
16 use rustc_hir::{
17     self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID,
18 };
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::{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 is_valid = true;
70         let attrs = self.tcx.hir().attrs(hir_id);
71         for attr in attrs {
72             is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
73                 self.check_inline(hir_id, attr, span, target)
74             } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
75                 self.check_non_exhaustive(hir_id, attr, span, target)
76             } else if self.tcx.sess.check_name(attr, sym::marker) {
77                 self.check_marker(hir_id, attr, span, target)
78             } else if self.tcx.sess.check_name(attr, sym::target_feature) {
79                 self.check_target_feature(hir_id, attr, span, target)
80             } else if self.tcx.sess.check_name(attr, sym::track_caller) {
81                 self.check_track_caller(hir_id, &attr.span, attrs, span, target)
82             } else if self.tcx.sess.check_name(attr, sym::doc) {
83                 self.check_doc_attrs(attr, hir_id, target)
84             } else if self.tcx.sess.check_name(attr, sym::no_link) {
85                 self.check_no_link(hir_id, &attr, span, target)
86             } else if self.tcx.sess.check_name(attr, sym::export_name) {
87                 self.check_export_name(hir_id, &attr, span, target)
88             } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
89                 self.check_rustc_args_required_const(&attr, span, target, item)
90             } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_start) {
91                 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
92             } else if self.tcx.sess.check_name(attr, sym::rustc_layout_scalar_valid_range_end) {
93                 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
94             } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) {
95                 self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
96             } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) {
97                 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
98             } else if self.tcx.sess.check_name(attr, sym::naked) {
99                 self.check_naked(hir_id, attr, span, target)
100             } else if self.tcx.sess.check_name(attr, sym::rustc_legacy_const_generics) {
101                 self.check_rustc_legacy_const_generics(&attr, span, target, item)
102             } else if self.tcx.sess.check_name(attr, sym::rustc_clean)
103                 || self.tcx.sess.check_name(attr, sym::rustc_dirty)
104                 || self.tcx.sess.check_name(attr, sym::rustc_if_this_changed)
105                 || self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need)
106             {
107                 self.check_rustc_dirty_clean(&attr)
108             } else {
109                 // lint-only checks
110                 if self.tcx.sess.check_name(attr, sym::cold) {
111                     self.check_cold(hir_id, attr, span, target);
112                 } else if self.tcx.sess.check_name(attr, sym::link_name) {
113                     self.check_link_name(hir_id, attr, span, target);
114                 } else if self.tcx.sess.check_name(attr, sym::link_section) {
115                     self.check_link_section(hir_id, attr, span, target);
116                 } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
117                     self.check_no_mangle(hir_id, attr, span, target);
118                 }
119                 true
120             };
121         }
122
123         if !is_valid {
124             return;
125         }
126
127         if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
128             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
129         }
130
131         self.check_repr(attrs, span, target, item, hir_id);
132         self.check_used(attrs, target);
133     }
134
135     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
136         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
137             lint.build(&format!(
138                 "`#[{}]` is ignored on struct fields, match arms and macro defs",
139                 sym,
140             ))
141             .warn(
142                 "this was previously accepted by the compiler but is \
143                  being phased out; it will become a hard error in \
144                  a future release!",
145             )
146             .note(
147                 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
148                  for more information",
149             )
150             .emit();
151         });
152     }
153
154     fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
155         self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
156             lint.build(&format!("`#[{}]` is ignored on struct fields and match arms", sym))
157                 .warn(
158                     "this was previously accepted by the compiler but is \
159                  being phased out; it will become a hard error in \
160                  a future release!",
161                 )
162                 .note(
163                     "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
164                  for more information",
165                 )
166                 .emit();
167         });
168     }
169
170     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
171     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
172         match target {
173             Target::Fn
174             | Target::Closure
175             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
176             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
177                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
178                     lint.build("`#[inline]` is ignored on function prototypes").emit()
179                 });
180                 true
181             }
182             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
183             // just a lint, because we previously erroneously allowed it and some crates used it
184             // accidentally, to to be compatible with crates depending on them, we can't throw an
185             // error here.
186             Target::AssocConst => {
187                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
188                     lint.build("`#[inline]` is ignored on constants")
189                         .warn(
190                             "this was previously accepted by the compiler but is \
191                              being phased out; it will become a hard error in \
192                              a future release!",
193                         )
194                         .note(
195                             "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
196                              for more information",
197                         )
198                         .emit();
199                 });
200                 true
201             }
202             // FIXME(#80564): Same for fields, arms, and macro defs
203             Target::Field | Target::Arm | Target::MacroDef => {
204                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
205                 true
206             }
207             _ => {
208                 struct_span_err!(
209                     self.tcx.sess,
210                     attr.span,
211                     E0518,
212                     "attribute should be applied to function or closure",
213                 )
214                 .span_label(*span, "not a function or closure")
215                 .emit();
216                 false
217             }
218         }
219     }
220
221     /// Checks if `#[naked]` is applied to a function definition.
222     fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
223         match target {
224             Target::Fn
225             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
226             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
227             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
228             // erroneously allowed it and some crates used it accidentally, to to be compatible
229             // with crates depending on them, we can't throw an error here.
230             Target::Field | Target::Arm | Target::MacroDef => {
231                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
232                 true
233             }
234             _ => {
235                 self.tcx
236                     .sess
237                     .struct_span_err(
238                         attr.span,
239                         "attribute should be applied to a function definition",
240                     )
241                     .span_label(*span, "not a function definition")
242                     .emit();
243                 false
244             }
245         }
246     }
247
248     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
249     fn check_track_caller(
250         &self,
251         hir_id: HirId,
252         attr_span: &Span,
253         attrs: &'hir [Attribute],
254         span: &Span,
255         target: Target,
256     ) -> bool {
257         match target {
258             _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
259                 struct_span_err!(
260                     self.tcx.sess,
261                     *attr_span,
262                     E0736,
263                     "cannot use `#[track_caller]` with `#[naked]`",
264                 )
265                 .emit();
266                 false
267             }
268             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
269             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
270             // `#[track_caller]` attribute with just a lint, because we previously
271             // erroneously allowed it and some crates used it accidentally, to to be compatible
272             // with crates depending on them, we can't throw an error here.
273             Target::Field | Target::Arm | Target::MacroDef => {
274                 for attr in attrs {
275                     self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
276                 }
277                 true
278             }
279             _ => {
280                 struct_span_err!(
281                     self.tcx.sess,
282                     *attr_span,
283                     E0739,
284                     "attribute should be applied to function"
285                 )
286                 .span_label(*span, "not a function")
287                 .emit();
288                 false
289             }
290         }
291     }
292
293     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
294     fn check_non_exhaustive(
295         &self,
296         hir_id: HirId,
297         attr: &Attribute,
298         span: &Span,
299         target: Target,
300     ) -> bool {
301         match target {
302             Target::Struct | Target::Enum | Target::Variant => true,
303             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
304             // `#[non_exhaustive]` attribute with just a lint, because we previously
305             // erroneously allowed it and some crates used it accidentally, to to be compatible
306             // with crates depending on them, we can't throw an error here.
307             Target::Field | Target::Arm | Target::MacroDef => {
308                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
309                 true
310             }
311             _ => {
312                 struct_span_err!(
313                     self.tcx.sess,
314                     attr.span,
315                     E0701,
316                     "attribute can only be applied to a struct or enum"
317                 )
318                 .span_label(*span, "not a struct or enum")
319                 .emit();
320                 false
321             }
322         }
323     }
324
325     /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
326     fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
327         match target {
328             Target::Trait => true,
329             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
330             // `#[marker]` attribute with just a lint, because we previously
331             // erroneously allowed it and some crates used it accidentally, to to be compatible
332             // with crates depending on them, we can't throw an error here.
333             Target::Field | Target::Arm | Target::MacroDef => {
334                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
335                 true
336             }
337             _ => {
338                 self.tcx
339                     .sess
340                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
341                     .span_label(*span, "not a trait")
342                     .emit();
343                 false
344             }
345         }
346     }
347
348     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
349     fn check_target_feature(
350         &self,
351         hir_id: HirId,
352         attr: &Attribute,
353         span: &Span,
354         target: Target,
355     ) -> bool {
356         match target {
357             Target::Fn
358             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
359             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
360             // crates used this, so only emit a warning.
361             Target::Statement => {
362                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
363                     lint.build("attribute should be applied to a function")
364                         .warn(
365                             "this was previously accepted by the compiler but is \
366                              being phased out; it will become a hard error in \
367                              a future release!",
368                         )
369                         .span_label(*span, "not a function")
370                         .emit();
371                 });
372                 true
373             }
374             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
375             // `#[target_feature]` attribute with just a lint, because we previously
376             // erroneously allowed it and some crates used it accidentally, to to be compatible
377             // with crates depending on them, we can't throw an error here.
378             Target::Field | Target::Arm | Target::MacroDef => {
379                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
380                 true
381             }
382             _ => {
383                 self.tcx
384                     .sess
385                     .struct_span_err(attr.span, "attribute should be applied to a function")
386                     .span_label(*span, "not a function")
387                     .emit();
388                 false
389             }
390         }
391     }
392
393     fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
394         self.tcx
395             .sess
396             .struct_span_err(
397                 meta.span(),
398                 &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
399             )
400             .emit();
401     }
402
403     fn check_doc_alias_value(
404         &self,
405         meta: &NestedMetaItem,
406         doc_alias: &str,
407         hir_id: HirId,
408         target: Target,
409         is_list: bool,
410     ) -> bool {
411         let tcx = self.tcx;
412         let err_fn = move |span: Span, msg: &str| {
413             tcx.sess.span_err(
414                 span,
415                 &format!(
416                     "`#[doc(alias{})]` {}",
417                     if is_list { "(\"...\")" } else { " = \"...\"" },
418                     msg,
419                 ),
420             );
421             false
422         };
423         if doc_alias.is_empty() {
424             return err_fn(
425                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
426                 "attribute cannot have empty value",
427             );
428         }
429         if let Some(c) =
430             doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
431         {
432             self.tcx.sess.span_err(
433                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
434                 &format!(
435                     "{:?} character isn't allowed in `#[doc(alias{})]`",
436                     c,
437                     if is_list { "(\"...\")" } else { " = \"...\"" },
438                 ),
439             );
440             return false;
441         }
442         if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
443             return err_fn(
444                 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
445                 "cannot start or end with ' '",
446             );
447         }
448         if let Some(err) = match target {
449             Target::Impl => Some("implementation block"),
450             Target::ForeignMod => Some("extern block"),
451             Target::AssocTy => {
452                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
453                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
454                 if Target::from_item(containing_item) == Target::Impl {
455                     Some("type alias in implementation block")
456                 } else {
457                     None
458                 }
459             }
460             Target::AssocConst => {
461                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
462                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
463                 // We can't link to trait impl's consts.
464                 let err = "associated constant in trait implementation block";
465                 match containing_item.kind {
466                     ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
467                     _ => None,
468                 }
469             }
470             _ => None,
471         } {
472             return err_fn(meta.span(), &format!("isn't allowed on {}", err));
473         }
474         let item_name = self.tcx.hir().name(hir_id);
475         if &*item_name.as_str() == doc_alias {
476             return err_fn(meta.span(), "is the same as the item's name");
477         }
478         true
479     }
480
481     fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
482         if let Some(values) = meta.meta_item_list() {
483             let mut errors = 0;
484             for v in values {
485                 match v.literal() {
486                     Some(l) => match l.kind {
487                         LitKind::Str(s, _) => {
488                             if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
489                                 errors += 1;
490                             }
491                         }
492                         _ => {
493                             self.tcx
494                                 .sess
495                                 .struct_span_err(
496                                     v.span(),
497                                     "`#[doc(alias(\"a\"))]` expects string literals",
498                                 )
499                                 .emit();
500                             errors += 1;
501                         }
502                     },
503                     None => {
504                         self.tcx
505                             .sess
506                             .struct_span_err(
507                                 v.span(),
508                                 "`#[doc(alias(\"a\"))]` expects string literals",
509                             )
510                             .emit();
511                         errors += 1;
512                     }
513                 }
514             }
515             errors == 0
516         } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
517             self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
518         } else {
519             self.tcx
520                 .sess
521                 .struct_span_err(
522                     meta.span(),
523                     "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
524                      strings `#[doc(alias(\"a\", \"b\"))]`",
525                 )
526                 .emit();
527             false
528         }
529     }
530
531     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
532         let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
533         if doc_keyword.is_empty() {
534             self.doc_attr_str_error(meta, "keyword");
535             return false;
536         }
537         match self.tcx.hir().expect_item(hir_id).kind {
538             ItemKind::Mod(ref module) => {
539                 if !module.item_ids.is_empty() {
540                     self.tcx
541                         .sess
542                         .struct_span_err(
543                             meta.span(),
544                             "`#[doc(keyword = \"...\")]` can only be used on empty modules",
545                         )
546                         .emit();
547                     return false;
548                 }
549             }
550             _ => {
551                 self.tcx
552                     .sess
553                     .struct_span_err(
554                         meta.span(),
555                         "`#[doc(keyword = \"...\")]` can only be used on modules",
556                     )
557                     .emit();
558                 return false;
559             }
560         }
561         if !rustc_lexer::is_ident(&doc_keyword) {
562             self.tcx
563                 .sess
564                 .struct_span_err(
565                     meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
566                     &format!("`{}` is not a valid identifier", doc_keyword),
567                 )
568                 .emit();
569             return false;
570         }
571         true
572     }
573
574     fn check_attr_crate_level(
575         &self,
576         meta: &NestedMetaItem,
577         hir_id: HirId,
578         attr_name: &str,
579     ) -> bool {
580         if CRATE_HIR_ID == hir_id {
581             self.tcx
582                 .sess
583                 .struct_span_err(
584                     meta.span(),
585                     &format!(
586                         "`#![doc({} = \"...\")]` isn't allowed as a crate-level attribute",
587                         attr_name,
588                     ),
589                 )
590                 .emit();
591             return false;
592         }
593         true
594     }
595
596     fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
597         let mut is_valid = true;
598
599         if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
600             for meta in list {
601                 if let Some(i_meta) = meta.meta_item() {
602                     match i_meta.name_or_empty() {
603                         sym::alias
604                             if !self.check_attr_crate_level(&meta, hir_id, "alias")
605                                 || !self.check_doc_alias(&meta, hir_id, target) =>
606                         {
607                             is_valid = false
608                         }
609
610                         sym::keyword
611                             if !self.check_attr_crate_level(&meta, hir_id, "keyword")
612                                 || !self.check_doc_keyword(&meta, hir_id) =>
613                         {
614                             is_valid = false
615                         }
616
617                         sym::test if CRATE_HIR_ID != hir_id => {
618                             self.tcx.struct_span_lint_hir(
619                                 INVALID_DOC_ATTRIBUTES,
620                                 hir_id,
621                                 meta.span(),
622                                 |lint| {
623                                     lint.build(
624                                         "`#![doc(test(...)]` is only allowed \
625                                          as a crate-level attribute",
626                                     )
627                                     .emit();
628                                 },
629                             );
630                             is_valid = false;
631                         }
632
633                         // no_default_passes: deprecated
634                         // passes: deprecated
635                         // plugins: removed, but rustdoc warns about it itself
636                         sym::alias
637                         | sym::cfg
638                         | sym::hidden
639                         | sym::html_favicon_url
640                         | sym::html_logo_url
641                         | sym::html_no_source
642                         | sym::html_playground_url
643                         | sym::html_root_url
644                         | sym::include
645                         | sym::inline
646                         | sym::issue_tracker_base_url
647                         | sym::keyword
648                         | sym::masked
649                         | sym::no_default_passes
650                         | sym::no_inline
651                         | sym::notable_trait
652                         | sym::passes
653                         | sym::plugins
654                         | sym::primitive
655                         | sym::test => {}
656
657                         _ => {
658                             self.tcx.struct_span_lint_hir(
659                                 INVALID_DOC_ATTRIBUTES,
660                                 hir_id,
661                                 i_meta.span,
662                                 |lint| {
663                                     let mut diag = lint.build(&format!(
664                                         "unknown `doc` attribute `{}`",
665                                         rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
666                                     ));
667                                     if i_meta.has_name(sym::spotlight) {
668                                         diag.note(
669                                             "`doc(spotlight)` was renamed to `doc(notable_trait)`",
670                                         );
671                                         diag.span_suggestion_short(
672                                             i_meta.span,
673                                             "use `notable_trait` instead",
674                                             String::from("notable_trait"),
675                                             Applicability::MachineApplicable,
676                                         );
677                                         diag.note("`doc(spotlight)` is now a no-op");
678                                     }
679                                     diag.emit();
680                                 },
681                             );
682                             is_valid = false;
683                         }
684                     }
685                 } else {
686                     self.tcx.struct_span_lint_hir(
687                         INVALID_DOC_ATTRIBUTES,
688                         hir_id,
689                         meta.span(),
690                         |lint| {
691                             lint.build(&format!("invalid `doc` attribute")).emit();
692                         },
693                     );
694                     is_valid = false;
695                 }
696             }
697         }
698
699         is_valid
700     }
701
702     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
703     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
704         match target {
705             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
706             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
707             // `#[cold]` attribute with just a lint, because we previously
708             // erroneously allowed it and some crates used it accidentally, to to be compatible
709             // with crates depending on them, we can't throw an error here.
710             Target::Field | Target::Arm | Target::MacroDef => {
711                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
712             }
713             _ => {
714                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
715                 // this, so only emit a warning.
716                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
717                     lint.build("attribute should be applied to a function")
718                         .warn(
719                             "this was previously accepted by the compiler but is \
720                              being phased out; it will become a hard error in \
721                              a future release!",
722                         )
723                         .span_label(*span, "not a function")
724                         .emit();
725                 });
726             }
727         }
728     }
729
730     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
731     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
732         match target {
733             Target::ForeignFn | Target::ForeignStatic => {}
734             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
735             // `#[link_name]` attribute with just a lint, because we previously
736             // erroneously allowed it and some crates used it accidentally, to to be compatible
737             // with crates depending on them, we can't throw an error here.
738             Target::Field | Target::Arm | Target::MacroDef => {
739                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
740             }
741             _ => {
742                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
743                 // used this, so only emit a warning.
744                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
745                     let mut diag =
746                         lint.build("attribute should be applied to a foreign function or static");
747                     diag.warn(
748                         "this was previously accepted by the compiler but is \
749                          being phased out; it will become a hard error in \
750                          a future release!",
751                     );
752
753                     // See issue #47725
754                     if let Target::ForeignMod = target {
755                         if let Some(value) = attr.value_str() {
756                             diag.span_help(
757                                 attr.span,
758                                 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
759                             );
760                         } else {
761                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
762                         }
763                     }
764
765                     diag.span_label(*span, "not a foreign function or static");
766                     diag.emit();
767                 });
768             }
769         }
770     }
771
772     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
773     fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
774         match target {
775             Target::ExternCrate => true,
776             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
777             // `#[no_link]` attribute with just a lint, because we previously
778             // erroneously allowed it and some crates used it accidentally, to to be compatible
779             // with crates depending on them, we can't throw an error here.
780             Target::Field | Target::Arm | Target::MacroDef => {
781                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
782                 true
783             }
784             _ => {
785                 self.tcx
786                     .sess
787                     .struct_span_err(
788                         attr.span,
789                         "attribute should be applied to an `extern crate` item",
790                     )
791                     .span_label(*span, "not an `extern crate` item")
792                     .emit();
793                 false
794             }
795         }
796     }
797
798     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
799     fn check_export_name(
800         &self,
801         hir_id: HirId,
802         attr: &Attribute,
803         span: &Span,
804         target: Target,
805     ) -> bool {
806         match target {
807             Target::Static | Target::Fn | Target::Method(..) => true,
808             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
809             // `#[export_name]` attribute with just a lint, because we previously
810             // erroneously allowed it and some crates used it accidentally, to to be compatible
811             // with crates depending on them, we can't throw an error here.
812             Target::Field | Target::Arm | Target::MacroDef => {
813                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
814                 true
815             }
816             _ => {
817                 self.tcx
818                     .sess
819                     .struct_span_err(
820                         attr.span,
821                         "attribute should be applied to a function or static",
822                     )
823                     .span_label(*span, "not a function or static")
824                     .emit();
825                 false
826             }
827         }
828     }
829
830     /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
831     fn check_rustc_args_required_const(
832         &self,
833         attr: &Attribute,
834         span: &Span,
835         target: Target,
836         item: Option<ItemLike<'_>>,
837     ) -> bool {
838         let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
839         if !is_function {
840             self.tcx
841                 .sess
842                 .struct_span_err(attr.span, "attribute should be applied to a function")
843                 .span_label(*span, "not a function")
844                 .emit();
845             return false;
846         }
847
848         let list = match attr.meta_item_list() {
849             // The attribute form is validated on AST.
850             None => return false,
851             Some(it) => it,
852         };
853
854         let mut invalid_args = vec![];
855         for meta in list {
856             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
857                 if let Some(ItemLike::Item(Item {
858                     kind: ItemKind::Fn(FnSig { decl, .. }, ..),
859                     ..
860                 }))
861                 | Some(ItemLike::ForeignItem(ForeignItem {
862                     kind: ForeignItemKind::Fn(decl, ..),
863                     ..
864                 })) = item
865                 {
866                     let arg_count = decl.inputs.len() as u128;
867                     if *val >= arg_count {
868                         let span = meta.span();
869                         self.tcx
870                             .sess
871                             .struct_span_err(span, "index exceeds number of arguments")
872                             .span_label(
873                                 span,
874                                 format!(
875                                     "there {} only {} argument{}",
876                                     if arg_count != 1 { "are" } else { "is" },
877                                     arg_count,
878                                     pluralize!(arg_count)
879                                 ),
880                             )
881                             .emit();
882                         return false;
883                     }
884                 } else {
885                     bug!("should be a function item");
886                 }
887             } else {
888                 invalid_args.push(meta.span());
889             }
890         }
891
892         if !invalid_args.is_empty() {
893             self.tcx
894                 .sess
895                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
896                 .emit();
897             false
898         } else {
899             true
900         }
901     }
902
903     fn check_rustc_layout_scalar_valid_range(
904         &self,
905         attr: &Attribute,
906         span: &Span,
907         target: Target,
908     ) -> bool {
909         if target != Target::Struct {
910             self.tcx
911                 .sess
912                 .struct_span_err(attr.span, "attribute should be applied to a struct")
913                 .span_label(*span, "not a struct")
914                 .emit();
915             return false;
916         }
917
918         let list = match attr.meta_item_list() {
919             None => return false,
920             Some(it) => it,
921         };
922
923         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
924             true
925         } else {
926             self.tcx
927                 .sess
928                 .struct_span_err(attr.span, "expected exactly one integer literal argument")
929                 .emit();
930             false
931         }
932     }
933
934     /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
935     fn check_rustc_legacy_const_generics(
936         &self,
937         attr: &Attribute,
938         span: &Span,
939         target: Target,
940         item: Option<ItemLike<'_>>,
941     ) -> bool {
942         let is_function = matches!(target, Target::Fn | Target::Method(..));
943         if !is_function {
944             self.tcx
945                 .sess
946                 .struct_span_err(attr.span, "attribute should be applied to a function")
947                 .span_label(*span, "not a function")
948                 .emit();
949             return false;
950         }
951
952         let list = match attr.meta_item_list() {
953             // The attribute form is validated on AST.
954             None => return false,
955             Some(it) => it,
956         };
957
958         let (decl, generics) = match item {
959             Some(ItemLike::Item(Item {
960                 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
961                 ..
962             })) => (decl, generics),
963             _ => bug!("should be a function item"),
964         };
965
966         for param in generics.params {
967             match param.kind {
968                 hir::GenericParamKind::Const { .. } => {}
969                 _ => {
970                     self.tcx
971                         .sess
972                         .struct_span_err(
973                             attr.span,
974                             "#[rustc_legacy_const_generics] functions must \
975                              only have const generics",
976                         )
977                         .span_label(param.span, "non-const generic parameter")
978                         .emit();
979                     return false;
980                 }
981             }
982         }
983
984         if list.len() != generics.params.len() {
985             self.tcx
986                 .sess
987                 .struct_span_err(
988                     attr.span,
989                     "#[rustc_legacy_const_generics] must have one index for each generic parameter",
990                 )
991                 .span_label(generics.span, "generic parameters")
992                 .emit();
993             return false;
994         }
995
996         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
997         let mut invalid_args = vec![];
998         for meta in list {
999             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1000                 if *val >= arg_count {
1001                     let span = meta.span();
1002                     self.tcx
1003                         .sess
1004                         .struct_span_err(span, "index exceeds number of arguments")
1005                         .span_label(
1006                             span,
1007                             format!(
1008                                 "there {} only {} argument{}",
1009                                 if arg_count != 1 { "are" } else { "is" },
1010                                 arg_count,
1011                                 pluralize!(arg_count)
1012                             ),
1013                         )
1014                         .emit();
1015                     return false;
1016                 }
1017             } else {
1018                 invalid_args.push(meta.span());
1019             }
1020         }
1021
1022         if !invalid_args.is_empty() {
1023             self.tcx
1024                 .sess
1025                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1026                 .emit();
1027             false
1028         } else {
1029             true
1030         }
1031     }
1032
1033     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1034     /// option is passed to the compiler.
1035     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1036         if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1037             true
1038         } else {
1039             self.tcx
1040                 .sess
1041                 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1042                 .emit();
1043             false
1044         }
1045     }
1046
1047     /// Checks if `#[link_section]` is applied to a function or static.
1048     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1049         match target {
1050             Target::Static | Target::Fn | Target::Method(..) => {}
1051             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1052             // `#[link_section]` attribute with just a lint, because we previously
1053             // erroneously allowed it and some crates used it accidentally, to to be compatible
1054             // with crates depending on them, we can't throw an error here.
1055             Target::Field | Target::Arm | Target::MacroDef => {
1056                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1057             }
1058             _ => {
1059                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1060                 // crates used this, so only emit a warning.
1061                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1062                     lint.build("attribute should be applied to a function or static")
1063                         .warn(
1064                             "this was previously accepted by the compiler but is \
1065                              being phased out; it will become a hard error in \
1066                              a future release!",
1067                         )
1068                         .span_label(*span, "not a function or static")
1069                         .emit();
1070                 });
1071             }
1072         }
1073     }
1074
1075     /// Checks if `#[no_mangle]` is applied to a function or static.
1076     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1077         match target {
1078             Target::Static | Target::Fn | Target::Method(..) => {}
1079             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1080             // `#[no_mangle]` attribute with just a lint, because we previously
1081             // erroneously allowed it and some crates used it accidentally, to to be compatible
1082             // with crates depending on them, we can't throw an error here.
1083             Target::Field | Target::Arm | Target::MacroDef => {
1084                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1085             }
1086             _ => {
1087                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1088                 // crates used this, so only emit a warning.
1089                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1090                     lint.build("attribute should be applied to a function or static")
1091                         .warn(
1092                             "this was previously accepted by the compiler but is \
1093                              being phased out; it will become a hard error in \
1094                              a future release!",
1095                         )
1096                         .span_label(*span, "not a function or static")
1097                         .emit();
1098                 });
1099             }
1100         }
1101     }
1102
1103     /// Checks if the `#[repr]` attributes on `item` are valid.
1104     fn check_repr(
1105         &self,
1106         attrs: &'hir [Attribute],
1107         span: &Span,
1108         target: Target,
1109         item: Option<ItemLike<'_>>,
1110         hir_id: HirId,
1111     ) {
1112         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1113         // ```
1114         // #[repr(foo)]
1115         // #[repr(bar, align(8))]
1116         // ```
1117         let hints: Vec<_> = attrs
1118             .iter()
1119             .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
1120             .filter_map(|attr| attr.meta_item_list())
1121             .flatten()
1122             .collect();
1123
1124         let mut int_reprs = 0;
1125         let mut is_c = false;
1126         let mut is_simd = false;
1127         let mut is_transparent = false;
1128
1129         for hint in &hints {
1130             if !hint.is_meta_item() {
1131                 struct_span_err!(
1132                     self.tcx.sess,
1133                     hint.span(),
1134                     E0565,
1135                     "meta item in `repr` must be an identifier"
1136                 )
1137                 .emit();
1138                 continue;
1139             }
1140
1141             let (article, allowed_targets) = match hint.name_or_empty() {
1142                 sym::C => {
1143                     is_c = true;
1144                     match target {
1145                         Target::Struct | Target::Union | Target::Enum => continue,
1146                         _ => ("a", "struct, enum, or union"),
1147                     }
1148                 }
1149                 sym::align => {
1150                     if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) {
1151                         feature_err(
1152                             &self.tcx.sess.parse_sess,
1153                             sym::fn_align,
1154                             hint.span(),
1155                             "`repr(align)` attributes on functions are unstable",
1156                         )
1157                         .emit();
1158                     }
1159
1160                     match target {
1161                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1162                         _ => ("a", "struct, enum, function, or union"),
1163                     }
1164                 }
1165                 sym::packed => {
1166                     if target != Target::Struct && target != Target::Union {
1167                         ("a", "struct or union")
1168                     } else {
1169                         continue;
1170                     }
1171                 }
1172                 sym::simd => {
1173                     is_simd = true;
1174                     if target != Target::Struct {
1175                         ("a", "struct")
1176                     } else {
1177                         continue;
1178                     }
1179                 }
1180                 sym::transparent => {
1181                     is_transparent = true;
1182                     match target {
1183                         Target::Struct | Target::Union | Target::Enum => continue,
1184                         _ => ("a", "struct, enum, or union"),
1185                     }
1186                 }
1187                 sym::no_niche => {
1188                     if !self.tcx.features().enabled(sym::no_niche) {
1189                         feature_err(
1190                             &self.tcx.sess.parse_sess,
1191                             sym::no_niche,
1192                             hint.span(),
1193                             "the attribute `repr(no_niche)` is currently unstable",
1194                         )
1195                         .emit();
1196                     }
1197                     match target {
1198                         Target::Struct | Target::Enum => continue,
1199                         _ => ("a", "struct or enum"),
1200                     }
1201                 }
1202                 sym::i8
1203                 | sym::u8
1204                 | sym::i16
1205                 | sym::u16
1206                 | sym::i32
1207                 | sym::u32
1208                 | sym::i64
1209                 | sym::u64
1210                 | sym::i128
1211                 | sym::u128
1212                 | sym::isize
1213                 | sym::usize => {
1214                     int_reprs += 1;
1215                     if target != Target::Enum {
1216                         ("an", "enum")
1217                     } else {
1218                         continue;
1219                     }
1220                 }
1221                 _ => {
1222                     struct_span_err!(
1223                         self.tcx.sess,
1224                         hint.span(),
1225                         E0552,
1226                         "unrecognized representation hint"
1227                     )
1228                     .emit();
1229
1230                     continue;
1231                 }
1232             };
1233
1234             struct_span_err!(
1235                 self.tcx.sess,
1236                 hint.span(),
1237                 E0517,
1238                 "{}",
1239                 &format!("attribute should be applied to {} {}", article, allowed_targets)
1240             )
1241             .span_label(*span, &format!("not {} {}", article, allowed_targets))
1242             .emit();
1243         }
1244
1245         // Just point at all repr hints if there are any incompatibilities.
1246         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1247         let hint_spans = hints.iter().map(|hint| hint.span());
1248
1249         // Error on repr(transparent, <anything else apart from no_niche>).
1250         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1251         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1252         if is_transparent && non_no_niche_count > 1 {
1253             let hint_spans: Vec<_> = hint_spans.clone().collect();
1254             struct_span_err!(
1255                 self.tcx.sess,
1256                 hint_spans,
1257                 E0692,
1258                 "transparent {} cannot have other repr hints",
1259                 target
1260             )
1261             .emit();
1262         }
1263         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1264         if (int_reprs > 1)
1265             || (is_simd && is_c)
1266             || (int_reprs == 1
1267                 && is_c
1268                 && item.map_or(false, |item| {
1269                     if let ItemLike::Item(item) = item {
1270                         return is_c_like_enum(item);
1271                     }
1272                     return false;
1273                 }))
1274         {
1275             self.tcx.struct_span_lint_hir(
1276                 CONFLICTING_REPR_HINTS,
1277                 hir_id,
1278                 hint_spans.collect::<Vec<Span>>(),
1279                 |lint| {
1280                     lint.build("conflicting representation hints")
1281                         .code(rustc_errors::error_code!(E0566))
1282                         .emit();
1283                 },
1284             );
1285         }
1286     }
1287
1288     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1289         for attr in attrs {
1290             if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
1291                 self.tcx
1292                     .sess
1293                     .span_err(attr.span, "attribute must be applied to a `static` variable");
1294             }
1295         }
1296     }
1297
1298     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1299     /// (Allows proc_macro functions)
1300     fn check_allow_internal_unstable(
1301         &self,
1302         hir_id: HirId,
1303         attr: &Attribute,
1304         span: &Span,
1305         target: Target,
1306         attrs: &[Attribute],
1307     ) -> bool {
1308         debug!("Checking target: {:?}", target);
1309         match target {
1310             Target::Fn => {
1311                 for attr in attrs {
1312                     if self.tcx.sess.is_proc_macro_attr(attr) {
1313                         debug!("Is proc macro attr");
1314                         return true;
1315                     }
1316                 }
1317                 debug!("Is not proc macro attr");
1318                 false
1319             }
1320             Target::MacroDef => true,
1321             // FIXME(#80564): We permit struct fields and match arms to have an
1322             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1323             // erroneously allowed it and some crates used it accidentally, to to be compatible
1324             // with crates depending on them, we can't throw an error here.
1325             Target::Field | Target::Arm => {
1326                 self.inline_attr_str_error_without_macro_def(
1327                     hir_id,
1328                     attr,
1329                     "allow_internal_unstable",
1330                 );
1331                 true
1332             }
1333             _ => {
1334                 self.tcx
1335                     .sess
1336                     .struct_span_err(attr.span, "attribute should be applied to a macro")
1337                     .span_label(*span, "not a macro")
1338                     .emit();
1339                 false
1340             }
1341         }
1342     }
1343
1344     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1345     /// (Allows proc_macro functions)
1346     fn check_rustc_allow_const_fn_unstable(
1347         &self,
1348         hir_id: HirId,
1349         attr: &Attribute,
1350         span: &Span,
1351         target: Target,
1352     ) -> bool {
1353         match target {
1354             Target::Fn | Target::Method(_)
1355                 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1356             {
1357                 true
1358             }
1359             // FIXME(#80564): We permit struct fields and match arms to have an
1360             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1361             // erroneously allowed it and some crates used it accidentally, to to be compatible
1362             // with crates depending on them, we can't throw an error here.
1363             Target::Field | Target::Arm | Target::MacroDef => {
1364                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1365                 true
1366             }
1367             _ => {
1368                 self.tcx
1369                     .sess
1370                     .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1371                     .span_label(*span, "not a `const fn`")
1372                     .emit();
1373                 false
1374             }
1375         }
1376     }
1377 }
1378
1379 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
1380     type Map = Map<'tcx>;
1381
1382     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1383         NestedVisitorMap::OnlyBodies(self.tcx.hir())
1384     }
1385
1386     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
1387         let target = Target::from_item(item);
1388         self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
1389         intravisit::walk_item(self, item)
1390     }
1391
1392     fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1393         let target = Target::from_generic_param(generic_param);
1394         self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
1395         intravisit::walk_generic_param(self, generic_param)
1396     }
1397
1398     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
1399         let target = Target::from_trait_item(trait_item);
1400         self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
1401         intravisit::walk_trait_item(self, trait_item)
1402     }
1403
1404     fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1405         self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1406         intravisit::walk_field_def(self, struct_field);
1407     }
1408
1409     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
1410         self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
1411         intravisit::walk_arm(self, arm);
1412     }
1413
1414     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
1415         let target = Target::from_foreign_item(f_item);
1416         self.check_attributes(
1417             f_item.hir_id(),
1418             &f_item.span,
1419             target,
1420             Some(ItemLike::ForeignItem(f_item)),
1421         );
1422         intravisit::walk_foreign_item(self, f_item)
1423     }
1424
1425     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
1426         let target = target_from_impl_item(self.tcx, impl_item);
1427         self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
1428         intravisit::walk_impl_item(self, impl_item)
1429     }
1430
1431     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
1432         // When checking statements ignore expressions, they will be checked later.
1433         if let hir::StmtKind::Local(ref l) = stmt.kind {
1434             self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
1435         }
1436         intravisit::walk_stmt(self, stmt)
1437     }
1438
1439     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
1440         let target = match expr.kind {
1441             hir::ExprKind::Closure(..) => Target::Closure,
1442             _ => Target::Expression,
1443         };
1444
1445         self.check_attributes(expr.hir_id, &expr.span, target, None);
1446         intravisit::walk_expr(self, expr)
1447     }
1448
1449     fn visit_variant(
1450         &mut self,
1451         variant: &'tcx hir::Variant<'tcx>,
1452         generics: &'tcx hir::Generics<'tcx>,
1453         item_id: HirId,
1454     ) {
1455         self.check_attributes(variant.id, &variant.span, Target::Variant, None);
1456         intravisit::walk_variant(self, variant, generics, item_id)
1457     }
1458
1459     fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1460         self.check_attributes(macro_def.hir_id(), &macro_def.span, Target::MacroDef, None);
1461         intravisit::walk_macro_def(self, macro_def);
1462     }
1463
1464     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1465         self.check_attributes(param.hir_id, &param.span, Target::Param, None);
1466
1467         intravisit::walk_param(self, param);
1468     }
1469 }
1470
1471 fn is_c_like_enum(item: &Item<'_>) -> bool {
1472     if let ItemKind::Enum(ref def, _) = item.kind {
1473         for variant in def.variants {
1474             match variant.data {
1475                 hir::VariantData::Unit(..) => { /* continue */ }
1476                 _ => return false,
1477             }
1478         }
1479         true
1480     } else {
1481         false
1482     }
1483 }
1484
1485 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1486     const ATTRS_TO_CHECK: &[Symbol] = &[
1487         sym::macro_export,
1488         sym::repr,
1489         sym::path,
1490         sym::automatically_derived,
1491         sym::start,
1492         sym::main,
1493     ];
1494
1495     for attr in attrs {
1496         for attr_to_check in ATTRS_TO_CHECK {
1497             if tcx.sess.check_name(attr, *attr_to_check) {
1498                 tcx.sess
1499                     .struct_span_err(
1500                         attr.span,
1501                         &format!(
1502                             "`{}` attribute cannot be used at crate level",
1503                             attr_to_check.to_ident_string()
1504                         ),
1505                     )
1506                     .emit();
1507             }
1508         }
1509     }
1510 }
1511
1512 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1513     for attr in attrs {
1514         if tcx.sess.check_name(attr, sym::inline) {
1515             struct_span_err!(
1516                 tcx.sess,
1517                 attr.span,
1518                 E0518,
1519                 "attribute should be applied to function or closure",
1520             )
1521             .span_label(attr.span, "not a function or closure")
1522             .emit();
1523         }
1524     }
1525 }
1526
1527 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1528     let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1529     tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1530     tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1531     check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
1532     if module_def_id.is_top_level_module() {
1533         check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
1534         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1535     }
1536 }
1537
1538 pub(crate) fn provide(providers: &mut Providers) {
1539     *providers = Providers { check_mod_attrs, ..*providers };
1540 }