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