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