]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_passes/src/check_attr.rs
New upstream version 1.55.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
17df50a5 11use rustc_ast::{AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
cdc7bbd5 12use rustc_errors::{pluralize, struct_span_err, Applicability};
dfeec247 13use rustc_hir as hir;
f035d41b 14use rustc_hir::def_id::LocalDefId;
dfeec247 15use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
17df50a5 16use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
74b04a01 17use rustc_hir::{MethodKind, Target};
6a06907d
XL
18use rustc_session::lint::builtin::{
19 CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
20};
74b04a01 21use rustc_session::parse::feature_err;
29967ef6 22use rustc_span::symbol::{sym, Symbol};
17df50a5 23use rustc_span::{MultiSpan, Span, DUMMY_SP};
b039eaaf 24
f035d41b
XL
25pub(crate) fn target_from_impl_item<'tcx>(
26 tcx: TyCtxt<'tcx>,
27 impl_item: &hir::ImplItem<'_>,
28) -> Target {
74b04a01
XL
29 match impl_item.kind {
30 hir::ImplItemKind::Const(..) => Target::AssocConst,
ba9703b0 31 hir::ImplItemKind::Fn(..) => {
6a06907d 32 let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id());
74b04a01
XL
33 let containing_item = tcx.hir().expect_item(parent_hir_id);
34 let containing_impl_is_for_trait = match &containing_item.kind {
5869c6ff 35 hir::ItemKind::Impl(impl_) => impl_.of_trait.is_some(),
74b04a01
XL
36 _ => bug!("parent of an ImplItem must be an Impl"),
37 };
38 if containing_impl_is_for_trait {
e74abb32 39 Target::Method(MethodKind::Trait { body: true })
74b04a01
XL
40 } else {
41 Target::Method(MethodKind::Inherent)
e74abb32 42 }
b039eaaf 43 }
f035d41b 44 hir::ImplItemKind::TyAlias(..) => Target::AssocTy,
b039eaaf
SL
45 }
46}
47
1b1a35ee
XL
48#[derive(Clone, Copy)]
49enum ItemLike<'tcx> {
50 Item(&'tcx Item<'tcx>),
51 ForeignItem(&'tcx ForeignItem<'tcx>),
52}
53
dc9dc135
XL
54struct CheckAttrVisitor<'tcx> {
55 tcx: TyCtxt<'tcx>,
b039eaaf
SL
56}
57
dc9dc135 58impl CheckAttrVisitor<'tcx> {
9fa01778 59 /// Checks any attribute.
e74abb32
XL
60 fn check_attributes(
61 &self,
62 hir_id: HirId,
e74abb32
XL
63 span: &Span,
64 target: Target,
1b1a35ee 65 item: Option<ItemLike<'_>>,
e74abb32
XL
66 ) {
67 let mut is_valid = true;
17df50a5 68 let mut specified_inline = None;
6a06907d 69 let attrs = self.tcx.hir().attrs(hir_id);
e74abb32 70 for attr in attrs {
cdc7bbd5
XL
71 is_valid &= match attr.name_or_empty() {
72 sym::inline => self.check_inline(hir_id, attr, span, target),
73 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
74 sym::marker => self.check_marker(hir_id, attr, span, target),
75 sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
76 sym::track_caller => {
77 self.check_track_caller(hir_id, &attr.span, attrs, span, target)
1b1a35ee 78 }
17df50a5 79 sym::doc => self.check_doc_attrs(attr, hir_id, target, &mut specified_inline),
cdc7bbd5
XL
80 sym::no_link => self.check_no_link(hir_id, &attr, span, target),
81 sym::export_name => self.check_export_name(hir_id, &attr, span, target),
cdc7bbd5
XL
82 sym::rustc_layout_scalar_valid_range_start
83 | sym::rustc_layout_scalar_valid_range_end => {
84 self.check_rustc_layout_scalar_valid_range(&attr, span, target)
85 }
86 sym::allow_internal_unstable => {
87 self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
88 }
89 sym::rustc_allow_const_fn_unstable => {
90 self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
91 }
92 sym::naked => self.check_naked(hir_id, attr, span, target),
93 sym::rustc_legacy_const_generics => {
94 self.check_rustc_legacy_const_generics(&attr, span, target, item)
95 }
96 sym::rustc_clean
97 | sym::rustc_dirty
98 | sym::rustc_if_this_changed
99 | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
136023e0
XL
100 sym::cmse_nonsecure_entry => self.check_cmse_nonsecure_entry(attr, span, target),
101 sym::default_method_body_is_const => {
102 self.check_default_method_body_is_const(attr, span, target)
103 }
cdc7bbd5 104 _ => true,
e74abb32 105 };
cdc7bbd5
XL
106 // lint-only checks
107 match attr.name_or_empty() {
108 sym::cold => self.check_cold(hir_id, attr, span, target),
109 sym::link_name => self.check_link_name(hir_id, attr, span, target),
110 sym::link_section => self.check_link_section(hir_id, attr, span, target),
111 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
112 _ => {}
113 }
041b39d2 114 }
2c00a5a8 115
e74abb32
XL
116 if !is_valid {
117 return;
118 }
119
29967ef6 120 if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
f9f354fc 121 self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
e74abb32
XL
122 }
123
dfeec247 124 self.check_repr(attrs, span, target, item, hir_id);
e74abb32
XL
125 self.check_used(attrs, target);
126 }
127
5869c6ff
XL
128 fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
129 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
130 lint.build(&format!(
131 "`#[{}]` is ignored on struct fields, match arms and macro defs",
132 sym,
133 ))
134 .warn(
135 "this was previously accepted by the compiler but is \
136 being phased out; it will become a hard error in \
137 a future release!",
138 )
139 .note(
140 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
141 for more information",
142 )
143 .emit();
144 });
145 }
146
147 fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
148 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
149 lint.build(&format!("`#[{}]` is ignored on struct fields and match arms", sym))
150 .warn(
151 "this was previously accepted by the compiler but is \
152 being phased out; it will become a hard error in \
153 a future release!",
154 )
155 .note(
156 "see issue #80564 <https://github.com/rust-lang/rust/issues/80564> \
157 for more information",
158 )
159 .emit();
160 });
161 }
162
e74abb32
XL
163 /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
164 fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
165 match target {
dfeec247
XL
166 Target::Fn
167 | Target::Closure
ba9703b0 168 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
e74abb32 169 Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
74b04a01
XL
170 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
171 lint.build("`#[inline]` is ignored on function prototypes").emit()
172 });
e74abb32
XL
173 true
174 }
175 // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
176 // just a lint, because we previously erroneously allowed it and some crates used it
177 // accidentally, to to be compatible with crates depending on them, we can't throw an
178 // error here.
179 Target::AssocConst => {
74b04a01
XL
180 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
181 lint.build("`#[inline]` is ignored on constants")
182 .warn(
183 "this was previously accepted by the compiler but is \
1b1a35ee
XL
184 being phased out; it will become a hard error in \
185 a future release!",
74b04a01
XL
186 )
187 .note(
188 "see issue #65833 <https://github.com/rust-lang/rust/issues/65833> \
1b1a35ee 189 for more information",
74b04a01
XL
190 )
191 .emit();
192 });
e74abb32
XL
193 true
194 }
5869c6ff
XL
195 // FIXME(#80564): Same for fields, arms, and macro defs
196 Target::Field | Target::Arm | Target::MacroDef => {
197 self.inline_attr_str_error_with_macro_def(hir_id, attr, "inline");
198 true
199 }
e74abb32
XL
200 _ => {
201 struct_span_err!(
202 self.tcx.sess,
203 attr.span,
204 E0518,
205 "attribute should be applied to function or closure",
dfeec247
XL
206 )
207 .span_label(*span, "not a function or closure")
208 .emit();
e74abb32
XL
209 false
210 }
211 }
041b39d2
XL
212 }
213
fc512014 214 /// Checks if `#[naked]` is applied to a function definition.
5869c6ff 215 fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
fc512014
XL
216 match target {
217 Target::Fn
218 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
5869c6ff
XL
219 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
220 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
221 // erroneously allowed it and some crates used it accidentally, to to be compatible
222 // with crates depending on them, we can't throw an error here.
223 Target::Field | Target::Arm | Target::MacroDef => {
224 self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked");
225 true
226 }
fc512014
XL
227 _ => {
228 self.tcx
229 .sess
230 .struct_span_err(
231 attr.span,
232 "attribute should be applied to a function definition",
233 )
234 .span_label(*span, "not a function definition")
235 .emit();
236 false
237 }
238 }
239 }
240
136023e0
XL
241 /// Checks if `#[cmse_nonsecure_entry]` is applied to a function definition.
242 fn check_cmse_nonsecure_entry(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
243 match target {
244 Target::Fn
245 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
246 _ => {
247 self.tcx
248 .sess
249 .struct_span_err(
250 attr.span,
251 "attribute should be applied to a function definition",
252 )
253 .span_label(*span, "not a function definition")
254 .emit();
255 false
256 }
257 }
258 }
259
e74abb32
XL
260 /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
261 fn check_track_caller(
262 &self,
5869c6ff 263 hir_id: HirId,
e74abb32 264 attr_span: &Span,
dfeec247 265 attrs: &'hir [Attribute],
e74abb32
XL
266 span: &Span,
267 target: Target,
268 ) -> bool {
269 match target {
fc512014 270 _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => {
e74abb32
XL
271 struct_span_err!(
272 self.tcx.sess,
273 *attr_span,
274 E0736,
275 "cannot use `#[track_caller]` with `#[naked]`",
dfeec247
XL
276 )
277 .emit();
e74abb32
XL
278 false
279 }
1b1a35ee 280 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
5869c6ff
XL
281 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
282 // `#[track_caller]` attribute with just a lint, because we previously
283 // erroneously allowed it and some crates used it accidentally, to to be compatible
284 // with crates depending on them, we can't throw an error here.
285 Target::Field | Target::Arm | Target::MacroDef => {
286 for attr in attrs {
287 self.inline_attr_str_error_with_macro_def(hir_id, attr, "track_caller");
288 }
289 true
290 }
e74abb32
XL
291 _ => {
292 struct_span_err!(
293 self.tcx.sess,
294 *attr_span,
295 E0739,
296 "attribute should be applied to function"
297 )
298 .span_label(*span, "not a function")
83c7162d 299 .emit();
e74abb32
XL
300 false
301 }
83c7162d
XL
302 }
303 }
304
e74abb32 305 /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
5869c6ff
XL
306 fn check_non_exhaustive(
307 &self,
308 hir_id: HirId,
309 attr: &Attribute,
310 span: &Span,
311 target: Target,
312 ) -> bool {
83c7162d 313 match target {
29967ef6 314 Target::Struct | Target::Enum | Target::Variant => true,
5869c6ff
XL
315 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
316 // `#[non_exhaustive]` attribute with just a lint, because we previously
317 // erroneously allowed it and some crates used it accidentally, to to be compatible
318 // with crates depending on them, we can't throw an error here.
319 Target::Field | Target::Arm | Target::MacroDef => {
320 self.inline_attr_str_error_with_macro_def(hir_id, attr, "non_exhaustive");
321 true
322 }
83c7162d 323 _ => {
dfeec247
XL
324 struct_span_err!(
325 self.tcx.sess,
326 attr.span,
327 E0701,
328 "attribute can only be applied to a struct or enum"
329 )
330 .span_label(*span, "not a struct or enum")
331 .emit();
e74abb32 332 false
83c7162d
XL
333 }
334 }
b039eaaf
SL
335 }
336
e74abb32 337 /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
5869c6ff 338 fn check_marker(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
0bf4aa26 339 match target {
e74abb32 340 Target::Trait => true,
5869c6ff
XL
341 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
342 // `#[marker]` attribute with just a lint, because we previously
343 // erroneously allowed it and some crates used it accidentally, to to be compatible
344 // with crates depending on them, we can't throw an error here.
345 Target::Field | Target::Arm | Target::MacroDef => {
346 self.inline_attr_str_error_with_macro_def(hir_id, attr, "marker");
347 true
348 }
0bf4aa26 349 _ => {
dfeec247
XL
350 self.tcx
351 .sess
0bf4aa26 352 .struct_span_err(attr.span, "attribute can only be applied to a trait")
e74abb32 353 .span_label(*span, "not a trait")
0bf4aa26 354 .emit();
e74abb32 355 false
0bf4aa26
XL
356 }
357 }
0bf4aa26
XL
358 }
359
e74abb32 360 /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
1b1a35ee
XL
361 fn check_target_feature(
362 &self,
363 hir_id: HirId,
364 attr: &Attribute,
365 span: &Span,
366 target: Target,
367 ) -> bool {
e74abb32 368 match target {
dfeec247 369 Target::Fn
ba9703b0 370 | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
1b1a35ee
XL
371 // FIXME: #[target_feature] was previously erroneously allowed on statements and some
372 // crates used this, so only emit a warning.
373 Target::Statement => {
374 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
375 lint.build("attribute should be applied to a function")
376 .warn(
377 "this was previously accepted by the compiler but is \
378 being phased out; it will become a hard error in \
379 a future release!",
380 )
381 .span_label(*span, "not a function")
382 .emit();
383 });
384 true
385 }
5869c6ff
XL
386 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
387 // `#[target_feature]` attribute with just a lint, because we previously
388 // erroneously allowed it and some crates used it accidentally, to to be compatible
389 // with crates depending on them, we can't throw an error here.
390 Target::Field | Target::Arm | Target::MacroDef => {
391 self.inline_attr_str_error_with_macro_def(hir_id, attr, "target_feature");
392 true
393 }
e74abb32 394 _ => {
dfeec247
XL
395 self.tcx
396 .sess
e74abb32
XL
397 .struct_span_err(attr.span, "attribute should be applied to a function")
398 .span_label(*span, "not a function")
399 .emit();
400 false
dfeec247 401 }
e74abb32
XL
402 }
403 }
404
fc512014 405 fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) {
1b1a35ee
XL
406 self.tcx
407 .sess
408 .struct_span_err(
409 meta.span(),
fc512014 410 &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name),
1b1a35ee
XL
411 )
412 .emit();
413 }
414
6a06907d
XL
415 fn check_doc_alias_value(
416 &self,
417 meta: &NestedMetaItem,
418 doc_alias: &str,
419 hir_id: HirId,
420 target: Target,
421 is_list: bool,
422 ) -> bool {
423 let tcx = self.tcx;
424 let err_fn = move |span: Span, msg: &str| {
425 tcx.sess.span_err(
426 span,
427 &format!(
428 "`#[doc(alias{})]` {}",
429 if is_list { "(\"...\")" } else { " = \"...\"" },
430 msg,
431 ),
432 );
433 false
434 };
fc512014 435 if doc_alias.is_empty() {
6a06907d
XL
436 return err_fn(
437 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
438 "attribute cannot have empty value",
439 );
fc512014
XL
440 }
441 if let Some(c) =
442 doc_alias.chars().find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
443 {
6a06907d
XL
444 self.tcx.sess.span_err(
445 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
446 &format!(
447 "{:?} character isn't allowed in `#[doc(alias{})]`",
448 c,
449 if is_list { "(\"...\")" } else { " = \"...\"" },
450 ),
451 );
fc512014
XL
452 return false;
453 }
454 if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
6a06907d
XL
455 return err_fn(
456 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
457 "cannot start or end with ' '",
458 );
fc512014
XL
459 }
460 if let Some(err) = match target {
461 Target::Impl => Some("implementation block"),
462 Target::ForeignMod => Some("extern block"),
463 Target::AssocTy => {
464 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
465 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
466 if Target::from_item(containing_item) == Target::Impl {
467 Some("type alias in implementation block")
468 } else {
469 None
470 }
471 }
472 Target::AssocConst => {
473 let parent_hir_id = self.tcx.hir().get_parent_item(hir_id);
474 let containing_item = self.tcx.hir().expect_item(parent_hir_id);
475 // We can't link to trait impl's consts.
476 let err = "associated constant in trait implementation block";
477 match containing_item.kind {
5869c6ff 478 ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => Some(err),
fc512014
XL
479 _ => None,
480 }
481 }
136023e0
XL
482 // we check the validity of params elsewhere
483 Target::Param => return false,
fc512014
XL
484 _ => None,
485 } {
6a06907d 486 return err_fn(meta.span(), &format!("isn't allowed on {}", err));
fc512014 487 }
5869c6ff
XL
488 let item_name = self.tcx.hir().name(hir_id);
489 if &*item_name.as_str() == doc_alias {
6a06907d
XL
490 return err_fn(meta.span(), "is the same as the item's name");
491 }
492 true
493 }
494
495 fn check_doc_alias(&self, meta: &NestedMetaItem, hir_id: HirId, target: Target) -> bool {
496 if let Some(values) = meta.meta_item_list() {
497 let mut errors = 0;
498 for v in values {
499 match v.literal() {
500 Some(l) => match l.kind {
501 LitKind::Str(s, _) => {
502 if !self.check_doc_alias_value(v, &s.as_str(), hir_id, target, true) {
503 errors += 1;
504 }
505 }
506 _ => {
507 self.tcx
508 .sess
509 .struct_span_err(
510 v.span(),
511 "`#[doc(alias(\"a\"))]` expects string literals",
512 )
513 .emit();
514 errors += 1;
515 }
516 },
517 None => {
518 self.tcx
519 .sess
520 .struct_span_err(
521 v.span(),
522 "`#[doc(alias(\"a\"))]` expects string literals",
523 )
524 .emit();
525 errors += 1;
526 }
527 }
528 }
529 errors == 0
530 } else if let Some(doc_alias) = meta.value_str().map(|s| s.to_string()) {
531 self.check_doc_alias_value(meta, &doc_alias, hir_id, target, false)
532 } else {
5869c6ff
XL
533 self.tcx
534 .sess
535 .struct_span_err(
536 meta.span(),
6a06907d
XL
537 "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \
538 strings `#[doc(alias(\"a\", \"b\"))]`",
5869c6ff
XL
539 )
540 .emit();
6a06907d 541 false
5869c6ff 542 }
fc512014
XL
543 }
544
545 fn check_doc_keyword(&self, meta: &NestedMetaItem, hir_id: HirId) -> bool {
546 let doc_keyword = meta.value_str().map(|s| s.to_string()).unwrap_or_else(String::new);
547 if doc_keyword.is_empty() {
548 self.doc_attr_str_error(meta, "keyword");
549 return false;
550 }
136023e0
XL
551 match self.tcx.hir().find(hir_id).and_then(|node| match node {
552 hir::Node::Item(item) => Some(&item.kind),
553 _ => None,
554 }) {
555 Some(ItemKind::Mod(ref module)) => {
fc512014
XL
556 if !module.item_ids.is_empty() {
557 self.tcx
558 .sess
559 .struct_span_err(
560 meta.span(),
561 "`#[doc(keyword = \"...\")]` can only be used on empty modules",
562 )
563 .emit();
564 return false;
565 }
566 }
567 _ => {
568 self.tcx
569 .sess
570 .struct_span_err(
571 meta.span(),
572 "`#[doc(keyword = \"...\")]` can only be used on modules",
573 )
574 .emit();
575 return false;
576 }
577 }
578 if !rustc_lexer::is_ident(&doc_keyword) {
579 self.tcx
580 .sess
581 .struct_span_err(
582 meta.name_value_literal_span().unwrap_or_else(|| meta.span()),
583 &format!("`{}` is not a valid identifier", doc_keyword),
584 )
585 .emit();
586 return false;
587 }
588 true
589 }
590
17df50a5
XL
591 /// Checks `#[doc(inline)]`/`#[doc(no_inline)]` attributes. Returns `true` if valid.
592 ///
593 /// A doc inlining attribute is invalid if it is applied to a non-`use` item, or
594 /// if there are conflicting attributes for one item.
595 ///
596 /// `specified_inline` is used to keep track of whether we have
597 /// already seen an inlining attribute for this item.
598 /// If so, `specified_inline` holds the value and the span of
599 /// the first `inline`/`no_inline` attribute.
600 fn check_doc_inline(
601 &self,
602 attr: &Attribute,
603 meta: &NestedMetaItem,
604 hir_id: HirId,
605 target: Target,
606 specified_inline: &mut Option<(bool, Span)>,
607 ) -> bool {
608 if target == Target::Use || target == Target::ExternCrate {
609 let do_inline = meta.name_or_empty() == sym::inline;
610 if let Some((prev_inline, prev_span)) = *specified_inline {
611 if do_inline != prev_inline {
612 let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]);
613 spans.push_span_label(prev_span, String::from("this attribute..."));
614 spans.push_span_label(
615 meta.span(),
616 String::from("...conflicts with this attribute"),
617 );
618 self.tcx
619 .sess
620 .struct_span_err(spans, "conflicting doc inlining attributes")
621 .help("remove one of the conflicting attributes")
622 .emit();
623 return false;
624 }
625 true
626 } else {
627 *specified_inline = Some((do_inline, meta.span()));
628 true
629 }
630 } else {
631 self.tcx.struct_span_lint_hir(
632 INVALID_DOC_ATTRIBUTES,
633 hir_id,
634 meta.span(),
635 |lint| {
636 let mut err = lint.build(
637 "this attribute can only be applied to a `use` item",
638 );
639 err.span_label(meta.span(), "only applicable on `use` items");
640 if attr.style == AttrStyle::Outer {
641 err.span_label(
642 self.tcx.hir().span(hir_id),
643 "not a `use` item",
644 );
645 }
646 err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#docno_inlinedocinline for more information")
647 .emit();
648 },
649 );
650 false
651 }
652 }
653
654 /// Checks that an attribute is *not* used at the crate level. Returns `true` if valid.
655 fn check_attr_not_crate_level(
fc512014
XL
656 &self,
657 meta: &NestedMetaItem,
658 hir_id: HirId,
659 attr_name: &str,
660 ) -> bool {
661 if CRATE_HIR_ID == hir_id {
662 self.tcx
663 .sess
664 .struct_span_err(
665 meta.span(),
666 &format!(
6a06907d 667 "`#![doc({} = \"...\")]` isn't allowed as a crate-level attribute",
fc512014
XL
668 attr_name,
669 ),
670 )
671 .emit();
672 return false;
673 }
674 true
675 }
676
17df50a5
XL
677 /// Checks that an attribute is used at the crate level. Returns `true` if valid.
678 fn check_attr_crate_level(
679 &self,
680 attr: &Attribute,
681 meta: &NestedMetaItem,
682 hir_id: HirId,
683 ) -> bool {
684 if hir_id != CRATE_HIR_ID {
685 self.tcx.struct_span_lint_hir(
686 INVALID_DOC_ATTRIBUTES,
687 hir_id,
688 meta.span(),
689 |lint| {
690 let mut err = lint.build(
691 "this attribute can only be applied at the crate level",
692 );
693 if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_HIR_ID {
694 if let Ok(mut src) =
695 self.tcx.sess.source_map().span_to_snippet(attr.span)
696 {
697 src.insert(1, '!');
698 err.span_suggestion_verbose(
699 attr.span,
700 "to apply to the crate, use an inner attribute",
701 src,
702 Applicability::MaybeIncorrect,
703 );
704 } else {
705 err.span_help(
706 attr.span,
707 "to apply to the crate, use an inner attribute",
708 );
709 }
710 }
711 err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information")
712 .emit();
713 },
714 );
715 return false;
716 }
717 true
718 }
719
720 /// Runs various checks on `#[doc]` attributes. Returns `true` if valid.
721 ///
722 /// `specified_inline` should be initialized to `None` and kept for the scope
723 /// of one item. Read the documentation of [`check_doc_inline`] for more information.
724 ///
725 /// [`check_doc_inline`]: Self::check_doc_inline
726 fn check_doc_attrs(
727 &self,
728 attr: &Attribute,
729 hir_id: HirId,
730 target: Target,
731 specified_inline: &mut Option<(bool, Span)>,
732 ) -> bool {
6a06907d
XL
733 let mut is_valid = true;
734
735 if let Some(list) = attr.meta().and_then(|mi| mi.meta_item_list().map(|l| l.to_vec())) {
17df50a5 736 for meta in &list {
6a06907d
XL
737 if let Some(i_meta) = meta.meta_item() {
738 match i_meta.name_or_empty() {
739 sym::alias
17df50a5 740 if !self.check_attr_not_crate_level(&meta, hir_id, "alias")
6a06907d 741 || !self.check_doc_alias(&meta, hir_id, target) =>
3dfed10e 742 {
6a06907d 743 is_valid = false
29967ef6 744 }
6a06907d
XL
745
746 sym::keyword
17df50a5 747 if !self.check_attr_not_crate_level(&meta, hir_id, "keyword")
6a06907d 748 || !self.check_doc_keyword(&meta, hir_id) =>
fc512014 749 {
6a06907d
XL
750 is_valid = false
751 }
752
17df50a5
XL
753 sym::html_favicon_url
754 | sym::html_logo_url
755 | sym::html_playground_url
756 | sym::issue_tracker_base_url
757 | sym::html_root_url
758 | sym::html_no_source
759 | sym::test
760 if !self.check_attr_crate_level(&attr, &meta, hir_id) =>
761 {
762 is_valid = false;
763 }
764
765 sym::inline | sym::no_inline
766 if !self.check_doc_inline(
767 &attr,
768 &meta,
6a06907d 769 hir_id,
17df50a5
XL
770 target,
771 specified_inline,
772 ) =>
773 {
6a06907d
XL
774 is_valid = false;
775 }
776
777 // no_default_passes: deprecated
778 // passes: deprecated
779 // plugins: removed, but rustdoc warns about it itself
780 sym::alias
781 | sym::cfg
782 | sym::hidden
783 | sym::html_favicon_url
784 | sym::html_logo_url
785 | sym::html_no_source
786 | sym::html_playground_url
787 | sym::html_root_url
6a06907d
XL
788 | sym::inline
789 | sym::issue_tracker_base_url
790 | sym::keyword
791 | sym::masked
792 | sym::no_default_passes
793 | sym::no_inline
cdc7bbd5 794 | sym::notable_trait
6a06907d
XL
795 | sym::passes
796 | sym::plugins
797 | sym::primitive
6a06907d
XL
798 | sym::test => {}
799
800 _ => {
801 self.tcx.struct_span_lint_hir(
802 INVALID_DOC_ATTRIBUTES,
803 hir_id,
804 i_meta.span,
805 |lint| {
cdc7bbd5 806 let mut diag = lint.build(&format!(
6a06907d
XL
807 "unknown `doc` attribute `{}`",
808 rustc_ast_pretty::pprust::path_to_string(&i_meta.path),
cdc7bbd5
XL
809 ));
810 if i_meta.has_name(sym::spotlight) {
811 diag.note(
812 "`doc(spotlight)` was renamed to `doc(notable_trait)`",
813 );
814 diag.span_suggestion_short(
815 i_meta.span,
816 "use `notable_trait` instead",
817 String::from("notable_trait"),
818 Applicability::MachineApplicable,
819 );
820 diag.note("`doc(spotlight)` is now a no-op");
821 }
17df50a5
XL
822 if i_meta.has_name(sym::include) {
823 if let Some(value) = i_meta.value_str() {
824 // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect
825 let applicability = if list.len() == 1 {
826 Applicability::MachineApplicable
827 } else {
828 Applicability::MaybeIncorrect
829 };
830 let inner = if attr.style == AttrStyle::Inner {
831 "!"
832 } else {
833 ""
834 };
835 diag.span_suggestion(
836 attr.meta().unwrap().span,
837 "use `doc = include_str!` instead",
838 format!(
839 "#{}[doc = include_str!(\"{}\")]",
840 inner, value
841 ),
842 applicability,
843 );
844 }
845 }
cdc7bbd5 846 diag.emit();
6a06907d
XL
847 },
848 );
849 is_valid = false;
29967ef6 850 }
3dfed10e 851 }
6a06907d
XL
852 } else {
853 self.tcx.struct_span_lint_hir(
854 INVALID_DOC_ATTRIBUTES,
855 hir_id,
856 meta.span(),
857 |lint| {
858 lint.build(&format!("invalid `doc` attribute")).emit();
859 },
860 );
861 is_valid = false;
3dfed10e
XL
862 }
863 }
864 }
6a06907d
XL
865
866 is_valid
3dfed10e
XL
867 }
868
1b1a35ee
XL
869 /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
870 fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
871 match target {
872 Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {}
5869c6ff
XL
873 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
874 // `#[cold]` attribute with just a lint, because we previously
875 // erroneously allowed it and some crates used it accidentally, to to be compatible
876 // with crates depending on them, we can't throw an error here.
877 Target::Field | Target::Arm | Target::MacroDef => {
878 self.inline_attr_str_error_with_macro_def(hir_id, attr, "cold");
879 }
1b1a35ee
XL
880 _ => {
881 // FIXME: #[cold] was previously allowed on non-functions and some crates used
882 // this, so only emit a warning.
883 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
884 lint.build("attribute should be applied to a function")
885 .warn(
886 "this was previously accepted by the compiler but is \
887 being phased out; it will become a hard error in \
888 a future release!",
889 )
890 .span_label(*span, "not a function")
891 .emit();
892 });
893 }
894 }
895 }
896
897 /// Checks if `#[link_name]` is applied to an item other than a foreign function or static.
898 fn check_link_name(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
899 match target {
900 Target::ForeignFn | Target::ForeignStatic => {}
5869c6ff
XL
901 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
902 // `#[link_name]` attribute with just a lint, because we previously
903 // erroneously allowed it and some crates used it accidentally, to to be compatible
904 // with crates depending on them, we can't throw an error here.
905 Target::Field | Target::Arm | Target::MacroDef => {
906 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_name");
907 }
1b1a35ee
XL
908 _ => {
909 // FIXME: #[cold] was previously allowed on non-functions/statics and some crates
910 // used this, so only emit a warning.
911 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
912 let mut diag =
913 lint.build("attribute should be applied to a foreign function or static");
914 diag.warn(
915 "this was previously accepted by the compiler but is \
916 being phased out; it will become a hard error in \
917 a future release!",
918 );
919
920 // See issue #47725
921 if let Target::ForeignMod = target {
922 if let Some(value) = attr.value_str() {
923 diag.span_help(
924 attr.span,
925 &format!(r#"try `#[link(name = "{}")]` instead"#, value),
926 );
927 } else {
928 diag.span_help(attr.span, r#"try `#[link(name = "...")]` instead"#);
929 }
930 }
931
932 diag.span_label(*span, "not a foreign function or static");
933 diag.emit();
934 });
935 }
936 }
937 }
938
939 /// Checks if `#[no_link]` is applied to an `extern crate`. Returns `true` if valid.
5869c6ff
XL
940 fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
941 match target {
942 Target::ExternCrate => true,
943 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
944 // `#[no_link]` attribute with just a lint, because we previously
945 // erroneously allowed it and some crates used it accidentally, to to be compatible
946 // with crates depending on them, we can't throw an error here.
947 Target::Field | Target::Arm | Target::MacroDef => {
948 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link");
949 true
950 }
951 _ => {
952 self.tcx
953 .sess
954 .struct_span_err(
955 attr.span,
956 "attribute should be applied to an `extern crate` item",
957 )
958 .span_label(*span, "not an `extern crate` item")
959 .emit();
960 false
961 }
1b1a35ee
XL
962 }
963 }
964
965 /// Checks if `#[export_name]` is applied to a function or static. Returns `true` if valid.
5869c6ff
XL
966 fn check_export_name(
967 &self,
968 hir_id: HirId,
969 attr: &Attribute,
970 span: &Span,
971 target: Target,
972 ) -> bool {
1b1a35ee
XL
973 match target {
974 Target::Static | Target::Fn | Target::Method(..) => true,
5869c6ff
XL
975 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
976 // `#[export_name]` attribute with just a lint, because we previously
977 // erroneously allowed it and some crates used it accidentally, to to be compatible
978 // with crates depending on them, we can't throw an error here.
979 Target::Field | Target::Arm | Target::MacroDef => {
980 self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name");
981 true
982 }
1b1a35ee
XL
983 _ => {
984 self.tcx
985 .sess
986 .struct_span_err(
987 attr.span,
988 "attribute should be applied to a function or static",
989 )
990 .span_label(*span, "not a function or static")
991 .emit();
992 false
993 }
994 }
995 }
996
6a06907d
XL
997 fn check_rustc_layout_scalar_valid_range(
998 &self,
999 attr: &Attribute,
1000 span: &Span,
1001 target: Target,
1002 ) -> bool {
1003 if target != Target::Struct {
1004 self.tcx
1005 .sess
1006 .struct_span_err(attr.span, "attribute should be applied to a struct")
1007 .span_label(*span, "not a struct")
1008 .emit();
1009 return false;
1010 }
1011
1012 let list = match attr.meta_item_list() {
1013 None => return false,
1014 Some(it) => it,
1015 };
1016
1017 if matches!(&list[..], &[NestedMetaItem::Literal(Lit { kind: LitKind::Int(..), .. })]) {
1018 true
1019 } else {
1020 self.tcx
1021 .sess
1022 .struct_span_err(attr.span, "expected exactly one integer literal argument")
1023 .emit();
1024 false
1025 }
1026 }
1027
1028 /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument.
1029 fn check_rustc_legacy_const_generics(
1030 &self,
1031 attr: &Attribute,
1032 span: &Span,
1033 target: Target,
1034 item: Option<ItemLike<'_>>,
1035 ) -> bool {
1036 let is_function = matches!(target, Target::Fn | Target::Method(..));
1037 if !is_function {
1038 self.tcx
1039 .sess
1040 .struct_span_err(attr.span, "attribute should be applied to a function")
1041 .span_label(*span, "not a function")
1042 .emit();
1043 return false;
1044 }
1045
1046 let list = match attr.meta_item_list() {
1047 // The attribute form is validated on AST.
1048 None => return false,
1049 Some(it) => it,
1050 };
1051
1052 let (decl, generics) = match item {
1053 Some(ItemLike::Item(Item {
1054 kind: ItemKind::Fn(FnSig { decl, .. }, generics, _),
1055 ..
1056 })) => (decl, generics),
1057 _ => bug!("should be a function item"),
1058 };
1059
1060 for param in generics.params {
1061 match param.kind {
1062 hir::GenericParamKind::Const { .. } => {}
1063 _ => {
1064 self.tcx
1065 .sess
1066 .struct_span_err(
1067 attr.span,
1068 "#[rustc_legacy_const_generics] functions must \
1069 only have const generics",
1070 )
1071 .span_label(param.span, "non-const generic parameter")
1072 .emit();
1073 return false;
1074 }
1075 }
1076 }
1077
1078 if list.len() != generics.params.len() {
1079 self.tcx
1080 .sess
1081 .struct_span_err(
1082 attr.span,
1083 "#[rustc_legacy_const_generics] must have one index for each generic parameter",
1084 )
1085 .span_label(generics.span, "generic parameters")
1086 .emit();
1087 return false;
1088 }
1089
1090 let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128;
1091 let mut invalid_args = vec![];
1092 for meta in list {
1093 if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
1094 if *val >= arg_count {
1095 let span = meta.span();
1096 self.tcx
1097 .sess
1098 .struct_span_err(span, "index exceeds number of arguments")
1099 .span_label(
1100 span,
1101 format!(
1102 "there {} only {} argument{}",
1103 if arg_count != 1 { "are" } else { "is" },
1104 arg_count,
1105 pluralize!(arg_count)
1106 ),
1107 )
1108 .emit();
1109 return false;
1110 }
1111 } else {
1112 invalid_args.push(meta.span());
1113 }
1114 }
1115
1116 if !invalid_args.is_empty() {
1117 self.tcx
1118 .sess
1119 .struct_span_err(invalid_args, "arguments should be non-negative integers")
1120 .emit();
1121 false
1122 } else {
1123 true
1124 }
1125 }
1126
cdc7bbd5
XL
1127 /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
1128 /// option is passed to the compiler.
1129 fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
1130 if self.tcx.sess.opts.debugging_opts.query_dep_graph {
1131 true
1132 } else {
1133 self.tcx
1134 .sess
1135 .struct_span_err(attr.span, "attribute requires -Z query-dep-graph to be enabled")
1136 .emit();
1137 false
1138 }
1139 }
1140
1b1a35ee
XL
1141 /// Checks if `#[link_section]` is applied to a function or static.
1142 fn check_link_section(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1143 match target {
1144 Target::Static | Target::Fn | Target::Method(..) => {}
5869c6ff
XL
1145 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1146 // `#[link_section]` attribute with just a lint, because we previously
1147 // erroneously allowed it and some crates used it accidentally, to to be compatible
1148 // with crates depending on them, we can't throw an error here.
1149 Target::Field | Target::Arm | Target::MacroDef => {
1150 self.inline_attr_str_error_with_macro_def(hir_id, attr, "link_section");
1151 }
1b1a35ee
XL
1152 _ => {
1153 // FIXME: #[link_section] was previously allowed on non-functions/statics and some
1154 // crates used this, so only emit a warning.
1155 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1156 lint.build("attribute should be applied to a function or static")
1157 .warn(
1158 "this was previously accepted by the compiler but is \
1159 being phased out; it will become a hard error in \
1160 a future release!",
1161 )
1162 .span_label(*span, "not a function or static")
1163 .emit();
1164 });
1165 }
1166 }
1167 }
1168
1169 /// Checks if `#[no_mangle]` is applied to a function or static.
1170 fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
1171 match target {
1172 Target::Static | Target::Fn | Target::Method(..) => {}
5869c6ff
XL
1173 // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
1174 // `#[no_mangle]` attribute with just a lint, because we previously
1175 // erroneously allowed it and some crates used it accidentally, to to be compatible
1176 // with crates depending on them, we can't throw an error here.
1177 Target::Field | Target::Arm | Target::MacroDef => {
1178 self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_mangle");
1179 }
1b1a35ee
XL
1180 _ => {
1181 // FIXME: #[no_mangle] was previously allowed on non-functions/statics and some
1182 // crates used this, so only emit a warning.
1183 self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
1184 lint.build("attribute should be applied to a function or static")
1185 .warn(
1186 "this was previously accepted by the compiler but is \
1187 being phased out; it will become a hard error in \
1188 a future release!",
1189 )
1190 .span_label(*span, "not a function or static")
1191 .emit();
1192 });
1193 }
1194 }
1195 }
1196
9fa01778 1197 /// Checks if the `#[repr]` attributes on `item` are valid.
e74abb32
XL
1198 fn check_repr(
1199 &self,
dfeec247 1200 attrs: &'hir [Attribute],
e74abb32
XL
1201 span: &Span,
1202 target: Target,
1b1a35ee 1203 item: Option<ItemLike<'_>>,
dfeec247 1204 hir_id: HirId,
e74abb32 1205 ) {
2c00a5a8
XL
1206 // Extract the names of all repr hints, e.g., [foo, bar, align] for:
1207 // ```
1208 // #[repr(foo)]
1209 // #[repr(bar, align(8))]
1210 // ```
e74abb32 1211 let hints: Vec<_> = attrs
2c00a5a8 1212 .iter()
cdc7bbd5 1213 .filter(|attr| attr.has_name(sym::repr))
2c00a5a8 1214 .filter_map(|attr| attr.meta_item_list())
b7449926 1215 .flatten()
2c00a5a8 1216 .collect();
9e0c209e 1217
ff7c6d11
XL
1218 let mut int_reprs = 0;
1219 let mut is_c = false;
1220 let mut is_simd = false;
2c00a5a8
XL
1221 let mut is_transparent = false;
1222
1223 for hint in &hints {
136023e0
XL
1224 if !hint.is_meta_item() {
1225 struct_span_err!(
1226 self.tcx.sess,
1227 hint.span(),
1228 E0565,
1229 "meta item in `repr` must be an identifier"
1230 )
1231 .emit();
1232 continue;
1233 }
1234
48663c56 1235 let (article, allowed_targets) = match hint.name_or_empty() {
136023e0
XL
1236 sym::C => {
1237 is_c = true;
dc9dc135
XL
1238 match target {
1239 Target::Struct | Target::Union | Target::Enum => continue,
1240 _ => ("a", "struct, enum, or union"),
9e0c209e
SL
1241 }
1242 }
136023e0
XL
1243 sym::align => {
1244 if let (Target::Fn, true) = (target, !self.tcx.features().fn_align) {
1245 feature_err(
1246 &self.tcx.sess.parse_sess,
1247 sym::fn_align,
1248 hint.span(),
1249 "`repr(align)` attributes on functions are unstable",
1250 )
1251 .emit();
1252 }
1253
1254 match target {
1255 Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
1256 _ => ("a", "struct, enum, function, or union"),
1257 }
1258 }
48663c56 1259 sym::packed => {
dfeec247
XL
1260 if target != Target::Struct && target != Target::Union {
1261 ("a", "struct or union")
92a42be0 1262 } else {
dfeec247 1263 continue;
b039eaaf
SL
1264 }
1265 }
48663c56 1266 sym::simd => {
ff7c6d11 1267 is_simd = true;
1b1a35ee
XL
1268 if target != Target::Struct {
1269 ("a", "struct")
1270 } else {
1271 continue;
1272 }
b039eaaf 1273 }
48663c56 1274 sym::transparent => {
2c00a5a8 1275 is_transparent = true;
dc9dc135
XL
1276 match target {
1277 Target::Struct | Target::Union | Target::Enum => continue,
1278 _ => ("a", "struct, enum, or union"),
cc61c64b
XL
1279 }
1280 }
74b04a01
XL
1281 sym::no_niche => {
1282 if !self.tcx.features().enabled(sym::no_niche) {
1283 feature_err(
1284 &self.tcx.sess.parse_sess,
1285 sym::no_niche,
1286 hint.span(),
1287 "the attribute `repr(no_niche)` is currently unstable",
1288 )
1289 .emit();
1290 }
1291 match target {
1292 Target::Struct | Target::Enum => continue,
1293 _ => ("a", "struct or enum"),
1294 }
1295 }
dfeec247
XL
1296 sym::i8
1297 | sym::u8
1298 | sym::i16
1299 | sym::u16
1300 | sym::i32
1301 | sym::u32
1302 | sym::i64
1303 | sym::u64
f035d41b
XL
1304 | sym::i128
1305 | sym::u128
dfeec247
XL
1306 | sym::isize
1307 | sym::usize => {
ff7c6d11 1308 int_reprs += 1;
1b1a35ee
XL
1309 if target != Target::Enum {
1310 ("an", "enum")
1311 } else {
1312 continue;
1313 }
b039eaaf 1314 }
136023e0
XL
1315 _ => {
1316 struct_span_err!(
1317 self.tcx.sess,
1318 hint.span(),
1319 E0552,
1320 "unrecognized representation hint"
1321 )
1322 .emit();
1323
1324 continue;
1325 }
92a42be0 1326 };
29967ef6
XL
1327
1328 struct_span_err!(
1329 self.tcx.sess,
532ac7d7 1330 hint.span(),
29967ef6
XL
1331 E0517,
1332 "{}",
1333 &format!("attribute should be applied to {} {}", article, allowed_targets)
0531ce1d 1334 )
29967ef6
XL
1335 .span_label(*span, &format!("not {} {}", article, allowed_targets))
1336 .emit();
9e0c209e 1337 }
ff7c6d11 1338
2c00a5a8
XL
1339 // Just point at all repr hints if there are any incompatibilities.
1340 // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
532ac7d7 1341 let hint_spans = hints.iter().map(|hint| hint.span());
2c00a5a8 1342
74b04a01
XL
1343 // Error on repr(transparent, <anything else apart from no_niche>).
1344 let non_no_niche = |hint: &&NestedMetaItem| hint.name_or_empty() != sym::no_niche;
1345 let non_no_niche_count = hints.iter().filter(non_no_niche).count();
1346 if is_transparent && non_no_niche_count > 1 {
2c00a5a8 1347 let hint_spans: Vec<_> = hint_spans.clone().collect();
dfeec247
XL
1348 struct_span_err!(
1349 self.tcx.sess,
1350 hint_spans,
1351 E0692,
1352 "transparent {} cannot have other repr hints",
1353 target
1354 )
1355 .emit();
2c00a5a8 1356 }
ff7c6d11
XL
1357 // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
1358 if (int_reprs > 1)
dfeec247 1359 || (is_simd && is_c)
1b1a35ee
XL
1360 || (int_reprs == 1
1361 && is_c
1362 && item.map_or(false, |item| {
1363 if let ItemLike::Item(item) = item {
1364 return is_c_like_enum(item);
1365 }
1366 return false;
1367 }))
dfeec247 1368 {
74b04a01
XL
1369 self.tcx.struct_span_lint_hir(
1370 CONFLICTING_REPR_HINTS,
1371 hir_id,
1372 hint_spans.collect::<Vec<Span>>(),
1373 |lint| {
1374 lint.build("conflicting representation hints")
1375 .code(rustc_errors::error_code!(E0566))
1376 .emit();
1377 },
1378 );
b039eaaf
SL
1379 }
1380 }
0531ce1d 1381
29967ef6
XL
1382 fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
1383 for attr in attrs {
cdc7bbd5 1384 if attr.has_name(sym::used) && target != Target::Static {
29967ef6
XL
1385 self.tcx
1386 .sess
1387 .span_err(attr.span, "attribute must be applied to a `static` variable");
0531ce1d
XL
1388 }
1389 }
1390 }
1391
29967ef6
XL
1392 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1393 /// (Allows proc_macro functions)
1394 fn check_allow_internal_unstable(
1395 &self,
5869c6ff 1396 hir_id: HirId,
29967ef6
XL
1397 attr: &Attribute,
1398 span: &Span,
1399 target: Target,
1400 attrs: &[Attribute],
1401 ) -> bool {
1402 debug!("Checking target: {:?}", target);
5869c6ff
XL
1403 match target {
1404 Target::Fn => {
1405 for attr in attrs {
1406 if self.tcx.sess.is_proc_macro_attr(attr) {
1407 debug!("Is proc macro attr");
1408 return true;
1409 }
29967ef6 1410 }
5869c6ff
XL
1411 debug!("Is not proc macro attr");
1412 false
1413 }
1414 Target::MacroDef => true,
1415 // FIXME(#80564): We permit struct fields and match arms to have an
1416 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1417 // erroneously allowed it and some crates used it accidentally, to to be compatible
1418 // with crates depending on them, we can't throw an error here.
1419 Target::Field | Target::Arm => {
1420 self.inline_attr_str_error_without_macro_def(
1421 hir_id,
1422 attr,
1423 "allow_internal_unstable",
1424 );
1425 true
1426 }
1427 _ => {
1428 self.tcx
1429 .sess
1430 .struct_span_err(attr.span, "attribute should be applied to a macro")
1431 .span_label(*span, "not a macro")
1432 .emit();
1433 false
0531ce1d
XL
1434 }
1435 }
1436 }
83c7162d 1437
29967ef6
XL
1438 /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
1439 /// (Allows proc_macro functions)
1440 fn check_rustc_allow_const_fn_unstable(
1441 &self,
1442 hir_id: HirId,
1443 attr: &Attribute,
1444 span: &Span,
1445 target: Target,
1446 ) -> bool {
5869c6ff
XL
1447 match target {
1448 Target::Fn | Target::Method(_)
1449 if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) =>
1450 {
1451 true
1452 }
1453 // FIXME(#80564): We permit struct fields and match arms to have an
1454 // `#[allow_internal_unstable]` attribute with just a lint, because we previously
1455 // erroneously allowed it and some crates used it accidentally, to to be compatible
1456 // with crates depending on them, we can't throw an error here.
1457 Target::Field | Target::Arm | Target::MacroDef => {
1458 self.inline_attr_str_error_with_macro_def(hir_id, attr, "allow_internal_unstable");
1459 true
1460 }
1461 _ => {
1462 self.tcx
1463 .sess
1464 .struct_span_err(attr.span, "attribute should be applied to `const fn`")
1465 .span_label(*span, "not a `const fn`")
1466 .emit();
1467 false
83c7162d
XL
1468 }
1469 }
1470 }
136023e0
XL
1471
1472 /// default_method_body_is_const should only be applied to trait methods with default bodies.
1473 fn check_default_method_body_is_const(
1474 &self,
1475 attr: &Attribute,
1476 span: &Span,
1477 target: Target,
1478 ) -> bool {
1479 match target {
1480 Target::Method(MethodKind::Trait { body: true }) => true,
1481 _ => {
1482 self.tcx
1483 .sess
1484 .struct_span_err(
1485 attr.span,
1486 "attribute should be applied to a trait method with body",
1487 )
1488 .span_label(*span, "not a trait method or missing a body")
1489 .emit();
1490 false
1491 }
1492 }
1493 }
b039eaaf
SL
1494}
1495
dc9dc135 1496impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
dfeec247
XL
1497 type Map = Map<'tcx>;
1498
ba9703b0
XL
1499 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1500 NestedVisitorMap::OnlyBodies(self.tcx.hir())
2c00a5a8
XL
1501 }
1502
dfeec247 1503 fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
b039eaaf 1504 let target = Target::from_item(item);
6a06907d 1505 self.check_attributes(item.hir_id(), &item.span, target, Some(ItemLike::Item(item)));
0531ce1d
XL
1506 intravisit::walk_item(self, item)
1507 }
1508
fc512014
XL
1509 fn visit_generic_param(&mut self, generic_param: &'tcx hir::GenericParam<'tcx>) {
1510 let target = Target::from_generic_param(generic_param);
6a06907d 1511 self.check_attributes(generic_param.hir_id, &generic_param.span, target, None);
fc512014
XL
1512 intravisit::walk_generic_param(self, generic_param)
1513 }
1514
dfeec247 1515 fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
e74abb32 1516 let target = Target::from_trait_item(trait_item);
6a06907d 1517 self.check_attributes(trait_item.hir_id(), &trait_item.span, target, None);
e74abb32
XL
1518 intravisit::walk_trait_item(self, trait_item)
1519 }
1520
6a06907d
XL
1521 fn visit_field_def(&mut self, struct_field: &'tcx hir::FieldDef<'tcx>) {
1522 self.check_attributes(struct_field.hir_id, &struct_field.span, Target::Field, None);
1523 intravisit::walk_field_def(self, struct_field);
5869c6ff
XL
1524 }
1525
1526 fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
6a06907d 1527 self.check_attributes(arm.hir_id, &arm.span, Target::Arm, None);
5869c6ff
XL
1528 intravisit::walk_arm(self, arm);
1529 }
1530
1b1a35ee 1531 fn visit_foreign_item(&mut self, f_item: &'tcx ForeignItem<'tcx>) {
e74abb32 1532 let target = Target::from_foreign_item(f_item);
1b1a35ee 1533 self.check_attributes(
6a06907d 1534 f_item.hir_id(),
1b1a35ee
XL
1535 &f_item.span,
1536 target,
1537 Some(ItemLike::ForeignItem(f_item)),
1538 );
e74abb32
XL
1539 intravisit::walk_foreign_item(self, f_item)
1540 }
1541
dfeec247 1542 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
74b04a01 1543 let target = target_from_impl_item(self.tcx, impl_item);
6a06907d 1544 self.check_attributes(impl_item.hir_id(), &impl_item.span, target, None);
e74abb32
XL
1545 intravisit::walk_impl_item(self, impl_item)
1546 }
0531ce1d 1547
dfeec247 1548 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
29967ef6
XL
1549 // When checking statements ignore expressions, they will be checked later.
1550 if let hir::StmtKind::Local(ref l) = stmt.kind {
6a06907d 1551 self.check_attributes(l.hir_id, &stmt.span, Target::Statement, None);
29967ef6 1552 }
0531ce1d
XL
1553 intravisit::walk_stmt(self, stmt)
1554 }
1555
dfeec247 1556 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
29967ef6
XL
1557 let target = match expr.kind {
1558 hir::ExprKind::Closure(..) => Target::Closure,
1559 _ => Target::Expression,
1560 };
1561
6a06907d 1562 self.check_attributes(expr.hir_id, &expr.span, target, None);
0531ce1d 1563 intravisit::walk_expr(self, expr)
b039eaaf 1564 }
29967ef6
XL
1565
1566 fn visit_variant(
1567 &mut self,
1568 variant: &'tcx hir::Variant<'tcx>,
1569 generics: &'tcx hir::Generics<'tcx>,
1570 item_id: HirId,
1571 ) {
6a06907d 1572 self.check_attributes(variant.id, &variant.span, Target::Variant, None);
29967ef6
XL
1573 intravisit::walk_variant(self, variant, generics, item_id)
1574 }
6a06907d
XL
1575
1576 fn visit_macro_def(&mut self, macro_def: &'tcx hir::MacroDef<'tcx>) {
1577 self.check_attributes(macro_def.hir_id(), &macro_def.span, Target::MacroDef, None);
1578 intravisit::walk_macro_def(self, macro_def);
1579 }
1580
1581 fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
1582 self.check_attributes(param.hir_id, &param.span, Target::Param, None);
1583
1584 intravisit::walk_param(self, param);
1585 }
b039eaaf
SL
1586}
1587
dfeec247 1588fn is_c_like_enum(item: &Item<'_>) -> bool {
e74abb32 1589 if let ItemKind::Enum(ref def, _) = item.kind {
dfeec247 1590 for variant in def.variants {
e1599b0c 1591 match variant.data {
9fa01778 1592 hir::VariantData::Unit(..) => { /* continue */ }
e74abb32 1593 _ => return false,
ff7c6d11
XL
1594 }
1595 }
1596 true
1597 } else {
1598 false
1599 }
1600}
0731742a 1601
29967ef6
XL
1602fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1603 const ATTRS_TO_CHECK: &[Symbol] = &[
1604 sym::macro_export,
1605 sym::repr,
1606 sym::path,
1607 sym::automatically_derived,
1608 sym::start,
cdc7bbd5 1609 sym::rustc_main,
29967ef6
XL
1610 ];
1611
1612 for attr in attrs {
1613 for attr_to_check in ATTRS_TO_CHECK {
1614 if tcx.sess.check_name(attr, *attr_to_check) {
1615 tcx.sess
1616 .struct_span_err(
1617 attr.span,
1618 &format!(
1619 "`{}` attribute cannot be used at crate level",
1620 attr_to_check.to_ident_string()
1621 ),
1622 )
1623 .emit();
1624 }
1625 }
1626 }
1627}
1628
5869c6ff
XL
1629fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
1630 for attr in attrs {
1631 if tcx.sess.check_name(attr, sym::inline) {
1632 struct_span_err!(
1633 tcx.sess,
1634 attr.span,
1635 E0518,
1636 "attribute should be applied to function or closure",
1637 )
1638 .span_label(attr.span, "not a function or closure")
1639 .emit();
1640 }
1641 }
1642}
1643
f035d41b 1644fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
5869c6ff
XL
1645 let check_attr_visitor = &mut CheckAttrVisitor { tcx };
1646 tcx.hir().visit_item_likes_in_module(module_def_id, &mut check_attr_visitor.as_deep_visitor());
1647 tcx.hir().visit_exported_macros_in_krate(check_attr_visitor);
1648 check_invalid_macro_level_attr(tcx, tcx.hir().krate().non_exported_macro_attrs);
29967ef6 1649 if module_def_id.is_top_level_module() {
6a06907d 1650 check_attr_visitor.check_attributes(CRATE_HIR_ID, &DUMMY_SP, Target::Mod, None);
29967ef6
XL
1651 check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
1652 }
0731742a
XL
1653}
1654
f035d41b 1655pub(crate) fn provide(providers: &mut Providers) {
dfeec247 1656 *providers = Providers { check_mod_attrs, ..*providers };
0731742a 1657}