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