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