]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/check_attr.rs
Rollup merge of #101292 - rust-lang:notriddle/rustdoc-table-first-child, r=GuillaumeGomez
[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 crate::errors;
8 use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
9 use rustc_data_structures::fx::FxHashMap;
10 use rustc_errors::{fluent, struct_span_err, Applicability, MultiSpan};
11 use rustc_expand::base::resolve_path;
12 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
13 use rustc_hir as hir;
14 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
15 use rustc_hir::intravisit::{self, Visitor};
16 use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
17 use rustc_hir::{MethodKind, Target};
18 use rustc_middle::hir::nested_filter;
19 use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
20 use rustc_middle::ty::query::Providers;
21 use rustc_middle::ty::TyCtxt;
22 use rustc_session::lint::builtin::{
23     CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
24 };
25 use rustc_session::parse::feature_err;
26 use rustc_span::symbol::{kw, sym, Symbol};
27 use rustc_span::{Span, DUMMY_SP};
28 use rustc_target::spec::abi::Abi;
29 use std::collections::hash_map::Entry;
30
31 pub(crate) fn target_from_impl_item<'tcx>(
32     tcx: TyCtxt<'tcx>,
33     impl_item: &hir::ImplItem<'_>,
34 ) -> Target {
35     match impl_item.kind {
36         hir::ImplItemKind::Const(..) => Target::AssocConst,
37         hir::ImplItemKind::Fn(..) => {
38             let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id());
39             let containing_item = tcx.hir().expect_item(parent_hir_id);
40             let containing_impl_is_for_trait = match &containing_item.kind {
41                 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
42                 _ => bug!("parent of an ImplItem must be an Impl"),
43             };
44             if containing_impl_is_for_trait {
45                 Target::Method(MethodKind::Trait { body: true })
46             } else {
47                 Target::Method(MethodKind::Inherent)
48             }
49         }
50         hir::ImplItemKind::TyAlias(..) => Target::AssocTy,
51     }
52 }
53
54 #[derive(Clone, Copy)]
55 enum ItemLike<'tcx> {
56     Item(&'tcx Item<'tcx>),
57     ForeignItem,
58 }
59
60 struct CheckAttrVisitor<'tcx> {
61     tcx: TyCtxt<'tcx>,
62 }
63
64 impl CheckAttrVisitor<'_> {
65     /// Checks any attribute.
66     fn check_attributes(
67         &self,
68         hir_id: HirId,
69         span: Span,
70         target: Target,
71         item: Option<ItemLike<'_>>,
72     ) {
73         let mut doc_aliases = FxHashMap::default();
74         let mut is_valid = true;
75         let mut specified_inline = None;
76         let mut seen = FxHashMap::default();
77         let attrs = self.tcx.hir().attrs(hir_id);
78         for attr in attrs {
79             let attr_is_valid = match attr.name_or_empty() {
80                 sym::inline => self.check_inline(hir_id, attr, span, target),
81                 sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
82                 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
83                 sym::marker => self.check_marker(hir_id, attr, span, target),
84                 sym::rustc_must_implement_one_of => {
85                     self.check_rustc_must_implement_one_of(attr, span, target)
86                 }
87                 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
88                 sym::thread_local => self.check_thread_local(attr, span, target),
89                 sym::track_caller => {
90                     self.check_track_caller(hir_id, attr.span, attrs, span, target)
91                 }
92                 sym::doc => self.check_doc_attrs(
93                     attr,
94                     hir_id,
95                     target,
96                     &mut specified_inline,
97                     &mut doc_aliases,
98                 ),
99                 sym::no_link => self.check_no_link(hir_id, &attr, span, target),
100                 sym::export_name => self.check_export_name(hir_id, &attr, span, target),
101                 sym::rustc_layout_scalar_valid_range_start
102                 | sym::rustc_layout_scalar_valid_range_end => {
103                     self.check_rustc_layout_scalar_valid_range(&attr, span, target)
104                 }
105                 sym::allow_internal_unstable => {
106                     self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
107                 }
108                 sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
109                 sym::rustc_allow_const_fn_unstable => {
110                     self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
111                 }
112                 sym::rustc_std_internal_symbol => {
113                     self.check_rustc_std_internal_symbol(&attr, span, target)
114                 }
115                 sym::naked => self.check_naked(hir_id, attr, span, target),
116                 sym::rustc_legacy_const_generics => {
117                     self.check_rustc_legacy_const_generics(&attr, span, target, item)
118                 }
119                 sym::rustc_lint_query_instability => {
120                     self.check_rustc_lint_query_instability(&attr, span, target)
121                 }
122                 sym::rustc_lint_diagnostics => {
123                     self.check_rustc_lint_diagnostics(&attr, span, target)
124                 }
125                 sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
126                 sym::rustc_lint_opt_deny_field_access => {
127                     self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
128                 }
129                 sym::rustc_clean
130                 | sym::rustc_dirty
131                 | sym::rustc_if_this_changed
132                 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
133                 sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
134                 sym::const_trait => self.check_const_trait(attr, span, target),
135                 sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
136                 sym::must_use => self.check_must_use(hir_id, &attr, span, target),
137                 sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
138                 sym::rustc_allow_incoherent_impl => {
139                     self.check_allow_incoherent_impl(&attr, span, target)
140                 }
141                 sym::rustc_has_incoherent_inherent_impls => {
142                     self.check_has_incoherent_inherent_impls(&attr, span, target)
143                 }
144                 sym::rustc_const_unstable
145                 | sym::rustc_const_stable
146                 | sym::unstable
147                 | sym::stable
148                 | sym::rustc_allowed_through_unstable_modules
149                 | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
150                 sym::link_ordinal => self.check_link_ordinal(&attr, span, target),
151                 _ => true,
152             };
153             is_valid &= attr_is_valid;
154
155             // lint-only checks
156             match attr.name_or_empty() {
157                 sym::cold => self.check_cold(hir_id, attr, span, target),
158                 sym::link => self.check_link(hir_id, attr, span, target),
159                 sym::link_name => self.check_link_name(hir_id, attr, span, target),
160                 sym::link_section => self.check_link_section(hir_id, attr, span, target),
161                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
162                 sym::deprecated => self.check_deprecated(hir_id, attr, span, target),
163                 sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
164                 sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
165                 sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
166                 sym::macro_export => self.check_macro_export(hir_id, attr, target),
167                 sym::ignore | sym::should_panic | sym::proc_macro_derive => {
168                     self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
169                 }
170                 sym::automatically_derived => {
171                     self.check_generic_attr(hir_id, attr, target, &[Target::Impl])
172                 }
173                 sym::no_implicit_prelude => {
174                     self.check_generic_attr(hir_id, attr, target, &[Target::Mod])
175                 }
176                 sym::rustc_object_lifetime_default => {
177                     self.check_object_lifetime_default(hir_id, span)
178                 }
179                 _ => {}
180             }
181
182             let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name));
183
184             if hir_id != CRATE_HIR_ID {
185                 if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) =
186                     attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
187                 {
188                     match attr.style {
189                         ast::AttrStyle::Outer => self.tcx.emit_spanned_lint(
190                             UNUSED_ATTRIBUTES,
191                             hir_id,
192                             attr.span,
193                             errors::OuterCrateLevelAttr,
194                         ),
195                         ast::AttrStyle::Inner => self.tcx.emit_spanned_lint(
196                             UNUSED_ATTRIBUTES,
197                             hir_id,
198                             attr.span,
199                             errors::InnerCrateLevelAttr,
200                         ),
201                     }
202                 }
203             }
204
205             if let Some(BuiltinAttribute { duplicates, .. }) = builtin {
206                 check_duplicates(self.tcx, attr, hir_id, *duplicates, &mut seen);
207             }
208
209             self.check_unused_attribute(hir_id, attr)
210         }
211
212         if !is_valid {
213             return;
214         }
215
216         // FIXME(@lcnr): this doesn't belong here.
217         if matches!(
218             target,
219             Target::Closure
220                 | Target::Fn
221                 | Target::Method(_)
222                 | Target::ForeignFn
223                 | Target::ForeignStatic
224         ) {
225             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
226         }
227
228         self.check_repr(attrs, span, target, item, hir_id);
229         self.check_used(attrs, target);
230     }
231
232     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
233         self.tcx.emit_spanned_lint(
234             UNUSED_ATTRIBUTES,
235             hir_id,
236             attr.span,
237             errors::IgnoredAttrWithMacro { sym },
238         );
239     }
240
241     fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
242         self.tcx.emit_spanned_lint(
243             UNUSED_ATTRIBUTES,
244             hir_id,
245             attr.span,
246             errors::IgnoredAttr { sym },
247         );
248     }
249
250     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
251     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
252         match target {
253             Target::Fn
254             | Target::Closure
255             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
256             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
257                 self.tcx.emit_spanned_lint(
258                     UNUSED_ATTRIBUTES,
259                     hir_id,
260                     attr.span,
261                     errors::IgnoredInlineAttrFnProto,
262                 );
263                 true
264             }
265             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
266             // just a lint, because we previously erroneously allowed it and some crates used it
267             // accidentally, to to be compatible with crates depending on them, we can't throw an
268             // error here.
269             Target::AssocConst => {
270                 self.tcx.emit_spanned_lint(
271                     UNUSED_ATTRIBUTES,
272                     hir_id,
273                     attr.span,
274                     errors::IgnoredInlineAttrConstants,
275                 );
276                 true
277             }
278             // FIXME(#80564): Same for fields, arms, and macro defs
279             Target::Field | Target::Arm | Target::MacroDef => {
280                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
281                 true
282             }
283             _ => {
284                 self.tcx.sess.emit_err(errors::InlineNotFnOrClosure {
285                     attr_span: attr.span,
286                     defn_span: span,
287                 });
288                 false
289             }
290         }
291     }
292
293     /// Checks if a `#[no_coverage]` is applied directly to a function
294     fn check_no_coverage(
295         &self,
296         hir_id: HirId,
297         attr: &Attribute,
298         span: Span,
299         target: Target,
300     ) -> bool {
301         match target {
302             // no_coverage on function is fine
303             Target::Fn
304             | Target::Closure
305             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
306
307             // function prototypes can't be covered
308             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
309                 self.tcx.emit_spanned_lint(
310                     UNUSED_ATTRIBUTES,
311                     hir_id,
312                     attr.span,
313                     errors::IgnoredNoCoverageFnProto,
314                 );
315                 true
316             }
317
318             Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
319                 self.tcx.emit_spanned_lint(
320                     UNUSED_ATTRIBUTES,
321                     hir_id,
322                     attr.span,
323                     errors::IgnoredNoCoveragePropagate,
324                 );
325                 true
326             }
327
328             Target::Expression | Target::Statement | Target::Arm => {
329                 self.tcx.emit_spanned_lint(
330                     UNUSED_ATTRIBUTES,
331                     hir_id,
332                     attr.span,
333                     errors::IgnoredNoCoverageFnDefn,
334                 );
335                 true
336             }
337
338             _ => {
339                 self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable {
340                     attr_span: attr.span,
341                     defn_span: span,
342                 });
343                 false
344             }
345         }
346     }
347
348     fn check_generic_attr(
349         &self,
350         hir_id: HirId,
351         attr: &Attribute,
352         target: Target,
353         allowed_targets: &[Target],
354     ) {
355         if !allowed_targets.iter().any(|t| t == &target) {
356             let name = attr.name_or_empty();
357             let mut i = allowed_targets.iter();
358             // Pluralize
359             let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
360             let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
361                 if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
362                     b.push_str(", and ");
363                 } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
364                     b.push_str(" and ");
365                 } else {
366                     b.push_str(", ");
367                 }
368                 // Pluralize
369                 b.push_str(&(allowed_target.to_string() + "s"));
370                 b
371             });
372             self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
373                 lint.build(&format!("`#[{name}]` only has an effect on {}", supported_names))
374                     .emit();
375             });
376         }
377     }
378
379     /// Checks if `#[naked]` is applied to a function definition.
380     fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
381         match target {
382             Target::Fn
383             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
384             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
385             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
386             // erroneously allowed it and some crates used it accidentally, to to be compatible
387             // with crates depending on them, we can't throw an error here.
388             Target::Field | Target::Arm | Target::MacroDef => {
389                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
390                 true
391             }
392             _ => {
393                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
394                     attr_span: attr.span,
395                     defn_span: span,
396                 });
397                 false
398             }
399         }
400     }
401
402     /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition.
403     fn check_cmse_nonsecure_entry(&self, attr: &Attribute, span: Span, target: Target) -> bool {
404         match target {
405             Target::Fn
406             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
407             _ => {
408                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
409                     attr_span: attr.span,
410                     defn_span: span,
411                 });
412                 false
413             }
414         }
415     }
416
417     /// Debugging aid for `object_lifetime_default` query.
418     fn check_object_lifetime_default(&self, hir_id: HirId, span: Span) {
419         let tcx = self.tcx;
420         if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
421             let object_lifetime_default_reprs: String = generics
422                 .params
423                 .iter()
424                 .filter_map(|p| {
425                     let param_id = tcx.hir().local_def_id(p.hir_id);
426                     let default = tcx.object_lifetime_default(param_id)?;
427                     Some(match default {
428                         ObjectLifetimeDefault::Empty => "BaseDefault".to_owned(),
429                         ObjectLifetimeDefault::Static => "'static".to_owned(),
430                         ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
431                         ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
432                     })
433                 })
434                 .collect::<Vec<String>>()
435                 .join(",");
436
437             tcx.sess.span_err(span, &object_lifetime_default_reprs);
438         }
439     }
440
441     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
442     fn check_track_caller(
443         &self,
444         hir_id: HirId,
445         attr_span: Span,
446         attrs: &[Attribute],
447         span: Span,
448         target: Target,
449     ) -> bool {
450         match target {
451             _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
452                 self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span });
453                 false
454             }
455             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
456             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
457             // `#[track_caller]` attribute with just a lint, because we previously
458             // erroneously allowed it and some crates used it accidentally, to to be compatible
459             // with crates depending on them, we can't throw an error here.
460             Target::Field | Target::Arm | Target::MacroDef => {
461                 for attr in attrs {
462                     self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
463                 }
464                 true
465             }
466             _ => {
467                 self.tcx
468                     .sess
469                     .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span });
470                 false
471             }
472         }
473     }
474
475     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
476     fn check_non_exhaustive(
477         &self,
478         hir_id: HirId,
479         attr: &Attribute,
480         span: Span,
481         target: Target,
482     ) -> bool {
483         match target {
484             Target::Struct | Target::Enum | Target::Variant => true,
485             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
486             // `#[non_exhaustive]` attribute with just a lint, because we previously
487             // erroneously allowed it and some crates used it accidentally, to to be compatible
488             // with crates depending on them, we can't throw an error here.
489             Target::Field | Target::Arm | Target::MacroDef => {
490                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
491                 true
492             }
493             _ => {
494                 self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation {
495                     attr_span: attr.span,
496                     defn_span: span,
497                 });
498                 false
499             }
500         }
501     }
502
503     /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
504     fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
505         match target {
506             Target::Trait => true,
507             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
508             // `#[marker]` attribute with just a lint, because we previously
509             // erroneously allowed it and some crates used it accidentally, to to be compatible
510             // with crates depending on them, we can't throw an error here.
511             Target::Field | Target::Arm | Target::MacroDef => {
512                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
513                 true
514             }
515             _ => {
516                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
517                     attr_span: attr.span,
518                     defn_span: span,
519                 });
520                 false
521             }
522         }
523     }
524
525     /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid.
526     fn check_rustc_must_implement_one_of(
527         &self,
528         attr: &Attribute,
529         span: Span,
530         target: Target,
531     ) -> bool {
532         match target {
533             Target::Trait => true,
534             _ => {
535                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
536                     attr_span: attr.span,
537                     defn_span: span,
538                 });
539                 false
540             }
541         }
542     }
543
544     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
545     fn check_target_feature(
546         &self,
547         hir_id: HirId,
548         attr: &Attribute,
549         span: Span,
550         target: Target,
551     ) -> bool {
552         match target {
553             Target::Fn
554             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
555             // FIXME: #[target_feature] was previously erroneously allowed on statements and some
556             // crates used this, so only emit a warning.
557             Target::Statement => {
558                 self.tcx.emit_spanned_lint(
559                     UNUSED_ATTRIBUTES,
560                     hir_id,
561                     attr.span,
562                     errors::TargetFeatureOnStatement,
563                 );
564                 true
565             }
566             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
567             // `#[target_feature]` attribute with just a lint, because we previously
568             // erroneously allowed it and some crates used it accidentally, to to be compatible
569             // with crates depending on them, we can't throw an error here.
570             Target::Field | Target::Arm | Target::MacroDef => {
571                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
572                 true
573             }
574             _ => {
575                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
576                     attr_span: attr.span,
577                     defn_span: span,
578                 });
579                 false
580             }
581         }
582     }
583
584     /// Checks if the `#[thread_local]` attribute on `item` is valid. Returns `true` if valid.
585     fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) -> bool {
586         match target {
587             Target::ForeignStatic | Target::Static => true,
588             _ => {
589                 self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic {
590                     attr_span: attr.span,
591                     defn_span: span,
592                 });
593                 false
594             }
595         }
596     }
597
598     fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
599         self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name });
600     }
601
602     fn check_doc_alias_value(
603         &self,
604         meta: &NestedMetaItem,
605         doc_alias: Symbol,
606         hir_id: HirId,
607         target: Target,
608         is_list: bool,
609         aliases: &mut FxHashMap<String, Span>,
610     ) -> bool {
611         let tcx = self.tcx;
612         let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span());
613         let attr_str =
614             &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" });
615         if doc_alias == kw::Empty {
616             tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str });
617             return false;
618         }
619
620         let doc_alias_str = doc_alias.as_str();
621         if let Some(c) = doc_alias_str
622             .chars()
623             .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
624         {
625             tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c });
626             return false;
627         }
628         if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') {
629             tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str });
630             return false;
631         }
632
633         let span = meta.span();
634         if let Some(location) = match target {
635             Target::AssocTy => {
636                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
637                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
638                 if Target::from_item(containing_item) == Target::Impl {
639                     Some("type alias in implementation block")
640                 } else {
641                     None
642                 }
643             }
644             Target::AssocConst => {
645                 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
646                 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
647                 // We can't link to trait impl's consts.
648                 let err = "associated constant in trait implementation block";
649                 match containing_item.kind {
650                     ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
651                     _ => None,
652                 }
653             }
654             // we check the validity of params elsewhere
655             Target::Param => return false,
656             Target::Expression
657             | Target::Statement
658             | Target::Arm
659             | Target::ForeignMod
660             | Target::Closure
661             | Target::Impl => Some(target.name()),
662             Target::ExternCrate
663             | Target::Use
664             | Target::Static
665             | Target::Const
666             | Target::Fn
667             | Target::Mod
668             | Target::GlobalAsm
669             | Target::TyAlias
670             | Target::OpaqueTy
671             | Target::Enum
672             | Target::Variant
673             | Target::Struct
674             | Target::Field
675             | Target::Union
676             | Target::Trait
677             | Target::TraitAlias
678             | Target::Method(..)
679             | Target::ForeignFn
680             | Target::ForeignStatic
681             | Target::ForeignTy
682             | Target::GenericParam(..)
683             | Target::MacroDef
684             | Target::PatField
685             | Target::ExprField => None,
686         } {
687             tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location });
688             return false;
689         }
690         let item_name = self.tcx.hir().name(hir_id);
691         if item_name == doc_alias {
692             tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str });
693             return false;
694         }
695         if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) {
696             self.tcx.emit_spanned_lint(
697                 UNUSED_ATTRIBUTES,
698                 hir_id,
699                 span,
700                 errors::DocAliasDuplicated { first_defn: *entry.entry.get() },
701             );
702         }
703         true
704     }
705
706     fn check_doc_alias(
707         &self,
708         meta: &NestedMetaItem,
709         hir_id: HirId,
710         target: Target,
711         aliases: &mut FxHashMap<String, Span>,
712     ) -> bool {
713         if let Some(values) = meta.meta_item_list() {
714             let mut errors = 0;
715             for v in values {
716                 match v.literal() {
717                     Some(l) => match l.kind {
718                         LitKind::Str(s, _) => {
719                             if !self.check_doc_alias_value(v, s, hir_id, target, true, aliases) {
720                                 errors += 1;
721                             }
722                         }
723                         _ => {
724                             self.tcx
725                                 .sess
726                                 .emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
727                             errors += 1;
728                         }
729                     },
730                     None => {
731                         self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() });
732                         errors += 1;
733                     }
734                 }
735             }
736             errors == 0
737         } else if let Some(doc_alias) = meta.value_str() {
738             self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases)
739         } else {
740             self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() });
741             false
742         }
743     }
744
745     fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
746         let doc_keyword = meta.value_str().unwrap_or(kw::Empty);
747         if doc_keyword == kw::Empty {
748             self.doc_attr_str_error(meta, "keyword");
749             return false;
750         }
751         match self.tcx.hir().find(hir_id).and_then(|node| match node {
752             hir::Node::Item(item) => Some(&item.kind),
753             _ => None,
754         }) {
755             Some(ItemKind::Mod(ref module)) => {
756                 if !module.item_ids.is_empty() {
757                     self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() });
758                     return false;
759                 }
760             }
761             _ => {
762                 self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() });
763                 return false;
764             }
765         }
766         if !rustc_lexer::is_ident(doc_keyword.as_str()) {
767             self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent {
768                 span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
769                 doc_keyword,
770             });
771             return false;
772         }
773         true
774     }
775
776     fn check_doc_fake_variadic(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
777         match self.tcx.hir().find(hir_id).and_then(|node| match node {
778             hir::Node::Item(item) => Some(&item.kind),
779             _ => None,
780         }) {
781             Some(ItemKind::Impl(ref i)) => {
782                 let is_valid = matches!(&i.self_ty.kind, hir::TyKind::Tup([_]))
783                     || if let hir::TyKind::BareFn(bare_fn_ty) = &i.self_ty.kind {
784                         bare_fn_ty.decl.inputs.len() == 1
785                     } else {
786                         false
787                     };
788                 if !is_valid {
789                     self.tcx.sess.emit_err(errors::DocFakeVariadicNotValid { span: meta.span() });
790                     return false;
791                 }
792             }
793             _ => {
794                 self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() });
795                 return false;
796             }
797         }
798         true
799     }
800
801     /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
802     ///
803     /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
804     /// if there are conflicting attributes for one item.
805     ///
806     /// `specified_inline` is used to keep track of whether we have
807     /// already seen an inlining attribute for this item.
808     /// If so, `specified_inline` holds the value and the span of
809     /// the first `inline`/`no_inline` attribute.
810     fn check_doc_inline(
811         &self,
812         attr: &Attribute,
813         meta: &NestedMetaItem,
814         hir_id: HirId,
815         target: Target,
816         specified_inline: &mut Option<(bool, Span)>,
817     ) -> bool {
818         if target == Target::Use || target == Target::ExternCrate {
819             let do_inline = meta.name_or_empty() == sym::inline;
820             if let Some((prev_inline, prev_span)) = *specified_inline {
821                 if do_inline != prev_inline {
822                     let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
823                     spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first);
824                     spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second);
825                     self.tcx.sess.emit_err(errors::DocKeywordConflict { spans });
826                     return false;
827                 }
828                 true
829             } else {
830                 *specified_inline = Some((do_inline, meta.span()));
831                 true
832             }
833         } else {
834             self.tcx.emit_spanned_lint(
835                 INVALID_DOC_ATTRIBUTES,
836                 hir_id,
837                 meta.span(),
838                 errors::DocInlineOnlyUse {
839                     attr_span: meta.span(),
840                     item_span: (attr.style == AttrStyle::Outer)
841                         .then(|| self.tcx.hir().span(hir_id)),
842                 },
843             );
844             false
845         }
846     }
847
848     /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
849     fn check_attr_not_crate_level(
850         &self,
851         meta: &NestedMetaItem,
852         hir_id: HirId,
853         attr_name: &str,
854     ) -> bool {
855         if CRATE_HIR_ID == hir_id {
856             self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name });
857             return false;
858         }
859         true
860     }
861
862     /// Checks that an attribute is used at the crate level. Returns `true` if valid.
863     fn check_attr_crate_level(
864         &self,
865         attr: &Attribute,
866         meta: &NestedMetaItem,
867         hir_id: HirId,
868     ) -> bool {
869         if hir_id != CRATE_HIR_ID {
870             self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| {
871                 let mut err = lint.build(fluent::passes::attr_crate_level);
872                 if attr.style == AttrStyle::Outer
873                     && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID
874                 {
875                     if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) {
876                         src.insert(1, '!');
877                         err.span_suggestion_verbose(
878                             attr.span,
879                             fluent::passes::suggestion,
880                             src,
881                             Applicability::MaybeIncorrect,
882                         );
883                     } else {
884                         err.span_help(attr.span, fluent::passes::help);
885                     }
886                 }
887                 err.note(fluent::passes::note).emit();
888             });
889             return false;
890         }
891         true
892     }
893
894     /// Checks that `doc(test(...))` attribute contains only valid attributes. Returns `true` if
895     /// valid.
896     fn check_test_attr(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
897         let mut is_valid = true;
898         if let Some(metas) = meta.meta_item_list() {
899             for i_meta in metas {
900                 match i_meta.name_or_empty() {
901                     sym::attr | sym::no_crate_inject => {}
902                     _ => {
903                         self.tcx.emit_spanned_lint(
904                             INVALID_DOC_ATTRIBUTES,
905                             hir_id,
906                             i_meta.span(),
907                             errors::DocTestUnknown {
908                                 path: rustc_ast_pretty::pprust::path_to_string(
909                                     &i_meta.meta_item().unwrap().path,
910                                 ),
911                             },
912                         );
913                         is_valid = false;
914                     }
915                 }
916             }
917         } else {
918             self.tcx.emit_spanned_lint(
919                 INVALID_DOC_ATTRIBUTES,
920                 hir_id,
921                 meta.span(),
922                 errors::DocTestTakesList,
923             );
924             is_valid = false;
925         }
926         is_valid
927     }
928
929     /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
930     ///
931     /// `specified_inline` should be initialized to `None` and kept for the scope
932     /// of one item. Read the documentation of [`check_doc_inline`] for more information.
933     ///
934     /// [`check_doc_inline`]: Self::check_doc_inline
935     fn check_doc_attrs(
936         &self,
937         attr: &Attribute,
938         hir_id: HirId,
939         target: Target,
940         specified_inline: &mut Option<(bool, Span)>,
941         aliases: &mut FxHashMap<String, Span>,
942     ) -> bool {
943         let mut is_valid = true;
944
945         if let Some(mi) = attr.meta() && let Some(list) = mi.meta_item_list() {
946             for meta in list {
947                 if let Some(i_meta) = meta.meta_item() {
948                     match i_meta.name_or_empty() {
949                         sym::alias
950                             if !self.check_attr_not_crate_level(meta, hir_id, "alias")
951                                 || !self.check_doc_alias(meta, hir_id, target, aliases) =>
952                         {
953                             is_valid = false
954                         }
955
956                         sym::keyword
957                             if !self.check_attr_not_crate_level(meta, hir_id, "keyword")
958                                 || !self.check_doc_keyword(meta, hir_id) =>
959                         {
960                             is_valid = false
961                         }
962
963                         sym::fake_variadic
964                             if !self.check_attr_not_crate_level(meta, hir_id, "fake_variadic")
965                                 || !self.check_doc_fake_variadic(meta, hir_id) =>
966                         {
967                             is_valid = false
968                         }
969
970                         sym::html_favicon_url
971                         | sym::html_logo_url
972                         | sym::html_playground_url
973                         | sym::issue_tracker_base_url
974                         | sym::html_root_url
975                         | sym::html_no_source
976                         | sym::test
977                             if !self.check_attr_crate_level(attr, meta, hir_id) =>
978                         {
979                             is_valid = false;
980                         }
981
982                         sym::inline | sym::no_inline
983                             if !self.check_doc_inline(
984                                 attr,
985                                 meta,
986                                 hir_id,
987                                 target,
988                                 specified_inline,
989                             ) =>
990                         {
991                             is_valid = false;
992                         }
993
994                         // no_default_passes: deprecated
995                         // passes: deprecated
996                         // plugins: removed, but rustdoc warns about it itself
997                         sym::alias
998                         | sym::cfg
999                         | sym::cfg_hide
1000                         | sym::hidden
1001                         | sym::html_favicon_url
1002                         | sym::html_logo_url
1003                         | sym::html_no_source
1004                         | sym::html_playground_url
1005                         | sym::html_root_url
1006                         | sym::inline
1007                         | sym::issue_tracker_base_url
1008                         | sym::keyword
1009                         | sym::masked
1010                         | sym::no_default_passes
1011                         | sym::no_inline
1012                         | sym::notable_trait
1013                         | sym::passes
1014                         | sym::plugins
1015                         | sym::fake_variadic => {}
1016
1017                         sym::test => {
1018                             if !self.check_test_attr(meta, hir_id) {
1019                                 is_valid = false;
1020                             }
1021                         }
1022
1023                         sym::primitive => {
1024                             if !self.tcx.features().rustdoc_internals {
1025                                 self.tcx.emit_spanned_lint(
1026                                     INVALID_DOC_ATTRIBUTES,
1027                                     hir_id,
1028                                     i_meta.span,
1029                                     errors::DocPrimitive,
1030                                 );
1031                             }
1032                         }
1033
1034                         _ => {
1035                             let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path);
1036                             if i_meta.has_name(sym::spotlight) {
1037                                 self.tcx.emit_spanned_lint(
1038                                     INVALID_DOC_ATTRIBUTES,
1039                                     hir_id,
1040                                     i_meta.span,
1041                                     errors::DocTestUnknownSpotlight {
1042                                         path,
1043                                         span: i_meta.span
1044                                     }
1045                                 );
1046                             } else if i_meta.has_name(sym::include) &&
1047                                     let Some(value) = i_meta.value_str() {
1048                                 let applicability = if list.len() == 1 {
1049                                     Applicability::MachineApplicable
1050                                 } else {
1051                                     Applicability::MaybeIncorrect
1052                                 };
1053                                 // If there are multiple attributes, the suggestion would suggest
1054                                 // deleting all of them, which is incorrect.
1055                                 self.tcx.emit_spanned_lint(
1056                                     INVALID_DOC_ATTRIBUTES,
1057                                     hir_id,
1058                                     i_meta.span,
1059                                     errors::DocTestUnknownInclude {
1060                                         path,
1061                                         value: value.to_string(),
1062                                         inner: (attr.style == AttrStyle::Inner)
1063                                             .then_some("!")
1064                                             .unwrap_or(""),
1065                                         sugg: (attr.meta().unwrap().span, applicability),
1066                                     }
1067                                 );
1068                             } else {
1069                                 self.tcx.emit_spanned_lint(
1070                                     INVALID_DOC_ATTRIBUTES,
1071                                     hir_id,
1072                                     i_meta.span,
1073                                     errors::DocTestUnknownAny { path }
1074                                 );
1075                             }
1076                             is_valid = false;
1077                         }
1078                     }
1079                 } else {
1080                     self.tcx.emit_spanned_lint(
1081                         INVALID_DOC_ATTRIBUTES,
1082                         hir_id,
1083                         meta.span(),
1084                         errors::DocInvalid,
1085                     );
1086                     is_valid = false;
1087                 }
1088             }
1089         }
1090
1091         is_valid
1092     }
1093
1094     /// Warns against some misuses of `#[pass_by_value]`
1095     fn check_pass_by_value(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1096         match target {
1097             Target::Struct | Target::Enum | Target::TyAlias => true,
1098             _ => {
1099                 self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span });
1100                 false
1101             }
1102         }
1103     }
1104
1105     fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1106         match target {
1107             Target::Method(MethodKind::Inherent) => true,
1108             _ => {
1109                 self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span });
1110                 false
1111             }
1112         }
1113     }
1114
1115     fn check_has_incoherent_inherent_impls(
1116         &self,
1117         attr: &Attribute,
1118         span: Span,
1119         target: Target,
1120     ) -> bool {
1121         match target {
1122             Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
1123                 true
1124             }
1125             _ => {
1126                 self.tcx
1127                     .sess
1128                     .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span });
1129                 false
1130             }
1131         }
1132     }
1133
1134     /// Warns against some misuses of `#[must_use]`
1135     fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1136         let node = self.tcx.hir().get(hir_id);
1137         if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() {
1138             self.tcx.emit_spanned_lint(
1139                 UNUSED_ATTRIBUTES,
1140                 hir_id,
1141                 attr.span,
1142                 errors::MustUseAsync { span }
1143             );
1144         }
1145
1146         if !matches!(
1147             target,
1148             Target::Fn
1149                 | Target::Enum
1150                 | Target::Struct
1151                 | Target::Union
1152                 | Target::Method(_)
1153                 | Target::ForeignFn
1154                 // `impl Trait` in return position can trip
1155                 // `unused_must_use` if `Trait` is marked as
1156                 // `#[must_use]`
1157                 | Target::Trait
1158         ) {
1159             let article = match target {
1160                 Target::ExternCrate
1161                 | Target::OpaqueTy
1162                 | Target::Enum
1163                 | Target::Impl
1164                 | Target::Expression
1165                 | Target::Arm
1166                 | Target::AssocConst
1167                 | Target::AssocTy => "an",
1168                 _ => "a",
1169             };
1170
1171             self.tcx.emit_spanned_lint(
1172                 UNUSED_ATTRIBUTES,
1173                 hir_id,
1174                 attr.span,
1175                 errors::MustUseNoEffect { article, target },
1176             );
1177         }
1178
1179         // For now, its always valid
1180         true
1181     }
1182
1183     /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
1184     fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1185         match target {
1186             Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
1187             _ => {
1188                 self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span });
1189                 false
1190             }
1191         }
1192     }
1193
1194     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
1195     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1196         match target {
1197             Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
1198             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1199             // `#[cold]` attribute with just a lint, because we previously
1200             // erroneously allowed it and some crates used it accidentally, to to be compatible
1201             // with crates depending on them, we can't throw an error here.
1202             Target::Field | Target::Arm | Target::MacroDef => {
1203                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
1204             }
1205             _ => {
1206                 // FIXME: #[cold] was previously allowed on non-functions and some crates used
1207                 // this, so only emit a warning.
1208                 self.tcx.emit_spanned_lint(
1209                     UNUSED_ATTRIBUTES,
1210                     hir_id,
1211                     attr.span,
1212                     errors::Cold { span },
1213                 );
1214             }
1215         }
1216     }
1217
1218     /// Checks if `#[link]` is applied to an item other than a foreign module.
1219     fn check_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1220         if target == Target::ForeignMod
1221             && let hir::Node::Item(item) = self.tcx.hir().get(hir_id)
1222             && let Item { kind: ItemKind::ForeignMod { abi, .. }, .. } = item
1223             && !matches!(abi, Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic)
1224         {
1225             return;
1226         }
1227
1228         self.tcx.emit_spanned_lint(
1229             UNUSED_ATTRIBUTES,
1230             hir_id,
1231             attr.span,
1232             errors::Link { span: (target != Target::ForeignMod).then_some(span) },
1233         );
1234     }
1235
1236     /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
1237     fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1238         match target {
1239             Target::ForeignFn | Target::ForeignStatic => {}
1240             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1241             // `#[link_name]` attribute with just a lint, because we previously
1242             // erroneously allowed it and some crates used it accidentally, to to be compatible
1243             // with crates depending on them, we can't throw an error here.
1244             Target::Field | Target::Arm | Target::MacroDef => {
1245                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
1246             }
1247             _ => {
1248                 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
1249                 // used this, so only emit a warning.
1250                 let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span);
1251                 if let Some(s) = attr.value_str() {
1252                     self.tcx.emit_spanned_lint(
1253                         UNUSED_ATTRIBUTES,
1254                         hir_id,
1255                         attr.span,
1256                         errors::LinkName { span, attr_span, value: s.as_str() },
1257                     );
1258                 } else {
1259                     self.tcx.emit_spanned_lint(
1260                         UNUSED_ATTRIBUTES,
1261                         hir_id,
1262                         attr.span,
1263                         errors::LinkName { span, attr_span, value: "..." },
1264                     );
1265                 };
1266             }
1267         }
1268     }
1269
1270     /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
1271     fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
1272         match target {
1273             Target::ExternCrate => true,
1274             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1275             // `#[no_link]` attribute with just a lint, because we previously
1276             // erroneously allowed it and some crates used it accidentally, to to be compatible
1277             // with crates depending on them, we can't throw an error here.
1278             Target::Field | Target::Arm | Target::MacroDef => {
1279                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
1280                 true
1281             }
1282             _ => {
1283                 self.tcx.sess.emit_err(errors::NoLink { attr_span: attr.span, span });
1284                 false
1285             }
1286         }
1287     }
1288
1289     fn is_impl_item(&self, hir_id: HirId) -> bool {
1290         matches!(self.tcx.hir().get(hir_id), hir::Node::ImplItem(..))
1291     }
1292
1293     /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
1294     fn check_export_name(
1295         &self,
1296         hir_id: HirId,
1297         attr: &Attribute,
1298         span: Span,
1299         target: Target,
1300     ) -> bool {
1301         match target {
1302             Target::Static | Target::Fn => true,
1303             Target::Method(..) if self.is_impl_item(hir_id) => true,
1304             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1305             // `#[export_name]` attribute with just a lint, because we previously
1306             // erroneously allowed it and some crates used it accidentally, to to be compatible
1307             // with crates depending on them, we can't throw an error here.
1308             Target::Field | Target::Arm | Target::MacroDef => {
1309                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
1310                 true
1311             }
1312             _ => {
1313                 self.tcx.sess.emit_err(errors::ExportName { attr_span: attr.span, span });
1314                 false
1315             }
1316         }
1317     }
1318
1319     fn check_rustc_layout_scalar_valid_range(
1320         &self,
1321         attr: &Attribute,
1322         span: Span,
1323         target: Target,
1324     ) -> bool {
1325         if target != Target::Struct {
1326             self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeNotStruct {
1327                 attr_span: attr.span,
1328                 span,
1329             });
1330             return false;
1331         }
1332
1333         let Some(list) = attr.meta_item_list() else {
1334             return false;
1335         };
1336
1337         if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
1338             true
1339         } else {
1340             self.tcx.sess.emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span });
1341             false
1342         }
1343     }
1344
1345     /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1346     fn check_rustc_legacy_const_generics(
1347         &self,
1348         attr: &Attribute,
1349         span: Span,
1350         target: Target,
1351         item: Option<ItemLike<'_>>,
1352     ) -> bool {
1353         let is_function = matches!(target, Target::Fn);
1354         if !is_function {
1355             self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1356                 attr_span: attr.span,
1357                 defn_span: span,
1358             });
1359             return false;
1360         }
1361
1362         let Some(list) = attr.meta_item_list() else {
1363             // The attribute form is validated on AST.
1364             return false;
1365         };
1366
1367         let Some(ItemLike::Item(Item {
1368             kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1369             ..
1370         }))  = item else {
1371             bug!("should be a function item");
1372         };
1373
1374         for param in generics.params {
1375             match param.kind {
1376                 hir::GenericParamKind::Const { .. } => {}
1377                 _ => {
1378                     self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsOnly {
1379                         attr_span: attr.span,
1380                         param_span: param.span,
1381                     });
1382                     return false;
1383                 }
1384             }
1385         }
1386
1387         if list.len() != generics.params.len() {
1388             self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndex {
1389                 attr_span: attr.span,
1390                 generics_span: generics.span,
1391             });
1392             return false;
1393         }
1394
1395         let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1396         let mut invalid_args = vec![];
1397         for meta in list {
1398             if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1399                 if *val >= arg_count {
1400                     let span = meta.span();
1401                     self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexExceed {
1402                         span,
1403                         arg_count: arg_count as usize,
1404                     });
1405                     return false;
1406                 }
1407             } else {
1408                 invalid_args.push(meta.span());
1409             }
1410         }
1411
1412         if !invalid_args.is_empty() {
1413             self.tcx.sess.emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args });
1414             false
1415         } else {
1416             true
1417         }
1418     }
1419
1420     /// Helper function for checking that the provided attribute is only applied to a function or
1421     /// method.
1422     fn check_applied_to_fn_or_method(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1423         let is_function = matches!(target, Target::Fn | Target::Method(..));
1424         if !is_function {
1425             self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn {
1426                 attr_span: attr.span,
1427                 defn_span: span,
1428             });
1429             false
1430         } else {
1431             true
1432         }
1433     }
1434
1435     /// Checks that the `#[rustc_lint_query_instability]` attribute is only applied to a function
1436     /// or method.
1437     fn check_rustc_lint_query_instability(
1438         &self,
1439         attr: &Attribute,
1440         span: Span,
1441         target: Target,
1442     ) -> bool {
1443         self.check_applied_to_fn_or_method(attr, span, target)
1444     }
1445
1446     /// Checks that the `#[rustc_lint_diagnostics]` attribute is only applied to a function or
1447     /// method.
1448     fn check_rustc_lint_diagnostics(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1449         self.check_applied_to_fn_or_method(attr, span, target)
1450     }
1451
1452     /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
1453     fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
1454         match target {
1455             Target::Struct => true,
1456             _ => {
1457                 self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
1458                 false
1459             }
1460         }
1461     }
1462
1463     /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
1464     fn check_rustc_lint_opt_deny_field_access(
1465         &self,
1466         attr: &Attribute,
1467         span: Span,
1468         target: Target,
1469     ) -> bool {
1470         match target {
1471             Target::Field => true,
1472             _ => {
1473                 self.tcx
1474                     .sess
1475                     .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
1476                 false
1477             }
1478         }
1479     }
1480
1481     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1482     /// option is passed to the compiler.
1483     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1484         if self.tcx.sess.opts.unstable_opts.query_dep_graph {
1485             true
1486         } else {
1487             self.tcx.sess.emit_err(errors::RustcDirtyClean { span: attr.span });
1488             false
1489         }
1490     }
1491
1492     /// Checks if `#[link_section]` is applied to a function or static.
1493     fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1494         match target {
1495             Target::Static | Target::Fn | Target::Method(..) => {}
1496             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1497             // `#[link_section]` attribute with just a lint, because we previously
1498             // erroneously allowed it and some crates used it accidentally, to to be compatible
1499             // with crates depending on them, we can't throw an error here.
1500             Target::Field | Target::Arm | Target::MacroDef => {
1501                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1502             }
1503             _ => {
1504                 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1505                 // crates used this, so only emit a warning.
1506                 self.tcx.emit_spanned_lint(
1507                     UNUSED_ATTRIBUTES,
1508                     hir_id,
1509                     attr.span,
1510                     errors::LinkSection { span },
1511                 );
1512             }
1513         }
1514     }
1515
1516     /// Checks if `#[no_mangle]` is applied to a function or static.
1517     fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
1518         match target {
1519             Target::Static | Target::Fn => {}
1520             Target::Method(..) if self.is_impl_item(hir_id) => {}
1521             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1522             // `#[no_mangle]` attribute with just a lint, because we previously
1523             // erroneously allowed it and some crates used it accidentally, to to be compatible
1524             // with crates depending on them, we can't throw an error here.
1525             Target::Field | Target::Arm | Target::MacroDef => {
1526                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1527             }
1528             // FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
1529             // The error should specify that the item that is wrong is specifically a *foreign* fn/static
1530             // otherwise the error seems odd
1531             Target::ForeignFn | Target::ForeignStatic => {
1532                 let foreign_item_kind = match target {
1533                     Target::ForeignFn => "function",
1534                     Target::ForeignStatic => "static",
1535                     _ => unreachable!(),
1536                 };
1537                 self.tcx.emit_spanned_lint(
1538                     UNUSED_ATTRIBUTES,
1539                     hir_id,
1540                     attr.span,
1541                     errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind },
1542                 );
1543             }
1544             _ => {
1545                 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1546                 // crates used this, so only emit a warning.
1547                 self.tcx.emit_spanned_lint(
1548                     UNUSED_ATTRIBUTES,
1549                     hir_id,
1550                     attr.span,
1551                     errors::NoMangle { span },
1552                 );
1553             }
1554         }
1555     }
1556
1557     /// Checks if the `#[repr]` attributes on `item` are valid.
1558     fn check_repr(
1559         &self,
1560         attrs: &[Attribute],
1561         span: Span,
1562         target: Target,
1563         item: Option<ItemLike<'_>>,
1564         hir_id: HirId,
1565     ) {
1566         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1567         // ```
1568         // #[repr(foo)]
1569         // #[repr(bar, align(8))]
1570         // ```
1571         let hints: Vec<_> = attrs
1572             .iter()
1573             .filter(|attr| attr.has_name(sym::repr))
1574             .filter_map(|attr| attr.meta_item_list())
1575             .flatten()
1576             .collect();
1577
1578         let mut int_reprs = 0;
1579         let mut is_c = false;
1580         let mut is_simd = false;
1581         let mut is_transparent = false;
1582
1583         for hint in &hints {
1584             if !hint.is_meta_item() {
1585                 self.tcx.sess.emit_err(errors::ReprIdent { span: hint.span() });
1586                 continue;
1587             }
1588
1589             let (article, allowed_targets) = match hint.name_or_empty() {
1590                 sym::C => {
1591                     is_c = true;
1592                     match target {
1593                         Target::Struct | Target::Union | Target::Enum => continue,
1594                         _ => ("a", "struct, enum, or union"),
1595                     }
1596                 }
1597                 sym::align => {
1598                     if let (Target::Fn, false) = (target, self.tcx.features().fn_align) {
1599                         feature_err(
1600                             &self.tcx.sess.parse_sess,
1601                             sym::fn_align,
1602                             hint.span(),
1603                             "`repr(align)` attributes on functions are unstable",
1604                         )
1605                         .emit();
1606                     }
1607
1608                     match target {
1609                         Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1610                         _ => ("a", "struct, enum, function, or union"),
1611                     }
1612                 }
1613                 sym::packed => {
1614                     if target != Target::Struct && target != Target::Union {
1615                         ("a", "struct or union")
1616                     } else {
1617                         continue;
1618                     }
1619                 }
1620                 sym::simd => {
1621                     is_simd = true;
1622                     if target != Target::Struct {
1623                         ("a", "struct")
1624                     } else {
1625                         continue;
1626                     }
1627                 }
1628                 sym::transparent => {
1629                     is_transparent = true;
1630                     match target {
1631                         Target::Struct | Target::Union | Target::Enum => continue,
1632                         _ => ("a", "struct, enum, or union"),
1633                     }
1634                 }
1635                 sym::i8
1636                 | sym::u8
1637                 | sym::i16
1638                 | sym::u16
1639                 | sym::i32
1640                 | sym::u32
1641                 | sym::i64
1642                 | sym::u64
1643                 | sym::i128
1644                 | sym::u128
1645                 | sym::isize
1646                 | sym::usize => {
1647                     int_reprs += 1;
1648                     if target != Target::Enum {
1649                         ("an", "enum")
1650                     } else {
1651                         continue;
1652                     }
1653                 }
1654                 _ => {
1655                     struct_span_err!(
1656                         self.tcx.sess,
1657                         hint.span(),
1658                         E0552,
1659                         "unrecognized representation hint"
1660                     )
1661                     .emit();
1662
1663                     continue;
1664                 }
1665             };
1666
1667             struct_span_err!(
1668                 self.tcx.sess,
1669                 hint.span(),
1670                 E0517,
1671                 "{}",
1672                 &format!("attribute should be applied to {article} {allowed_targets}")
1673             )
1674             .span_label(span, &format!("not {article} {allowed_targets}"))
1675             .emit();
1676         }
1677
1678         // Just point at all repr hints if there are any incompatibilities.
1679         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
1680         let hint_spans = hints.iter().map(|hint| hint.span());
1681
1682         // Error on repr(transparent, <anything else>).
1683         if is_transparent && hints.len() > 1 {
1684             let hint_spans: Vec<_> = hint_spans.clone().collect();
1685             struct_span_err!(
1686                 self.tcx.sess,
1687                 hint_spans,
1688                 E0692,
1689                 "transparent {} cannot have other repr hints",
1690                 target
1691             )
1692             .emit();
1693         }
1694         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1695         if (int_reprs > 1)
1696             || (is_simd && is_c)
1697             || (int_reprs == 1
1698                 && is_c
1699                 && item.map_or(false, |item| {
1700                     if let ItemLike::Item(item) = item {
1701                         return is_c_like_enum(item);
1702                     }
1703                     return false;
1704                 }))
1705         {
1706             self.tcx.emit_spanned_lint(
1707                 CONFLICTING_REPR_HINTS,
1708                 hir_id,
1709                 hint_spans.collect::<Vec<Span>>(),
1710                 errors::ReprConflicting,
1711             );
1712         }
1713     }
1714
1715     fn check_used(&self, attrs: &[Attribute], target: Target) {
1716         let mut used_linker_span = None;
1717         let mut used_compiler_span = None;
1718         for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
1719             if target != Target::Static {
1720                 self.tcx.sess.emit_err(errors::UsedStatic { span: attr.span });
1721             }
1722             let inner = attr.meta_item_list();
1723             match inner.as_deref() {
1724                 Some([item]) if item.has_name(sym::linker) => {
1725                     if used_linker_span.is_none() {
1726                         used_linker_span = Some(attr.span);
1727                     }
1728                 }
1729                 Some([item]) if item.has_name(sym::compiler) => {
1730                     if used_compiler_span.is_none() {
1731                         used_compiler_span = Some(attr.span);
1732                     }
1733                 }
1734                 Some(_) => {
1735                     // This error case is handled in rustc_typeck::collect.
1736                 }
1737                 None => {
1738                     // Default case (compiler) when arg isn't defined.
1739                     if used_compiler_span.is_none() {
1740                         used_compiler_span = Some(attr.span);
1741                     }
1742                 }
1743             }
1744         }
1745         if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
1746             self.tcx
1747                 .sess
1748                 .emit_err(errors::UsedCompilerLinker { spans: vec![linker_span, compiler_span] });
1749         }
1750     }
1751
1752     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1753     /// (Allows proc_macro functions)
1754     fn check_allow_internal_unstable(
1755         &self,
1756         hir_id: HirId,
1757         attr: &Attribute,
1758         span: Span,
1759         target: Target,
1760         attrs: &[Attribute],
1761     ) -> bool {
1762         debug!("Checking target: {:?}", target);
1763         match target {
1764             Target::Fn => {
1765                 for attr in attrs {
1766                     if self.tcx.sess.is_proc_macro_attr(attr) {
1767                         debug!("Is proc macro attr");
1768                         return true;
1769                     }
1770                 }
1771                 debug!("Is not proc macro attr");
1772                 false
1773             }
1774             Target::MacroDef => true,
1775             // FIXME(#80564): We permit struct fields and match arms to have an
1776             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1777             // erroneously allowed it and some crates used it accidentally, to to be compatible
1778             // with crates depending on them, we can't throw an error here.
1779             Target::Field | Target::Arm => {
1780                 self.inline_attr_str_error_without_macro_def(
1781                     hir_id,
1782                     attr,
1783                     "allow_internal_unstable",
1784                 );
1785                 true
1786             }
1787             _ => {
1788                 self.tcx
1789                     .sess
1790                     .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span });
1791                 false
1792             }
1793         }
1794     }
1795
1796     /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
1797     fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
1798         match target {
1799             Target::Mod => {}
1800             _ => {
1801                 self.tcx.sess.emit_err(errors::DebugVisualizerPlacement { span: attr.span });
1802                 return false;
1803             }
1804         }
1805
1806         let Some(hints) = attr.meta_item_list() else {
1807             self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1808             return false;
1809         };
1810
1811         let hint = match hints.len() {
1812             1 => &hints[0],
1813             _ => {
1814                 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1815                 return false;
1816             }
1817         };
1818
1819         let Some(meta_item) = hint.meta_item() else {
1820             self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: attr.span });
1821             return false;
1822         };
1823
1824         let visualizer_path = match (meta_item.name_or_empty(), meta_item.value_str()) {
1825             (sym::natvis_file, Some(value)) => value,
1826             (sym::gdb_script_file, Some(value)) => value,
1827             (_, _) => {
1828                 self.tcx.sess.emit_err(errors::DebugVisualizerInvalid { span: meta_item.span });
1829                 return false;
1830             }
1831         };
1832
1833         let file =
1834             match resolve_path(&self.tcx.sess.parse_sess, visualizer_path.as_str(), attr.span) {
1835                 Ok(file) => file,
1836                 Err(mut err) => {
1837                     err.emit();
1838                     return false;
1839                 }
1840             };
1841
1842         match std::fs::File::open(&file) {
1843             Ok(_) => true,
1844             Err(err) => {
1845                 self.tcx
1846                     .sess
1847                     .struct_span_err(
1848                         meta_item.span,
1849                         &format!("couldn't read {}: {}", file.display(), err),
1850                     )
1851                     .emit();
1852                 false
1853             }
1854         }
1855     }
1856
1857     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1858     /// (Allows proc_macro functions)
1859     fn check_rustc_allow_const_fn_unstable(
1860         &self,
1861         hir_id: HirId,
1862         attr: &Attribute,
1863         span: Span,
1864         target: Target,
1865     ) -> bool {
1866         match target {
1867             Target::Fn | Target::Method(_)
1868                 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id).to_def_id()) =>
1869             {
1870                 true
1871             }
1872             // FIXME(#80564): We permit struct fields and match arms to have an
1873             // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1874             // erroneously allowed it and some crates used it accidentally, to to be compatible
1875             // with crates depending on them, we can't throw an error here.
1876             Target::Field | Target::Arm | Target::MacroDef => {
1877                 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1878                 true
1879             }
1880             _ => {
1881                 self.tcx
1882                     .sess
1883                     .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span });
1884                 false
1885             }
1886         }
1887     }
1888
1889     fn check_rustc_std_internal_symbol(
1890         &self,
1891         attr: &Attribute,
1892         span: Span,
1893         target: Target,
1894     ) -> bool {
1895         match target {
1896             Target::Fn | Target::Static => true,
1897             _ => {
1898                 self.tcx
1899                     .sess
1900                     .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span });
1901                 false
1902             }
1903         }
1904     }
1905
1906     /// `#[const_trait]` only applies to traits.
1907     fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1908         match target {
1909             Target::Trait => true,
1910             _ => {
1911                 self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span });
1912                 false
1913             }
1914         }
1915     }
1916
1917     fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1918         match target {
1919             Target::Expression => {
1920                 self.tcx.sess.emit_err(errors::StabilityPromotable { attr_span: attr.span });
1921                 false
1922             }
1923             _ => true,
1924         }
1925     }
1926
1927     fn check_link_ordinal(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
1928         match target {
1929             Target::ForeignFn | Target::ForeignStatic => true,
1930             _ => {
1931                 self.tcx.sess.emit_err(errors::LinkOrdinal { attr_span: attr.span });
1932                 false
1933             }
1934         }
1935     }
1936
1937     fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: Span, target: Target) {
1938         match target {
1939             Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
1940                 self.tcx.emit_spanned_lint(
1941                     UNUSED_ATTRIBUTES,
1942                     hir_id,
1943                     attr.span,
1944                     errors::Deprecated,
1945                 );
1946             }
1947             _ => {}
1948         }
1949     }
1950
1951     fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1952         let name = attr.name_or_empty();
1953         match target {
1954             Target::ExternCrate | Target::Mod => {}
1955             _ => {
1956                 self.tcx.emit_spanned_lint(
1957                     UNUSED_ATTRIBUTES,
1958                     hir_id,
1959                     attr.span,
1960                     errors::MacroUse { name },
1961                 );
1962             }
1963         }
1964     }
1965
1966     fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1967         if target != Target::MacroDef {
1968             self.tcx.emit_spanned_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::MacroExport);
1969         }
1970     }
1971
1972     fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
1973         if target != Target::Fn {
1974             self.tcx.emit_spanned_lint(
1975                 UNUSED_ATTRIBUTES,
1976                 hir_id,
1977                 attr.span,
1978                 errors::PluginRegistrar,
1979             );
1980         }
1981     }
1982
1983     fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute) {
1984         // Warn on useless empty attributes.
1985         let note = if matches!(
1986             attr.name_or_empty(),
1987             sym::macro_use
1988                 | sym::allow
1989                 | sym::expect
1990                 | sym::warn
1991                 | sym::deny
1992                 | sym::forbid
1993                 | sym::feature
1994                 | sym::repr
1995                 | sym::target_feature
1996         ) && attr.meta_item_list().map_or(false, |list| list.is_empty())
1997         {
1998             errors::UnusedNote::EmptyList { name: attr.name_or_empty() }
1999         } else if matches!(
2000                 attr.name_or_empty(),
2001                 sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
2002             ) && let Some(meta) = attr.meta_item_list()
2003             && meta.len() == 1
2004             && let Some(item) = meta[0].meta_item()
2005             && let MetaItemKind::NameValue(_) = &item.kind
2006             && item.path == sym::reason
2007         {
2008             errors::UnusedNote::NoLints { name: attr.name_or_empty() }
2009         } else if attr.name_or_empty() == sym::default_method_body_is_const {
2010             errors::UnusedNote::DefaultMethodBodyConst
2011         } else {
2012             return;
2013         };
2014
2015         self.tcx.emit_spanned_lint(
2016             UNUSED_ATTRIBUTES,
2017             hir_id,
2018             attr.span,
2019             errors::Unused { attr_span: attr.span, note },
2020         );
2021     }
2022 }
2023
2024 impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
2025     type NestedFilter = nested_filter::OnlyBodies;
2026
2027     fn nested_visit_map(&mut self) -> Self::Map {
2028         self.tcx.hir()
2029     }
2030
2031     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
2032         // Historically we've run more checks on non-exported than exported macros,
2033         // so this lets us continue to run them while maintaining backwards compatibility.
2034         // In the long run, the checks should be harmonized.
2035         if let ItemKind::Macro(ref macro_def, _) = item.kind {
2036             let def_id = item.def_id.to_def_id();
2037             if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
2038                 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
2039             }
2040         }
2041
2042         let target = Target::from_item(item);
2043         self.check_attributes(item.hir_id(), item.span, target, Some(ItemLike::Item(item)));
2044         intravisit::walk_item(self, item)
2045     }
2046
2047     fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
2048         let target = Target::from_generic_param(generic_param);
2049         self.check_attributes(generic_param.hir_id, generic_param.span, target, None);
2050         intravisit::walk_generic_param(self, generic_param)
2051     }
2052
2053     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
2054         let target = Target::from_trait_item(trait_item);
2055         self.check_attributes(trait_item.hir_id(), trait_item.span, target, None);
2056         intravisit::walk_trait_item(self, trait_item)
2057     }
2058
2059     fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
2060         self.check_attributes(struct_field.hir_id, struct_field.span, Target::Field, None);
2061         intravisit::walk_field_def(self, struct_field);
2062     }
2063
2064     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
2065         self.check_attributes(arm.hir_id, arm.span, Target::Arm, None);
2066         intravisit::walk_arm(self, arm);
2067     }
2068
2069     fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
2070         let target = Target::from_foreign_item(f_item);
2071         self.check_attributes(f_item.hir_id(), f_item.span, target, Some(ItemLike::ForeignItem));
2072         intravisit::walk_foreign_item(self, f_item)
2073     }
2074
2075     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
2076         let target = target_from_impl_item(self.tcx, impl_item);
2077         self.check_attributes(impl_item.hir_id(), impl_item.span, target, None);
2078         intravisit::walk_impl_item(self, impl_item)
2079     }
2080
2081     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
2082         // When checking statements ignore expressions, they will be checked later.
2083         if let hir::StmtKind::Local(ref l) = stmt.kind {
2084             self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
2085         }
2086         intravisit::walk_stmt(self, stmt)
2087     }
2088
2089     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
2090         let target = match expr.kind {
2091             hir::ExprKind::Closure { .. } => Target::Closure,
2092             _ => Target::Expression,
2093         };
2094
2095         self.check_attributes(expr.hir_id, expr.span, target, None);
2096         intravisit::walk_expr(self, expr)
2097     }
2098
2099     fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
2100         self.check_attributes(field.hir_id, field.span, Target::ExprField, None);
2101         intravisit::walk_expr_field(self, field)
2102     }
2103
2104     fn visit_variant(&mut self, variant: &'tcx hir::Variant<'tcx>) {
2105         self.check_attributes(variant.id, variant.span, Target::Variant, None);
2106         intravisit::walk_variant(self, variant)
2107     }
2108
2109     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
2110         self.check_attributes(param.hir_id, param.span, Target::Param, None);
2111
2112         intravisit::walk_param(self, param);
2113     }
2114
2115     fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
2116         self.check_attributes(field.hir_id, field.span, Target::PatField, None);
2117         intravisit::walk_pat_field(self, field);
2118     }
2119 }
2120
2121 fn is_c_like_enum(item: &Item<'_>) -> bool {
2122     if let ItemKind::Enum(ref def, _) = item.kind {
2123         for variant in def.variants {
2124             match variant.data {
2125                 hir::VariantData::Unit(..) => { /* continue */ }
2126                 _ => return false,
2127             }
2128         }
2129         true
2130     } else {
2131         false
2132     }
2133 }
2134
2135 // FIXME: Fix "Cannot determine resolution" error and remove built-in macros
2136 // from this check.
2137 fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
2138     // Check for builtin attributes at the crate level
2139     // which were unsuccessfully resolved due to cannot determine
2140     // resolution for the attribute macro error.
2141     const ATTRS_TO_CHECK: &[Symbol] = &[
2142         sym::macro_export,
2143         sym::repr,
2144         sym::path,
2145         sym::automatically_derived,
2146         sym::start,
2147         sym::rustc_main,
2148         sym::derive,
2149         sym::test,
2150         sym::test_case,
2151         sym::global_allocator,
2152         sym::bench,
2153     ];
2154
2155     for attr in attrs {
2156         // This function should only be called with crate attributes
2157         // which are inner attributes always but lets check to make sure
2158         if attr.style == AttrStyle::Inner {
2159             for attr_to_check in ATTRS_TO_CHECK {
2160                 if attr.has_name(*attr_to_check) {
2161                     let mut err = tcx.sess.struct_span_err(
2162                         attr.span,
2163                         &format!(
2164                             "`{}` attribute cannot be used at crate level",
2165                             attr_to_check.to_ident_string()
2166                         ),
2167                     );
2168                     // Only emit an error with a suggestion if we can create a
2169                     // string out of the attribute span
2170                     if let Ok(src) = tcx.sess.source_map().span_to_snippet(attr.span) {
2171                         let replacement = src.replace("#!", "#");
2172                         err.span_suggestion_verbose(
2173                             attr.span,
2174                             "perhaps you meant to use an outer attribute",
2175                             replacement,
2176                             rustc_errors::Applicability::MachineApplicable,
2177                         );
2178                     }
2179                     err.emit();
2180                 }
2181             }
2182         }
2183     }
2184 }
2185
2186 fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) {
2187     let attrs = tcx.hir().attrs(item.hir_id());
2188
2189     for attr in attrs {
2190         if attr.has_name(sym::inline) {
2191             tcx.sess.emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span });
2192         }
2193     }
2194 }
2195
2196 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
2197     let check_attr_visitor = &mut CheckAttrVisitor { tcx };
2198     tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
2199     if module_def_id.is_top_level_module() {
2200         check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
2201         check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
2202     }
2203 }
2204
2205 pub(crate) fn provide(providers: &mut Providers) {
2206     *providers = Providers { check_mod_attrs, ..*providers };
2207 }
2208
2209 fn check_duplicates(
2210     tcx: TyCtxt<'_>,
2211     attr: &Attribute,
2212     hir_id: HirId,
2213     duplicates: AttributeDuplicates,
2214     seen: &mut FxHashMap<Symbol, Span>,
2215 ) {
2216     use AttributeDuplicates::*;
2217     if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() {
2218         return;
2219     }
2220     match duplicates {
2221         DuplicatesOk => {}
2222         WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2223             match seen.entry(attr.name_or_empty()) {
2224                 Entry::Occupied(mut entry) => {
2225                     let (this, other) = if matches!(duplicates, FutureWarnPreceding) {
2226                         let to_remove = entry.insert(attr.span);
2227                         (to_remove, attr.span)
2228                     } else {
2229                         (attr.span, *entry.get())
2230                     };
2231                     tcx.emit_spanned_lint(
2232                         UNUSED_ATTRIBUTES,
2233                         hir_id,
2234                         this,
2235                         errors::UnusedDuplicate {
2236                             this,
2237                             other,
2238                             warning: matches!(
2239                                 duplicates,
2240                                 FutureWarnFollowing | FutureWarnPreceding
2241                             )
2242                             .then_some(()),
2243                         },
2244                     );
2245                 }
2246                 Entry::Vacant(entry) => {
2247                     entry.insert(attr.span);
2248                 }
2249             }
2250         }
2251         ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) {
2252             Entry::Occupied(mut entry) => {
2253                 let (this, other) = if matches!(duplicates, ErrorPreceding) {
2254                     let to_remove = entry.insert(attr.span);
2255                     (to_remove, attr.span)
2256                 } else {
2257                     (attr.span, *entry.get())
2258                 };
2259                 tcx.sess.emit_err(errors::UnusedMultiple {
2260                     this,
2261                     other,
2262                     name: attr.name_or_empty(),
2263                 });
2264             }
2265             Entry::Vacant(entry) => {
2266                 entry.insert(attr.span);
2267             }
2268         },
2269     }
2270 }