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