]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Prevent #[doc(alias = "...")] at crate level
[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, 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::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
17 use rustc_hir::{MethodKind, Target};
18 use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES};
19 use rustc_session::parse::feature_err;
20 use rustc_span::symbol::sym;
21 use rustc_span::Span;
22
23 pub(crate) fn target_from_impl_item<'tcx>(
24     tcx: TyCtxt<'tcx>,
25     impl_item: &hir::ImplItem<'_>,
26 ) -> Target {
27     match impl_item.kind {
28         hir::ImplItemKind::Const(..) => Target::AssocConst,
29         hir::ImplItemKind::Fn(..) => {
30             let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id);
31             let containing_item = tcx.hir().expect_item(parent_hir_id);
32             let containing_impl_is_for_trait = match &containing_item.kind {
33                 hir::ItemKind::Impl { ref of_trait, .. } => of_trait.is_some(),
34                 _ => bug!("parent of an ImplItem must be an Impl"),
35             };
36             if containing_impl_is_for_trait {
37                 Target::Method(MethodKind::Trait { body: true })
38             } else {
39                 Target::Method(MethodKind::Inherent)
40             }
41         }
42         hir::ImplItemKind::TyAlias(..) => Target::AssocTy,
43     }
44 }
45
46 #[derive(Clone, Copy)]
47 enum ItemLike<'tcx> {
48     Item(&'tcx Item<'tcx>),
49     ForeignItem(&'tcx ForeignItem<'tcx>),
50 }
51
52 struct CheckAttrVisitor<'tcx> {
53     tcx: TyCtxt<'tcx>,
54 }
55
56 impl CheckAttrVisitor<'tcx> {
57     /// Checks any attribute.
58     fn check_attributes(
59         &self,
60         hir_id: HirId,
61         attrs: &'hir [Attribute],
62         span: &Span,
63         target: Target,
64         item: Option<ItemLike<'_>>,
65     ) {
66         let mut is_valid = true;
67         for attr in attrs {
68             is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
69                 self.check_inline(hir_id, attr, span, target)
70             } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
71                 self.check_non_exhaustive(attr, span, target)
72             } else if self.tcx.sess.check_name(attr, sym::marker) {
73                 self.check_marker(attr, span, target)
74             } else if self.tcx.sess.check_name(attr, sym::target_feature) {
75                 self.check_target_feature(hir_id, attr, span, target)
76             } else if self.tcx.sess.check_name(attr, sym::track_caller) {
77                 self.check_track_caller(&attr.span, attrs, span, target)
78             } else if self.tcx.sess.check_name(attr, sym::doc) {
79                 self.check_doc_alias(attr, hir_id, target)
80             } else if self.tcx.sess.check_name(attr, sym::no_link) {
81                 self.check_no_link(&attr, span, target)
82             } else if self.tcx.sess.check_name(attr, sym::export_name) {
83                 self.check_export_name(&attr, span, target)
84             } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) {
85                 self.check_rustc_args_required_const(&attr, span, target, item)
86             } else {
87                 // lint-only checks
88                 if self.tcx.sess.check_name(attr, sym::cold) {
89                     self.check_cold(hir_id, attr, span, target);
90                 } else if self.tcx.sess.check_name(attr, sym::link_name) {
91                     self.check_link_name(hir_id, attr, span, target);
92                 } else if self.tcx.sess.check_name(attr, sym::link_section) {
93                     self.check_link_section(hir_id, attr, span, target);
94                 } else if self.tcx.sess.check_name(attr, sym::no_mangle) {
95                     self.check_no_mangle(hir_id, attr, span, target);
96                 }
97                 true
98             };
99         }
100
101         if !is_valid {
102             return;
103         }
104
105         if matches!(target, Target::Fn | Target::Method(_) | Target::ForeignFn) {
106             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
107         }
108
109         self.check_repr(attrs, span, target, item, hir_id);
110         self.check_used(attrs, target);
111     }
112
113     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
114     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
115         match target {
116             Target::Fn
117             | Target::Closure
118             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
119             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
120                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
121                     lint.build("`#[inline]` is ignored on function prototypes").emit()
122                 });
123                 true
124             }
125             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
126             // just a lint, because we previously erroneously allowed it and some crates used it
127             // accidentally, to to be compatible with crates depending on them, we can't throw an
128             // error here.
129             Target::AssocConst => {
130                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
131                     lint.build("`#[inline]` is ignored on constants")
132                         .warn(
133                             "this was previously accepted by the compiler but is \
134                              being phased out; it will become a hard error in \
135                              a future release!",
136                         )
137                         .note(
138                             "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
139                              for more information",
140                         )
141                         .emit();
142                 });
143                 true
144             }
145             _ => {
146                 struct_span_err!(
147                     self.tcx.sess,
148                     attr.span,
149                     E0518,
150                     "attribute should be applied to function or closure",
151                 )
152                 .span_label(*span, "not a function or closure")
153                 .emit();
154                 false
155             }
156         }
157     }
158
159     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
160     fn check_track_caller(
161         &self,
162         attr_span: &Span,
163         attrs: &'hir [Attribute],
164         span: &Span,
165         target: Target,
166     ) -> bool {
167         match target {
168             _ if self.tcx.sess.contains_name(attrs, sym::naked) => {
169                 struct_span_err!(
170                     self.tcx.sess,
171                     *attr_span,
172                     E0736,
173                     "cannot use `#[track_caller]` with `#[naked]`",
174                 )
175                 .emit();
176                 false
177             }
178             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
179             _ => {
180                 struct_span_err!(
181                     self.tcx.sess,
182                     *attr_span,
183                     E0739,
184                     "attribute should be applied to function"
185                 )
186                 .span_label(*span, "not a function")
187                 .emit();
188                 false
189             }
190         }
191     }
192
193     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
194     fn check_non_exhaustive(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
195         match target {
196             Target::Struct | Target::Enum => true,
197             _ => {
198                 struct_span_err!(
199                     self.tcx.sess,
200                     attr.span,
201                     E0701,
202                     "attribute can only be applied to a struct or enum"
203                 )
204                 .span_label(*span, "not a struct or enum")
205                 .emit();
206                 false
207             }
208         }
209     }
210
211     /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
212     fn check_marker(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
213         match target {
214             Target::Trait => true,
215             _ => {
216                 self.tcx
217                     .sess
218                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
219                     .span_label(*span, "not a trait")
220                     .emit();
221                 false
222             }
223         }
224     }
225
226     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
227     fn check_target_feature(
228         &self,
229         hir_id: HirId,
230         attr: &Attribute,
231         span: &Span,
232         target: Target,
233     ) -> bool {
234         match target {
235             Target::Fn
236             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
237             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
238             // crates used this, so only emit a warning.
239             Target::Statement => {
240                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
241                     lint.build("attribute should be applied to a function")
242                         .warn(
243                             "this was previously accepted by the compiler but is \
244                              being phased out; it will become a hard error in \
245                              a future release!",
246                         )
247                         .span_label(*span, "not a function")
248                         .emit();
249                 });
250                 true
251             }
252             _ => {
253                 self.tcx
254                     .sess
255                     .struct_span_err(attr.span, "attribute should be applied to a function")
256                     .span_label(*span, "not a function")
257                     .emit();
258                 false
259             }
260         }
261     }
262
263     fn doc_alias_str_error(&self, meta: &NestedMetaItem) {
264         self.tcx
265             .sess
266             .struct_span_err(
267                 meta.span(),
268                 "doc alias attribute expects a string: #[doc(alias = \"0\")]",
269             )
270             .emit();
271     }
272
273     fn check_doc_alias(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
274         if let Some(mi) = attr.meta() {
275             if let Some(list) = mi.meta_item_list() {
276                 for meta in list {
277                     if meta.has_name(sym::alias) {
278                         if !meta.is_value_str() {
279                             self.doc_alias_str_error(meta);
280                             return false;
281                         }
282                         let doc_alias =
283                             meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
284                         if doc_alias.is_empty() {
285                             self.doc_alias_str_error(meta);
286                             return false;
287                         }
288                         if let Some(c) =
289                             doc_alias.chars().find(|&c| c == '"' || c == '\'' || c.is_whitespace())
290                         {
291                             self.tcx
292                                 .sess
293                                 .struct_span_err(
294                                     meta.span(),
295                                     &format!(
296                                         "{:?} character isn't allowed in `#[doc(alias = \"...\")]`",
297                                         c,
298                                     ),
299                                 )
300                                 .emit();
301                             return false;
302                         }
303                         if let Some(err) = match target {
304                             Target::Impl => Some("implementation block"),
305                             Target::ForeignMod => Some("extern block"),
306                             Target::AssocTy => {
307                                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
308                                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
309                                 if Target::from_item(containing_item) == Target::Impl {
310                                     Some("type alias in implementation block")
311                                 } else {
312                                     None
313                                 }
314                             }
315                             Target::AssocConst => {
316                                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
317                                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
318                                 // We can't link to trait impl's consts.
319                                 let err = "associated constant in trait implementation block";
320                                 match containing_item.kind {
321                                     ItemKind::Impl { of_trait: Some(_), .. } => Some(err),
322                                     _ => None,
323                                 }
324                             }
325                             _ => None,
326                         } {
327                             self.tcx
328                                 .sess
329                                 .struct_span_err(
330                                     meta.span(),
331                                     &format!("`#[doc(alias = \"...\")]` isn't allowed on {}", err),
332                                 )
333                                 .emit();
334                             return false;
335                         }
336                         if CRATE_HIR_ID == hir_id {
337                             self.tcx
338                                 .sess
339                                 .struct_span_err(
340                                     meta.span(),
341                                     "`#![doc(alias = \"...\")]` isn't allowed as a crate \
342                                      level attribute",
343                                 )
344                                 .emit();
345                             return false;
346                         }
347                     }
348                 }
349             }
350         }
351         true
352     }
353
354     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
355     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
356         match target {
357             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
358             _ => {
359                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
360                 // this, so only emit a warning.
361                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
362                     lint.build("attribute should be applied to a function")
363                         .warn(
364                             "this was previously accepted by the compiler but is \
365                              being phased out; it will become a hard error in \
366                              a future release!",
367                         )
368                         .span_label(*span, "not a function")
369                         .emit();
370                 });
371             }
372         }
373     }
374
375     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
376     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
377         match target {
378             Target::ForeignFn | Target::ForeignStatic => {}
379             _ => {
380                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
381                 // used this, so only emit a warning.
382                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
383                     let mut diag =
384                         lint.build("attribute should be applied to a foreign function or static");
385                     diag.warn(
386                         "this was previously accepted by the compiler but is \
387                          being phased out; it will become a hard error in \
388                          a future release!",
389                     );
390
391                     // See issue #47725
392                     if let Target::ForeignMod = target {
393                         if let Some(value) = attr.value_str() {
394                             diag.span_help(
395                                 attr.span,
396                                 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
397                             );
398                         } else {
399                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
400                         }
401                     }
402
403                     diag.span_label(*span, "not a foreign function or static");
404                     diag.emit();
405                 });
406             }
407         }
408     }
409
410     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
411     fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
412         if target == Target::ExternCrate {
413             true
414         } else {
415             self.tcx
416                 .sess
417                 .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item")
418                 .span_label(*span, "not an `extern crate` item")
419                 .emit();
420             false
421         }
422     }
423
424     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
425     fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
426         match target {
427             Target::Static | Target::Fn | Target::Method(..) => true,
428             _ => {
429                 self.tcx
430                     .sess
431                     .struct_span_err(
432                         attr.span,
433                         "attribute should be applied to a function or static",
434                     )
435                     .span_label(*span, "not a function or static")
436                     .emit();
437                 false
438             }
439         }
440     }
441
442     /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
443     fn check_rustc_args_required_const(
444         &self,
445         attr: &Attribute,
446         span: &Span,
447         target: Target,
448         item: Option<ItemLike<'_>>,
449     ) -> bool {
450         if let Target::Fn | Target::Method(..) | Target::ForeignFn = target {
451             let mut invalid_args = vec![];
452             for meta in attr.meta_item_list().expect("no meta item list") {
453                 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
454                     if let Some(ItemLike::Item(Item {
455                         kind: ItemKind::Fn(FnSig { decl, .. }, ..),
456                         ..
457                     }))
458                     | Some(ItemLike::ForeignItem(ForeignItem {
459                         kind: ForeignItemKind::Fn(decl, ..),
460                         ..
461                     })) = item
462                     {
463                         let arg_count = decl.inputs.len() as u128;
464                         if *val >= arg_count {
465                             let span = meta.span();
466                             self.tcx
467                                 .sess
468                                 .struct_span_err(span, "index exceeds number of arguments")
469                                 .span_label(
470                                     span,
471                                     format!(
472                                         "there {} only {} argument{}",
473                                         if arg_count != 1 { "are" } else { "is" },
474                                         arg_count,
475                                         pluralize!(arg_count)
476                                     ),
477                                 )
478                                 .emit();
479                             return false;
480                         }
481                     } else {
482                         bug!("should be a function item");
483                     }
484                 } else {
485                     invalid_args.push(meta.span());
486                 }
487             }
488             if !invalid_args.is_empty() {
489                 self.tcx
490                     .sess
491                     .struct_span_err(invalid_args, "arguments should be non-negative integers")
492                     .emit();
493                 false
494             } else {
495                 true
496             }
497         } else {
498             self.tcx
499                 .sess
500                 .struct_span_err(attr.span, "attribute should be applied to a function")
501                 .span_label(*span, "not a function")
502                 .emit();
503             false
504         }
505     }
506
507     /// Checks if `#[link_section]` is applied to a function or static.
508     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
509         match target {
510             Target::Static | Target::Fn | Target::Method(..) => {}
511             _ => {
512                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
513                 // crates used this, so only emit a warning.
514                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
515                     lint.build("attribute should be applied to a function or static")
516                         .warn(
517                             "this was previously accepted by the compiler but is \
518                              being phased out; it will become a hard error in \
519                              a future release!",
520                         )
521                         .span_label(*span, "not a function or static")
522                         .emit();
523                 });
524             }
525         }
526     }
527
528     /// Checks if `#[no_mangle]` is applied to a function or static.
529     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
530         match target {
531             Target::Static | Target::Fn | Target::Method(..) => {}
532             _ => {
533                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
534                 // crates used this, so only emit a warning.
535                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
536                     lint.build("attribute should be applied to a function or static")
537                         .warn(
538                             "this was previously accepted by the compiler but is \
539                              being phased out; it will become a hard error in \
540                              a future release!",
541                         )
542                         .span_label(*span, "not a function or static")
543                         .emit();
544                 });
545             }
546         }
547     }
548
549     /// Checks if the `#[repr]` attributes on `item` are valid.
550     fn check_repr(
551         &self,
552         attrs: &'hir [Attribute],
553         span: &Span,
554         target: Target,
555         item: Option<ItemLike<'_>>,
556         hir_id: HirId,
557     ) {
558         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
559         // ```
560         // #[repr(foo)]
561         // #[repr(bar, align(8))]
562         // ```
563         let hints: Vec<_> = attrs
564             .iter()
565             .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
566             .filter_map(|attr| attr.meta_item_list())
567             .flatten()
568             .collect();
569
570         let mut int_reprs = 0;
571         let mut is_c = false;
572         let mut is_simd = false;
573         let mut is_transparent = false;
574
575         for hint in &hints {
576             let (article, allowed_targets) = match hint.name_or_empty() {
577                 name @ sym::C | name @ sym::align => {
578                     is_c |= name == sym::C;
579                     match target {
580                         Target::Struct | Target::Union | Target::Enum => continue,
581                         _ => ("a", "struct, enum, or union"),
582                     }
583                 }
584                 sym::packed => {
585                     if target != Target::Struct && target != Target::Union {
586                         ("a", "struct or union")
587                     } else {
588                         continue;
589                     }
590                 }
591                 sym::simd => {
592                     is_simd = true;
593                     if target != Target::Struct {
594                         ("a", "struct")
595                     } else {
596                         continue;
597                     }
598                 }
599                 sym::transparent => {
600                     is_transparent = true;
601                     match target {
602                         Target::Struct | Target::Union | Target::Enum => continue,
603                         _ => ("a", "struct, enum, or union"),
604                     }
605                 }
606                 sym::no_niche => {
607                     if !self.tcx.features().enabled(sym::no_niche) {
608                         feature_err(
609                             &self.tcx.sess.parse_sess,
610                             sym::no_niche,
611                             hint.span(),
612                             "the attribute `repr(no_niche)` is currently unstable",
613                         )
614                         .emit();
615                     }
616                     match target {
617                         Target::Struct | Target::Enum => continue,
618                         _ => ("a", "struct or enum"),
619                     }
620                 }
621                 sym::i8
622                 | sym::u8
623                 | sym::i16
624                 | sym::u16
625                 | sym::i32
626                 | sym::u32
627                 | sym::i64
628                 | sym::u64
629                 | sym::i128
630                 | sym::u128
631                 | sym::isize
632                 | sym::usize => {
633                     int_reprs += 1;
634                     if target != Target::Enum {
635                         ("an", "enum")
636                     } else {
637                         continue;
638                     }
639                 }
640                 _ => continue,
641             };
642             self.emit_repr_error(
643                 hint.span(),
644                 *span,
645                 &format!("attribute should be applied to {}", allowed_targets),
646                 &format!("not {} {}", article, allowed_targets),
647             )
648         }
649
650         // Just point at all repr hints if there are any incompatibilities.
651         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
652         let hint_spans = hints.iter().map(|hint| hint.span());
653
654         // Error on repr(transparent, <anything else apart from no_niche>).
655         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
656         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
657         if is_transparent && non_no_niche_count > 1 {
658             let hint_spans: Vec<_> = hint_spans.clone().collect();
659             struct_span_err!(
660                 self.tcx.sess,
661                 hint_spans,
662                 E0692,
663                 "transparent {} cannot have other repr hints",
664                 target
665             )
666             .emit();
667         }
668         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
669         if (int_reprs > 1)
670             || (is_simd && is_c)
671             || (int_reprs == 1
672                 && is_c
673                 && item.map_or(false, |item| {
674                     if let ItemLike::Item(item) = item {
675                         return is_c_like_enum(item);
676                     }
677                     return false;
678                 }))
679         {
680             self.tcx.struct_span_lint_hir(
681                 CONFLICTING_REPR_HINTS,
682                 hir_id,
683                 hint_spans.collect::<Vec<Span>>(),
684                 |lint| {
685                     lint.build("conflicting representation hints")
686                         .code(rustc_errors::error_code!(E0566))
687                         .emit();
688                 },
689             );
690         }
691     }
692
693     fn emit_repr_error(
694         &self,
695         hint_span: Span,
696         label_span: Span,
697         hint_message: &str,
698         label_message: &str,
699     ) {
700         struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
701             .span_label(label_span, label_message)
702             .emit();
703     }
704
705     fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) {
706         // When checking statements ignore expressions, they will be checked later
707         if let hir::StmtKind::Local(ref l) = stmt.kind {
708             self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
709             for attr in l.attrs.iter() {
710                 if self.tcx.sess.check_name(attr, sym::repr) {
711                     self.emit_repr_error(
712                         attr.span,
713                         stmt.span,
714                         "attribute should not be applied to a statement",
715                         "not a struct, enum, or union",
716                     );
717                 }
718             }
719         }
720     }
721
722     fn check_expr_attributes(&self, expr: &hir::Expr<'_>) {
723         let target = match expr.kind {
724             hir::ExprKind::Closure(..) => Target::Closure,
725             _ => Target::Expression,
726         };
727         self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
728         for attr in expr.attrs.iter() {
729             if self.tcx.sess.check_name(attr, sym::repr) {
730                 self.emit_repr_error(
731                     attr.span,
732                     expr.span,
733                     "attribute should not be applied to an expression",
734                     "not defining a struct, enum, or union",
735                 );
736             }
737         }
738         if target == Target::Closure {
739             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(expr.hir_id));
740         }
741     }
742
743     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
744         for attr in attrs {
745             if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
746                 self.tcx
747                     .sess
748                     .span_err(attr.span, "attribute must be applied to a `static` variable");
749             }
750         }
751     }
752 }
753
754 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
755     type Map = Map<'tcx>;
756
757     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
758         NestedVisitorMap::OnlyBodies(self.tcx.hir())
759     }
760
761     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
762         let target = Target::from_item(item);
763         self.check_attributes(
764             item.hir_id,
765             item.attrs,
766             &item.span,
767             target,
768             Some(ItemLike::Item(item)),
769         );
770         intravisit::walk_item(self, item)
771     }
772
773     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
774         let target = Target::from_trait_item(trait_item);
775         self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None);
776         intravisit::walk_trait_item(self, trait_item)
777     }
778
779     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
780         let target = Target::from_foreign_item(f_item);
781         self.check_attributes(
782             f_item.hir_id,
783             &f_item.attrs,
784             &f_item.span,
785             target,
786             Some(ItemLike::ForeignItem(f_item)),
787         );
788         intravisit::walk_foreign_item(self, f_item)
789     }
790
791     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
792         let target = target_from_impl_item(self.tcx, impl_item);
793         self.check_attributes(impl_item.hir_id, &impl_item.attrs, &impl_item.span, target, None);
794         intravisit::walk_impl_item(self, impl_item)
795     }
796
797     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
798         self.check_stmt_attributes(stmt);
799         intravisit::walk_stmt(self, stmt)
800     }
801
802     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
803         self.check_expr_attributes(expr);
804         intravisit::walk_expr(self, expr)
805     }
806 }
807
808 fn is_c_like_enum(item: &Item<'_>) -> bool {
809     if let ItemKind::Enum(ref def, _) = item.kind {
810         for variant in def.variants {
811             match variant.data {
812                 hir::VariantData::Unit(..) => { /* continue */ }
813                 _ => return false,
814             }
815         }
816         true
817     } else {
818         false
819     }
820 }
821
822 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
823     tcx.hir()
824         .visit_item_likes_in_module(module_def_id, &mut CheckAttrVisitor { tcx }.as_deep_visitor());
825     if module_def_id.is_top_level_module() {
826         for attr in tcx.hir().krate_attrs() {
827             CheckAttrVisitor { tcx }.check_doc_alias(attr, CRATE_HIR_ID, Target::Mod);
828         }
829     }
830 }
831
832 pub(crate) fn provide(providers: &mut Providers) {
833     *providers = Providers { check_mod_attrs, ..*providers };
834 }