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