]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Rollup merge of #83015 - hyd-dev:test-79825-81555, r=Aaron1011
[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};
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::passes
652                         | sym::plugins
653                         | sym::primitive
654                         | sym::spotlight
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 msg = format!(
664                                         "unknown `doc` attribute `{}`",
665                                         rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
666                                     );
667                                     lint.build(&msg).emit();
668                                 },
669                             );
670                             is_valid = false;
671                         }
672                     }
673                 } else {
674                     self.tcx.struct_span_lint_hir(
675                         INVALID_DOC_ATTRIBUTES,
676                         hir_id,
677                         meta.span(),
678                         |lint| {
679                             lint.build(&format!("invalid `doc` attribute")).emit();
680                         },
681                     );
682                     is_valid = false;
683                 }
684             }
685         }
686
687         is_valid
688     }
689
690     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
691     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
692         match target {
693             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
694             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
695             // `#[cold]` attribute with just a lint, because we previously
696             // erroneously allowed it and some crates used it accidentally, to to be compatible
697             // with crates depending on them, we can't throw an error here.
698             Target::Field | Target::Arm | Target::MacroDef => {
699                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
700             }
701             _ => {
702                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
703                 // this, so only emit a warning.
704                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
705                     lint.build("attribute should be applied to a function")
706                         .warn(
707                             "this was previously accepted by the compiler but is \
708                              being phased out; it will become a hard error in \
709                              a future release!",
710                         )
711                         .span_label(*span, "not a function")
712                         .emit();
713                 });
714             }
715         }
716     }
717
718     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
719     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
720         match target {
721             Target::ForeignFn | Target::ForeignStatic => {}
722             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
723             // `#[link_name]` attribute with just a lint, because we previously
724             // erroneously allowed it and some crates used it accidentally, to to be compatible
725             // with crates depending on them, we can't throw an error here.
726             Target::Field | Target::Arm | Target::MacroDef => {
727                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
728             }
729             _ => {
730                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
731                 // used this, so only emit a warning.
732                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
733                     let mut diag =
734                         lint.build("attribute should be applied to a foreign function or static");
735                     diag.warn(
736                         "this was previously accepted by the compiler but is \
737                          being phased out; it will become a hard error in \
738                          a future release!",
739                     );
740
741                     // See issue #47725
742                     if let Target::ForeignMod = target {
743                         if let Some(value) = attr.value_str() {
744                             diag.span_help(
745                                 attr.span,
746                                 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
747                             );
748                         } else {
749                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
750                         }
751                     }
752
753                     diag.span_label(*span, "not a foreign function or static");
754                     diag.emit();
755                 });
756             }
757         }
758     }
759
760     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
761     fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
762         match target {
763             Target::ExternCrate => true,
764             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
765             // `#[no_link]` attribute with just a lint, because we previously
766             // erroneously allowed it and some crates used it accidentally, to to be compatible
767             // with crates depending on them, we can't throw an error here.
768             Target::Field | Target::Arm | Target::MacroDef => {
769                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
770                 true
771             }
772             _ => {
773                 self.tcx
774                     .sess
775                     .struct_span_err(
776                         attr.span,
777                         "attribute should be applied to an `extern crate` item",
778                     )
779                     .span_label(*span, "not an `extern crate` item")
780                     .emit();
781                 false
782             }
783         }
784     }
785
786     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
787     fn check_export_name(
788         &self,
789         hir_id: HirId,
790         attr: &Attribute,
791         span: &Span,
792         target: Target,
793     ) -> bool {
794         match target {
795             Target::Static | Target::Fn | Target::Method(..) => true,
796             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
797             // `#[export_name]` attribute with just a lint, because we previously
798             // erroneously allowed it and some crates used it accidentally, to to be compatible
799             // with crates depending on them, we can't throw an error here.
800             Target::Field | Target::Arm | Target::MacroDef => {
801                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
802                 true
803             }
804             _ => {
805                 self.tcx
806                     .sess
807                     .struct_span_err(
808                         attr.span,
809                         "attribute should be applied to a function or static",
810                     )
811                     .span_label(*span, "not a function or static")
812                     .emit();
813                 false
814             }
815         }
816     }
817
818     /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
819     fn check_rustc_args_required_const(
820         &self,
821         attr: &Attribute,
822         span: &Span,
823         target: Target,
824         item: Option<ItemLike<'_>>,
825     ) -> bool {
826         let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
827         if !is_function {
828             self.tcx
829                 .sess
830                 .struct_span_err(attr.span, "attribute should be applied to a function")
831                 .span_label(*span, "not a function")
832                 .emit();
833             return false;
834         }
835
836         let list = match attr.meta_item_list() {
837             // The attribute form is validated on AST.
838             None => return false,
839             Some(it) => it,
840         };
841
842         let mut invalid_args = vec![];
843         for meta in list {
844             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
845                 if let Some(ItemLike::Item(Item {
846                     kind: ItemKind::Fn(FnSig { decl, .. }, ..),
847                     ..
848                 }))
849                 | Some(ItemLike::ForeignItem(ForeignItem {
850                     kind: ForeignItemKind::Fn(decl, ..),
851                     ..
852                 })) = item
853                 {
854                     let arg_count = decl.inputs.len() as u128;
855                     if *val >= arg_count {
856                         let span = meta.span();
857                         self.tcx
858                             .sess
859                             .struct_span_err(span, "index exceeds number of arguments")
860                             .span_label(
861                                 span,
862                                 format!(
863                                     "there {} only {} argument{}",
864                                     if arg_count != 1 { "are" } else { "is" },
865                                     arg_count,
866                                     pluralize!(arg_count)
867                                 ),
868                             )
869                             .emit();
870                         return false;
871                     }
872                 } else {
873                     bug!("should be a function item");
874                 }
875             } else {
876                 invalid_args.push(meta.span());
877             }
878         }
879
880         if !invalid_args.is_empty() {
881             self.tcx
882                 .sess
883                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
884                 .emit();
885             false
886         } else {
887             true
888         }
889     }
890
891     fn check_rustc_layout_scalar_valid_range(
892         &self,
893         attr: &Attribute,
894         span: &Span,
895         target: Target,
896     ) -> bool {
897         if target != Target::Struct {
898             self.tcx
899                 .sess
900                 .struct_span_err(attr.span, "attribute should be applied to a struct")
901                 .span_label(*span, "not a struct")
902                 .emit();
903             return false;
904         }
905
906         let list = match attr.meta_item_list() {
907             None => return false,
908             Some(it) => it,
909         };
910
911         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
912             true
913         } else {
914             self.tcx
915                 .sess
916                 .struct_span_err(attr.span, "expected exactly one integer literal argument")
917                 .emit();
918             false
919         }
920     }
921
922     /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
923     fn check_rustc_legacy_const_generics(
924         &self,
925         attr: &Attribute,
926         span: &Span,
927         target: Target,
928         item: Option<ItemLike<'_>>,
929     ) -> bool {
930         let is_function = matches!(target, Target::Fn | Target::Method(..));
931         if !is_function {
932             self.tcx
933                 .sess
934                 .struct_span_err(attr.span, "attribute should be applied to a function")
935                 .span_label(*span, "not a function")
936                 .emit();
937             return false;
938         }
939
940         let list = match attr.meta_item_list() {
941             // The attribute form is validated on AST.
942             None => return false,
943             Some(it) => it,
944         };
945
946         let (decl, generics) = match item {
947             Some(ItemLike::Item(Item {
948                 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
949                 ..
950             })) => (decl, generics),
951             _ => bug!("should be a function item"),
952         };
953
954         for param in generics.params {
955             match param.kind {
956                 hir::GenericParamKind::Const { .. } => {}
957                 _ => {
958                     self.tcx
959                         .sess
960                         .struct_span_err(
961                             attr.span,
962                             "#[rustc_legacy_const_generics] functions must \
963                              only have const generics",
964                         )
965                         .span_label(param.span, "non-const generic parameter")
966                         .emit();
967                     return false;
968                 }
969             }
970         }
971
972         if list.len() != generics.params.len() {
973             self.tcx
974                 .sess
975                 .struct_span_err(
976                     attr.span,
977                     "#[rustc_legacy_const_generics] must have one index for each generic parameter",
978                 )
979                 .span_label(generics.span, "generic parameters")
980                 .emit();
981             return false;
982         }
983
984         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
985         let mut invalid_args = vec![];
986         for meta in list {
987             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
988                 if *val >= arg_count {
989                     let span = meta.span();
990                     self.tcx
991                         .sess
992                         .struct_span_err(span, "index exceeds number of arguments")
993                         .span_label(
994                             span,
995                             format!(
996                                 "there {} only {} argument{}",
997                                 if arg_count != 1 { "are" } else { "is" },
998                                 arg_count,
999                                 pluralize!(arg_count)
1000                             ),
1001                         )
1002                         .emit();
1003                     return false;
1004                 }
1005             } else {
1006                 invalid_args.push(meta.span());
1007             }
1008         }
1009
1010         if !invalid_args.is_empty() {
1011             self.tcx
1012                 .sess
1013                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1014                 .emit();
1015             false
1016         } else {
1017             true
1018         }
1019     }
1020
1021     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1022     /// option is passed to the compiler.
1023     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1024         if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1025             true
1026         } else {
1027             self.tcx
1028                 .sess
1029                 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1030                 .emit();
1031             false
1032         }
1033     }
1034
1035     /// Checks if `#[link_section]` is applied to a function or static.
1036     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1037         match target {
1038             Target::Static | Target::Fn | Target::Method(..) => {}
1039             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1040             // `#[link_section]` attribute with just a lint, because we previously
1041             // erroneously allowed it and some crates used it accidentally, to to be compatible
1042             // with crates depending on them, we can't throw an error here.
1043             Target::Field | Target::Arm | Target::MacroDef => {
1044                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1045             }
1046             _ => {
1047                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1048                 // crates used this, so only emit a warning.
1049                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1050                     lint.build("attribute should be applied to a function or static")
1051                         .warn(
1052                             "this was previously accepted by the compiler but is \
1053                              being phased out; it will become a hard error in \
1054                              a future release!",
1055                         )
1056                         .span_label(*span, "not a function or static")
1057                         .emit();
1058                 });
1059             }
1060         }
1061     }
1062
1063     /// Checks if `#[no_mangle]` is applied to a function or static.
1064     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1065         match target {
1066             Target::Static | Target::Fn | Target::Method(..) => {}
1067             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1068             // `#[no_mangle]` attribute with just a lint, because we previously
1069             // erroneously allowed it and some crates used it accidentally, to to be compatible
1070             // with crates depending on them, we can't throw an error here.
1071             Target::Field | Target::Arm | Target::MacroDef => {
1072                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1073             }
1074             _ => {
1075                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1076                 // crates used this, so only emit a warning.
1077                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1078                     lint.build("attribute should be applied to a function or static")
1079                         .warn(
1080                             "this was previously accepted by the compiler but is \
1081                              being phased out; it will become a hard error in \
1082                              a future release!",
1083                         )
1084                         .span_label(*span, "not a function or static")
1085                         .emit();
1086                 });
1087             }
1088         }
1089     }
1090
1091     /// Checks if the `#[repr]` attributes on `item` are valid.
1092     fn check_repr(
1093         &self,
1094         attrs: &'hir [Attribute],
1095         span: &Span,
1096         target: Target,
1097         item: Option<ItemLike<'_>>,
1098         hir_id: HirId,
1099     ) {
1100         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1101         // ```
1102         // #[repr(foo)]
1103         // #[repr(bar, align(8))]
1104         // ```
1105         let hints: Vec<_> = attrs
1106             .iter()
1107             .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
1108             .filter_map(|attr| attr.meta_item_list())
1109             .flatten()
1110             .collect();
1111
1112         let mut int_reprs = 0;
1113         let mut is_c = false;
1114         let mut is_simd = false;
1115         let mut is_transparent = false;
1116
1117         for hint in &hints {
1118             let (article, allowed_targets) = match hint.name_or_empty() {
1119                 _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => {
1120                     ("a", "struct, enum, or union")
1121                 }
1122                 name @ sym::C | name @ sym::align => {
1123                     is_c |= name == sym::C;
1124                     match target {
1125                         Target::Struct | Target::Union | Target::Enum => continue,
1126                         _ => ("a", "struct, enum, or union"),
1127                     }
1128                 }
1129                 sym::packed => {
1130                     if target != Target::Struct && target != Target::Union {
1131                         ("a", "struct or union")
1132                     } else {
1133                         continue;
1134                     }
1135                 }
1136                 sym::simd => {
1137                     is_simd = true;
1138                     if target != Target::Struct {
1139                         ("a", "struct")
1140                     } else {
1141                         continue;
1142                     }
1143                 }
1144                 sym::transparent => {
1145                     is_transparent = true;
1146                     match target {
1147                         Target::Struct | Target::Union | Target::Enum => continue,
1148                         _ => ("a", "struct, enum, or union"),
1149                     }
1150                 }
1151                 sym::no_niche => {
1152                     if !self.tcx.features().enabled(sym::no_niche) {
1153                         feature_err(
1154                             &self.tcx.sess.parse_sess,
1155                             sym::no_niche,
1156                             hint.span(),
1157                             "the attribute `repr(no_niche)` is currently unstable",
1158                         )
1159                         .emit();
1160                     }
1161                     match target {
1162                         Target::Struct | Target::Enum => continue,
1163                         _ => ("a", "struct or enum"),
1164                     }
1165                 }
1166                 sym::i8
1167                 | sym::u8
1168                 | sym::i16
1169                 | sym::u16
1170                 | sym::i32
1171                 | sym::u32
1172                 | sym::i64
1173                 | sym::u64
1174                 | sym::i128
1175                 | sym::u128
1176                 | sym::isize
1177                 | sym::usize => {
1178                     int_reprs += 1;
1179                     if target != Target::Enum {
1180                         ("an", "enum")
1181                     } else {
1182                         continue;
1183                     }
1184                 }
1185                 _ => continue,
1186             };
1187
1188             struct_span_err!(
1189                 self.tcx.sess,
1190                 hint.span(),
1191                 E0517,
1192                 "{}",
1193                 &format!("attribute should be applied to {} {}", article, allowed_targets)
1194             )
1195             .span_label(*span, &format!("not {} {}", article, allowed_targets))
1196             .emit();
1197         }
1198
1199         // Just point at all repr hints if there are any incompatibilities.
1200         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1201         let hint_spans = hints.iter().map(|hint| hint.span());
1202
1203         // Error on repr(transparent, <anything else apart from no_niche>).
1204         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1205         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1206         if is_transparent && non_no_niche_count > 1 {
1207             let hint_spans: Vec<_> = hint_spans.clone().collect();
1208             struct_span_err!(
1209                 self.tcx.sess,
1210                 hint_spans,
1211                 E0692,
1212                 "transparent {} cannot have other repr hints",
1213                 target
1214             )
1215             .emit();
1216         }
1217         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1218         if (int_reprs > 1)
1219             || (is_simd && is_c)
1220             || (int_reprs == 1
1221                 && is_c
1222                 && item.map_or(false, |item| {
1223                     if let ItemLike::Item(item) = item {
1224                         return is_c_like_enum(item);
1225                     }
1226                     return false;
1227                 }))
1228         {
1229             self.tcx.struct_span_lint_hir(
1230                 CONFLICTING_REPR_HINTS,
1231                 hir_id,
1232                 hint_spans.collect::<Vec<Span>>(),
1233                 |lint| {
1234                     lint.build("conflicting representation hints")
1235                         .code(rustc_errors::error_code!(E0566))
1236                         .emit();
1237                 },
1238             );
1239         }
1240     }
1241
1242     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1243         for attr in attrs {
1244             if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
1245                 self.tcx
1246                     .sess
1247                     .span_err(attr.span, "attribute must be applied to a `static` variable");
1248             }
1249         }
1250     }
1251
1252     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1253     /// (Allows proc_macro functions)
1254     fn check_allow_internal_unstable(
1255         &self,
1256         hir_id: HirId,
1257         attr: &Attribute,
1258         span: &Span,
1259         target: Target,
1260         attrs: &[Attribute],
1261     ) -> bool {
1262         debug!("Checking target: {:?}", target);
1263         match target {
1264             Target::Fn => {
1265                 for attr in attrs {
1266                     if self.tcx.sess.is_proc_macro_attr(attr) {
1267                         debug!("Is proc macro attr");
1268                         return true;
1269                     }
1270                 }
1271                 debug!("Is not proc macro attr");
1272                 false
1273             }
1274             Target::MacroDef => true,
1275             // FIXME(#80564): We permit struct fields and match arms to have an
1276             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1277             // erroneously allowed it and some crates used it accidentally, to to be compatible
1278             // with crates depending on them, we can't throw an error here.
1279             Target::Field | Target::Arm => {
1280                 self.inline_attr_str_error_without_macro_def(
1281                     hir_id,
1282                     attr,
1283                     "allow_internal_unstable",
1284                 );
1285                 true
1286             }
1287             _ => {
1288                 self.tcx
1289                     .sess
1290                     .struct_span_err(attr.span, "attribute should be applied to a macro")
1291                     .span_label(*span, "not a macro")
1292                     .emit();
1293                 false
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_rustc_allow_const_fn_unstable(
1301         &self,
1302         hir_id: HirId,
1303         attr: &Attribute,
1304         span: &Span,
1305         target: Target,
1306     ) -> bool {
1307         match target {
1308             Target::Fn | Target::Method(_)
1309                 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1310             {
1311                 true
1312             }
1313             // FIXME(#80564): We permit struct fields and match arms to have an
1314             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1315             // erroneously allowed it and some crates used it accidentally, to to be compatible
1316             // with crates depending on them, we can't throw an error here.
1317             Target::Field | Target::Arm | Target::MacroDef => {
1318                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1319                 true
1320             }
1321             _ => {
1322                 self.tcx
1323                     .sess
1324                     .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1325                     .span_label(*span, "not a `const fn`")
1326                     .emit();
1327                 false
1328             }
1329         }
1330     }
1331 }
1332
1333 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
1334     type Map = Map<'tcx>;
1335
1336     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1337         NestedVisitorMap::OnlyBodies(self.tcx.hir())
1338     }
1339
1340     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
1341         let target = Target::from_item(item);
1342         self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
1343         intravisit::walk_item(self, item)
1344     }
1345
1346     fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1347         let target = Target::from_generic_param(generic_param);
1348         self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
1349         intravisit::walk_generic_param(self, generic_param)
1350     }
1351
1352     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
1353         let target = Target::from_trait_item(trait_item);
1354         self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
1355         intravisit::walk_trait_item(self, trait_item)
1356     }
1357
1358     fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1359         self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1360         intravisit::walk_field_def(self, struct_field);
1361     }
1362
1363     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
1364         self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
1365         intravisit::walk_arm(self, arm);
1366     }
1367
1368     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
1369         let target = Target::from_foreign_item(f_item);
1370         self.check_attributes(
1371             f_item.hir_id(),
1372             &f_item.span,
1373             target,
1374             Some(ItemLike::ForeignItem(f_item)),
1375         );
1376         intravisit::walk_foreign_item(self, f_item)
1377     }
1378
1379     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
1380         let target = target_from_impl_item(self.tcx, impl_item);
1381         self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
1382         intravisit::walk_impl_item(self, impl_item)
1383     }
1384
1385     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
1386         // When checking statements ignore expressions, they will be checked later.
1387         if let hir::StmtKind::Local(ref l) = stmt.kind {
1388             self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
1389         }
1390         intravisit::walk_stmt(self, stmt)
1391     }
1392
1393     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
1394         let target = match expr.kind {
1395             hir::ExprKind::Closure(..) => Target::Closure,
1396             _ => Target::Expression,
1397         };
1398
1399         self.check_attributes(expr.hir_id, &expr.span, target, None);
1400         intravisit::walk_expr(self, expr)
1401     }
1402
1403     fn visit_variant(
1404         &mut self,
1405         variant: &'tcx hir::Variant<'tcx>,
1406         generics: &'tcx hir::Generics<'tcx>,
1407         item_id: HirId,
1408     ) {
1409         self.check_attributes(variant.id, &variant.span, Target::Variant, None);
1410         intravisit::walk_variant(self, variant, generics, item_id)
1411     }
1412
1413     fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1414         self.check_attributes(macro_def.hir_id(), &macro_def.span, Target::MacroDef, None);
1415         intravisit::walk_macro_def(self, macro_def);
1416     }
1417
1418     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1419         self.check_attributes(param.hir_id, &param.span, Target::Param, None);
1420
1421         intravisit::walk_param(self, param);
1422     }
1423 }
1424
1425 fn is_c_like_enum(item: &Item<'_>) -> bool {
1426     if let ItemKind::Enum(ref def, _) = item.kind {
1427         for variant in def.variants {
1428             match variant.data {
1429                 hir::VariantData::Unit(..) => { /* continue */ }
1430                 _ => return false,
1431             }
1432         }
1433         true
1434     } else {
1435         false
1436     }
1437 }
1438
1439 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1440     const ATTRS_TO_CHECK: &[Symbol] = &[
1441         sym::macro_export,
1442         sym::repr,
1443         sym::path,
1444         sym::automatically_derived,
1445         sym::start,
1446         sym::main,
1447     ];
1448
1449     for attr in attrs {
1450         for attr_to_check in ATTRS_TO_CHECK {
1451             if tcx.sess.check_name(attr, *attr_to_check) {
1452                 tcx.sess
1453                     .struct_span_err(
1454                         attr.span,
1455                         &format!(
1456                             "`{}` attribute cannot be used at crate level",
1457                             attr_to_check.to_ident_string()
1458                         ),
1459                     )
1460                     .emit();
1461             }
1462         }
1463     }
1464 }
1465
1466 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1467     for attr in attrs {
1468         if tcx.sess.check_name(attr, sym::inline) {
1469             struct_span_err!(
1470                 tcx.sess,
1471                 attr.span,
1472                 E0518,
1473                 "attribute should be applied to function or closure",
1474             )
1475             .span_label(attr.span, "not a function or closure")
1476             .emit();
1477         }
1478     }
1479 }
1480
1481 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1482     let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1483     tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1484     tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1485     check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
1486     if module_def_id.is_top_level_module() {
1487         check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
1488         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1489     }
1490 }
1491
1492 pub(crate) fn provide(providers: &mut Providers) {
1493     *providers = Providers { check_mod_attrs, ..*providers };
1494 }