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