]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Auto merge of #74699 - notriddle:fd-non-negative, r=m-ou-se
[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         true
362     }
363
364     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
365         let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
366         if doc_keyword.is_empty() {
367             self.doc_attr_str_error(meta, "keyword");
368             return false;
369         }
370         match self.tcx.hir().expect_item(hir_id).kind {
371             ItemKind::Mod(ref module) => {
372                 if !module.item_ids.is_empty() {
373                     self.tcx
374                         .sess
375                         .struct_span_err(
376                             meta.span(),
377                             "`#[doc(keyword = \"...\")]` can only be used on empty modules",
378                         )
379                         .emit();
380                     return false;
381                 }
382             }
383             _ => {
384                 self.tcx
385                     .sess
386                     .struct_span_err(
387                         meta.span(),
388                         "`#[doc(keyword = \"...\")]` can only be used on modules",
389                     )
390                     .emit();
391                 return false;
392             }
393         }
394         if !rustc_lexer::is_ident(&doc_keyword) {
395             self.tcx
396                 .sess
397                 .struct_span_err(
398                     meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
399                     &format!("`{}` is not a valid identifier", doc_keyword),
400                 )
401                 .emit();
402             return false;
403         }
404         true
405     }
406
407     fn check_attr_crate_level(
408         &self,
409         meta: &NestedMetaItem,
410         hir_id: HirId,
411         attr_name: &str,
412     ) -> bool {
413         if CRATE_HIR_ID == hir_id {
414             self.tcx
415                 .sess
416                 .struct_span_err(
417                     meta.span(),
418                     &format!(
419                         "`#![doc({} = \"...\")]` isn't allowed as a crate level attribute",
420                         attr_name,
421                     ),
422                 )
423                 .emit();
424             return false;
425         }
426         true
427     }
428
429     fn check_doc_attrs(&self, attr: &Attribute, hir_id: HirId, target: Target) -> bool {
430         if let Some(mi) = attr.meta() {
431             if let Some(list) = mi.meta_item_list() {
432                 for meta in list {
433                     if meta.has_name(sym::alias) {
434                         if !self.check_attr_crate_level(meta, hir_id, "alias")
435                             || !self.check_doc_alias(meta, hir_id, target)
436                         {
437                             return false;
438                         }
439                     } else if meta.has_name(sym::keyword) {
440                         if !self.check_attr_crate_level(meta, hir_id, "keyword")
441                             || !self.check_doc_keyword(meta, hir_id)
442                         {
443                             return false;
444                         }
445                     }
446                 }
447             }
448         }
449         true
450     }
451
452     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
453     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
454         match target {
455             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
456             _ => {
457                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
458                 // this, so only emit a warning.
459                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
460                     lint.build("attribute should be applied to a function")
461                         .warn(
462                             "this was previously accepted by the compiler but is \
463                              being phased out; it will become a hard error in \
464                              a future release!",
465                         )
466                         .span_label(*span, "not a function")
467                         .emit();
468                 });
469             }
470         }
471     }
472
473     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
474     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
475         match target {
476             Target::ForeignFn | Target::ForeignStatic => {}
477             _ => {
478                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
479                 // used this, so only emit a warning.
480                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
481                     let mut diag =
482                         lint.build("attribute should be applied to a foreign function or static");
483                     diag.warn(
484                         "this was previously accepted by the compiler but is \
485                          being phased out; it will become a hard error in \
486                          a future release!",
487                     );
488
489                     // See issue #47725
490                     if let Target::ForeignMod = target {
491                         if let Some(value) = attr.value_str() {
492                             diag.span_help(
493                                 attr.span,
494                                 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
495                             );
496                         } else {
497                             diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
498                         }
499                     }
500
501                     diag.span_label(*span, "not a foreign function or static");
502                     diag.emit();
503                 });
504             }
505         }
506     }
507
508     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
509     fn check_no_link(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
510         if target == Target::ExternCrate {
511             true
512         } else {
513             self.tcx
514                 .sess
515                 .struct_span_err(attr.span, "attribute should be applied to an `extern crate` item")
516                 .span_label(*span, "not an `extern crate` item")
517                 .emit();
518             false
519         }
520     }
521
522     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
523     fn check_export_name(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
524         match target {
525             Target::Static | Target::Fn | Target::Method(..) => true,
526             _ => {
527                 self.tcx
528                     .sess
529                     .struct_span_err(
530                         attr.span,
531                         "attribute should be applied to a function or static",
532                     )
533                     .span_label(*span, "not a function or static")
534                     .emit();
535                 false
536             }
537         }
538     }
539
540     /// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
541     fn check_rustc_args_required_const(
542         &self,
543         attr: &Attribute,
544         span: &Span,
545         target: Target,
546         item: Option<ItemLike<'_>>,
547     ) -> bool {
548         let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
549         if !is_function {
550             self.tcx
551                 .sess
552                 .struct_span_err(attr.span, "attribute should be applied to a function")
553                 .span_label(*span, "not a function")
554                 .emit();
555             return false;
556         }
557
558         let list = match attr.meta_item_list() {
559             // The attribute form is validated on AST.
560             None => return false,
561             Some(it) => it,
562         };
563
564         let mut invalid_args = vec![];
565         for meta in list {
566             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
567                 if let Some(ItemLike::Item(Item {
568                     kind: ItemKind::Fn(FnSig { decl, .. }, ..),
569                     ..
570                 }))
571                 | Some(ItemLike::ForeignItem(ForeignItem {
572                     kind: ForeignItemKind::Fn(decl, ..),
573                     ..
574                 })) = item
575                 {
576                     let arg_count = decl.inputs.len() as u128;
577                     if *val >= arg_count {
578                         let span = meta.span();
579                         self.tcx
580                             .sess
581                             .struct_span_err(span, "index exceeds number of arguments")
582                             .span_label(
583                                 span,
584                                 format!(
585                                     "there {} only {} argument{}",
586                                     if arg_count != 1 { "are" } else { "is" },
587                                     arg_count,
588                                     pluralize!(arg_count)
589                                 ),
590                             )
591                             .emit();
592                         return false;
593                     }
594                 } else {
595                     bug!("should be a function item");
596                 }
597             } else {
598                 invalid_args.push(meta.span());
599             }
600         }
601
602         if !invalid_args.is_empty() {
603             self.tcx
604                 .sess
605                 .struct_span_err(invalid_args, "arguments should be non-negative integers")
606                 .emit();
607             false
608         } else {
609             true
610         }
611     }
612
613     /// Checks if `#[link_section]` is applied to a function or static.
614     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
615         match target {
616             Target::Static | Target::Fn | Target::Method(..) => {}
617             _ => {
618                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
619                 // crates used this, so only emit a warning.
620                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
621                     lint.build("attribute should be applied to a function or static")
622                         .warn(
623                             "this was previously accepted by the compiler but is \
624                              being phased out; it will become a hard error in \
625                              a future release!",
626                         )
627                         .span_label(*span, "not a function or static")
628                         .emit();
629                 });
630             }
631         }
632     }
633
634     /// Checks if `#[no_mangle]` is applied to a function or static.
635     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
636         match target {
637             Target::Static | Target::Fn | Target::Method(..) => {}
638             _ => {
639                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
640                 // crates used this, so only emit a warning.
641                 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
642                     lint.build("attribute should be applied to a function or static")
643                         .warn(
644                             "this was previously accepted by the compiler but is \
645                              being phased out; it will become a hard error in \
646                              a future release!",
647                         )
648                         .span_label(*span, "not a function or static")
649                         .emit();
650                 });
651             }
652         }
653     }
654
655     /// Checks if the `#[repr]` attributes on `item` are valid.
656     fn check_repr(
657         &self,
658         attrs: &'hir [Attribute],
659         span: &Span,
660         target: Target,
661         item: Option<ItemLike<'_>>,
662         hir_id: HirId,
663     ) {
664         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
665         // ```
666         // #[repr(foo)]
667         // #[repr(bar, align(8))]
668         // ```
669         let hints: Vec<_> = attrs
670             .iter()
671             .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
672             .filter_map(|attr| attr.meta_item_list())
673             .flatten()
674             .collect();
675
676         let mut int_reprs = 0;
677         let mut is_c = false;
678         let mut is_simd = false;
679         let mut is_transparent = false;
680
681         for hint in &hints {
682             let (article, allowed_targets) = match hint.name_or_empty() {
683                 _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => {
684                     ("a", "struct, enum, or union")
685                 }
686                 name @ sym::C | name @ sym::align => {
687                     is_c |= name == sym::C;
688                     match target {
689                         Target::Struct | Target::Union | Target::Enum => continue,
690                         _ => ("a", "struct, enum, or union"),
691                     }
692                 }
693                 sym::packed => {
694                     if target != Target::Struct && target != Target::Union {
695                         ("a", "struct or union")
696                     } else {
697                         continue;
698                     }
699                 }
700                 sym::simd => {
701                     is_simd = true;
702                     if target != Target::Struct {
703                         ("a", "struct")
704                     } else {
705                         continue;
706                     }
707                 }
708                 sym::transparent => {
709                     is_transparent = true;
710                     match target {
711                         Target::Struct | Target::Union | Target::Enum => continue,
712                         _ => ("a", "struct, enum, or union"),
713                     }
714                 }
715                 sym::no_niche => {
716                     if !self.tcx.features().enabled(sym::no_niche) {
717                         feature_err(
718                             &self.tcx.sess.parse_sess,
719                             sym::no_niche,
720                             hint.span(),
721                             "the attribute `repr(no_niche)` is currently unstable",
722                         )
723                         .emit();
724                     }
725                     match target {
726                         Target::Struct | Target::Enum => continue,
727                         _ => ("a", "struct or enum"),
728                     }
729                 }
730                 sym::i8
731                 | sym::u8
732                 | sym::i16
733                 | sym::u16
734                 | sym::i32
735                 | sym::u32
736                 | sym::i64
737                 | sym::u64
738                 | sym::i128
739                 | sym::u128
740                 | sym::isize
741                 | sym::usize => {
742                     int_reprs += 1;
743                     if target != Target::Enum {
744                         ("an", "enum")
745                     } else {
746                         continue;
747                     }
748                 }
749                 _ => continue,
750             };
751
752             struct_span_err!(
753                 self.tcx.sess,
754                 hint.span(),
755                 E0517,
756                 "{}",
757                 &format!("attribute should be applied to {} {}", article, allowed_targets)
758             )
759             .span_label(*span, &format!("not {} {}", article, allowed_targets))
760             .emit();
761         }
762
763         // Just point at all repr hints if there are any incompatibilities.
764         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
765         let hint_spans = hints.iter().map(|hint| hint.span());
766
767         // Error on repr(transparent, <anything else apart from no_niche>).
768         let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
769         let non_no_niche_count = hints.iter().filter(non_no_niche).count();
770         if is_transparent && non_no_niche_count > 1 {
771             let hint_spans: Vec<_> = hint_spans.clone().collect();
772             struct_span_err!(
773                 self.tcx.sess,
774                 hint_spans,
775                 E0692,
776                 "transparent {} cannot have other repr hints",
777                 target
778             )
779             .emit();
780         }
781         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
782         if (int_reprs > 1)
783             || (is_simd && is_c)
784             || (int_reprs == 1
785                 && is_c
786                 && item.map_or(false, |item| {
787                     if let ItemLike::Item(item) = item {
788                         return is_c_like_enum(item);
789                     }
790                     return false;
791                 }))
792         {
793             self.tcx.struct_span_lint_hir(
794                 CONFLICTING_REPR_HINTS,
795                 hir_id,
796                 hint_spans.collect::<Vec<Span>>(),
797                 |lint| {
798                     lint.build("conflicting representation hints")
799                         .code(rustc_errors::error_code!(E0566))
800                         .emit();
801                 },
802             );
803         }
804     }
805
806     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
807         for attr in attrs {
808             if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
809                 self.tcx
810                     .sess
811                     .span_err(attr.span, "attribute must be applied to a `static` variable");
812             }
813         }
814     }
815
816     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
817     /// (Allows proc_macro functions)
818     fn check_allow_internal_unstable(
819         &self,
820         attr: &Attribute,
821         span: &Span,
822         target: Target,
823         attrs: &[Attribute],
824     ) -> bool {
825         debug!("Checking target: {:?}", target);
826         if target == Target::Fn {
827             for attr in attrs {
828                 if self.tcx.sess.is_proc_macro_attr(attr) {
829                     debug!("Is proc macro attr");
830                     return true;
831                 }
832             }
833             debug!("Is not proc macro attr");
834         }
835         self.tcx
836             .sess
837             .struct_span_err(attr.span, "attribute should be applied to a macro")
838             .span_label(*span, "not a macro")
839             .emit();
840         false
841     }
842
843     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
844     /// (Allows proc_macro functions)
845     fn check_rustc_allow_const_fn_unstable(
846         &self,
847         hir_id: HirId,
848         attr: &Attribute,
849         span: &Span,
850         target: Target,
851     ) -> bool {
852         if let Target::Fn | Target::Method(_) = target {
853             if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) {
854                 return true;
855             }
856         }
857         self.tcx
858             .sess
859             .struct_span_err(attr.span, "attribute should be applied to `const fn`")
860             .span_label(*span, "not a `const fn`")
861             .emit();
862         false
863     }
864 }
865
866 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
867     type Map = Map<'tcx>;
868
869     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
870         NestedVisitorMap::OnlyBodies(self.tcx.hir())
871     }
872
873     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
874         let target = Target::from_item(item);
875         self.check_attributes(
876             item.hir_id,
877             item.attrs,
878             &item.span,
879             target,
880             Some(ItemLike::Item(item)),
881         );
882         intravisit::walk_item(self, item)
883     }
884
885     fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
886         let target = Target::from_generic_param(generic_param);
887         self.check_attributes(
888             generic_param.hir_id,
889             generic_param.attrs,
890             &generic_param.span,
891             target,
892             None,
893         );
894         intravisit::walk_generic_param(self, generic_param)
895     }
896
897     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
898         let target = Target::from_trait_item(trait_item);
899         self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None);
900         intravisit::walk_trait_item(self, trait_item)
901     }
902
903     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
904         let target = Target::from_foreign_item(f_item);
905         self.check_attributes(
906             f_item.hir_id,
907             &f_item.attrs,
908             &f_item.span,
909             target,
910             Some(ItemLike::ForeignItem(f_item)),
911         );
912         intravisit::walk_foreign_item(self, f_item)
913     }
914
915     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
916         let target = target_from_impl_item(self.tcx, impl_item);
917         self.check_attributes(impl_item.hir_id, &impl_item.attrs, &impl_item.span, target, None);
918         intravisit::walk_impl_item(self, impl_item)
919     }
920
921     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
922         // When checking statements ignore expressions, they will be checked later.
923         if let hir::StmtKind::Local(ref l) = stmt.kind {
924             self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
925         }
926         intravisit::walk_stmt(self, stmt)
927     }
928
929     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
930         let target = match expr.kind {
931             hir::ExprKind::Closure(..) => Target::Closure,
932             _ => Target::Expression,
933         };
934
935         self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
936         intravisit::walk_expr(self, expr)
937     }
938
939     fn visit_variant(
940         &mut self,
941         variant: &'tcx hir::Variant<'tcx>,
942         generics: &'tcx hir::Generics<'tcx>,
943         item_id: HirId,
944     ) {
945         self.check_attributes(variant.id, variant.attrs, &variant.span, Target::Variant, None);
946         intravisit::walk_variant(self, variant, generics, item_id)
947     }
948 }
949
950 fn is_c_like_enum(item: &Item<'_>) -> bool {
951     if let ItemKind::Enum(ref def, _) = item.kind {
952         for variant in def.variants {
953             match variant.data {
954                 hir::VariantData::Unit(..) => { /* continue */ }
955                 _ => return false,
956             }
957         }
958         true
959     } else {
960         false
961     }
962 }
963
964 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
965     const ATTRS_TO_CHECK: &[Symbol] = &[
966         sym::macro_export,
967         sym::repr,
968         sym::path,
969         sym::automatically_derived,
970         sym::start,
971         sym::main,
972     ];
973
974     for attr in attrs {
975         for attr_to_check in ATTRS_TO_CHECK {
976             if tcx.sess.check_name(attr, *attr_to_check) {
977                 tcx.sess
978                     .struct_span_err(
979                         attr.span,
980                         &format!(
981                             "`{}` attribute cannot be used at crate level",
982                             attr_to_check.to_ident_string()
983                         ),
984                     )
985                     .emit();
986             }
987         }
988     }
989 }
990
991 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
992     tcx.hir()
993         .visit_item_likes_in_module(module_def_id, &mut CheckAttrVisitor { tcx }.as_deep_visitor());
994     if module_def_id.is_top_level_module() {
995         CheckAttrVisitor { tcx }.check_attributes(
996             CRATE_HIR_ID,
997             tcx.hir().krate_attrs(),
998             &DUMMY_SP,
999             Target::Mod,
1000             None,
1001         );
1002         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1003     }
1004 }
1005
1006 pub(crate) fn provide(providers: &mut Providers) {
1007     *providers = Providers { check_mod_attrs, ..*providers };
1008 }