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