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