]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/check_attr.rs
Auto merge of #67596 - Mark-Simulacrum:tidy-silence-rustfmt, r=Centril
[rust.git] / src / librustc / hir / check_attr.rs
1 //! This module implements some validity checks for attributes.
2 //! In particular it verifies that `#[inline]` and `#[repr]` attributes are
3 //! attached to items that actually support them and if there are
4 //! conflicts between multiple such attributes attached to the same
5 //! item.
6
7 use crate::hir::def_id::DefId;
8 use crate::hir::intravisit::{self, NestedVisitorMap, Visitor};
9 use crate::hir::DUMMY_HIR_ID;
10 use crate::hir::{self, Attribute, HirId, Item, ItemKind, TraitItem, TraitItemKind};
11 use crate::lint::builtin::UNUSED_ATTRIBUTES;
12 use crate::ty::query::Providers;
13 use crate::ty::TyCtxt;
14
15 use std::fmt::{self, Display};
16 use syntax::{attr, symbol::sym};
17 use syntax_pos::Span;
18
19 use rustc_error_codes::*;
20
21 #[derive(Copy, Clone, PartialEq)]
22 pub(crate) enum MethodKind {
23     Trait { body: bool },
24     Inherent,
25 }
26
27 #[derive(Copy, Clone, PartialEq)]
28 pub(crate) enum Target {
29     ExternCrate,
30     Use,
31     Static,
32     Const,
33     Fn,
34     Closure,
35     Mod,
36     ForeignMod,
37     GlobalAsm,
38     TyAlias,
39     OpaqueTy,
40     Enum,
41     Struct,
42     Union,
43     Trait,
44     TraitAlias,
45     Impl,
46     Expression,
47     Statement,
48     AssocConst,
49     Method(MethodKind),
50     AssocTy,
51     ForeignFn,
52     ForeignStatic,
53     ForeignTy,
54 }
55
56 impl Display for Target {
57     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58         write!(
59             f,
60             "{}",
61             match *self {
62                 Target::ExternCrate => "extern crate",
63                 Target::Use => "use",
64                 Target::Static => "static item",
65                 Target::Const => "constant item",
66                 Target::Fn => "function",
67                 Target::Closure => "closure",
68                 Target::Mod => "module",
69                 Target::ForeignMod => "foreign module",
70                 Target::GlobalAsm => "global asm",
71                 Target::TyAlias => "type alias",
72                 Target::OpaqueTy => "opaque type",
73                 Target::Enum => "enum",
74                 Target::Struct => "struct",
75                 Target::Union => "union",
76                 Target::Trait => "trait",
77                 Target::TraitAlias => "trait alias",
78                 Target::Impl => "item",
79                 Target::Expression => "expression",
80                 Target::Statement => "statement",
81                 Target::AssocConst => "associated const",
82                 Target::Method(_) => "method",
83                 Target::AssocTy => "associated type",
84                 Target::ForeignFn => "foreign function",
85                 Target::ForeignStatic => "foreign static item",
86                 Target::ForeignTy => "foreign type",
87             }
88         )
89     }
90 }
91
92 impl Target {
93     pub(crate) fn from_item(item: &Item<'_>) -> Target {
94         match item.kind {
95             ItemKind::ExternCrate(..) => Target::ExternCrate,
96             ItemKind::Use(..) => Target::Use,
97             ItemKind::Static(..) => Target::Static,
98             ItemKind::Const(..) => Target::Const,
99             ItemKind::Fn(..) => Target::Fn,
100             ItemKind::Mod(..) => Target::Mod,
101             ItemKind::ForeignMod(..) => Target::ForeignMod,
102             ItemKind::GlobalAsm(..) => Target::GlobalAsm,
103             ItemKind::TyAlias(..) => Target::TyAlias,
104             ItemKind::OpaqueTy(..) => Target::OpaqueTy,
105             ItemKind::Enum(..) => Target::Enum,
106             ItemKind::Struct(..) => Target::Struct,
107             ItemKind::Union(..) => Target::Union,
108             ItemKind::Trait(..) => Target::Trait,
109             ItemKind::TraitAlias(..) => Target::TraitAlias,
110             ItemKind::Impl(..) => Target::Impl,
111         }
112     }
113
114     fn from_trait_item(trait_item: &TraitItem<'_>) -> Target {
115         match trait_item.kind {
116             TraitItemKind::Const(..) => Target::AssocConst,
117             TraitItemKind::Method(_, hir::TraitMethod::Required(_)) => {
118                 Target::Method(MethodKind::Trait { body: false })
119             }
120             TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => {
121                 Target::Method(MethodKind::Trait { body: true })
122             }
123             TraitItemKind::Type(..) => Target::AssocTy,
124         }
125     }
126
127     fn from_foreign_item(foreign_item: &hir::ForeignItem<'_>) -> Target {
128         match foreign_item.kind {
129             hir::ForeignItemKind::Fn(..) => Target::ForeignFn,
130             hir::ForeignItemKind::Static(..) => Target::ForeignStatic,
131             hir::ForeignItemKind::Type => Target::ForeignTy,
132         }
133     }
134
135     fn from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
136         match impl_item.kind {
137             hir::ImplItemKind::Const(..) => Target::AssocConst,
138             hir::ImplItemKind::Method(..) => {
139                 let parent_hir_id = tcx.hir().get_parent_item(impl_item.hir_id);
140                 let containing_item = tcx.hir().expect_item(parent_hir_id);
141                 let containing_impl_is_for_trait = match &containing_item.kind {
142                     hir::ItemKind::Impl(_, _, _, _, tr, _, _) => tr.is_some(),
143                     _ => bug!("parent of an ImplItem must be an Impl"),
144                 };
145                 if containing_impl_is_for_trait {
146                     Target::Method(MethodKind::Trait { body: true })
147                 } else {
148                     Target::Method(MethodKind::Inherent)
149                 }
150             }
151             hir::ImplItemKind::TyAlias(..) | hir::ImplItemKind::OpaqueTy(..) => Target::AssocTy,
152         }
153     }
154 }
155
156 struct CheckAttrVisitor<'tcx> {
157     tcx: TyCtxt<'tcx>,
158 }
159
160 impl CheckAttrVisitor<'tcx> {
161     /// Checks any attribute.
162     fn check_attributes(
163         &self,
164         hir_id: HirId,
165         attrs: &'hir [Attribute],
166         span: &Span,
167         target: Target,
168         item: Option<&Item<'_>>,
169     ) {
170         let mut is_valid = true;
171         for attr in attrs {
172             is_valid &= if attr.check_name(sym::inline) {
173                 self.check_inline(hir_id, attr, span, target)
174             } else if attr.check_name(sym::non_exhaustive) {
175                 self.check_non_exhaustive(attr, span, target)
176             } else if attr.check_name(sym::marker) {
177                 self.check_marker(attr, span, target)
178             } else if attr.check_name(sym::target_feature) {
179                 self.check_target_feature(attr, span, target)
180             } else if attr.check_name(sym::track_caller) {
181                 self.check_track_caller(&attr.span, attrs, span, target)
182             } else {
183                 true
184             };
185         }
186
187         if !is_valid {
188             return;
189         }
190
191         if target == Target::Fn {
192             self.tcx.codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
193         }
194
195         self.check_repr(attrs, span, target, item);
196         self.check_used(attrs, target);
197     }
198
199     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
200     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
201         match target {
202             Target::Fn
203             | Target::Closure
204             | Target::Method(MethodKind::Trait { body: true })
205             | Target::Method(MethodKind::Inherent) => true,
206             Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
207                 self.tcx
208                     .struct_span_lint_hir(
209                         UNUSED_ATTRIBUTES,
210                         hir_id,
211                         attr.span,
212                         "`#[inline]` is ignored on function prototypes",
213                     )
214                     .emit();
215                 true
216             }
217             // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with
218             // just a lint, because we previously erroneously allowed it and some crates used it
219             // accidentally, to to be compatible with crates depending on them, we can't throw an
220             // error here.
221             Target::AssocConst => {
222                 self.tcx
223                     .struct_span_lint_hir(
224                         UNUSED_ATTRIBUTES,
225                         hir_id,
226                         attr.span,
227                         "`#[inline]` is ignored on constants",
228                     )
229                     .warn(
230                         "this was previously accepted by the compiler but is \
231                        being phased out; it will become a hard error in \
232                        a future release!",
233                     )
234                     .note(
235                         "for more information, see issue #65833 \
236                        <https://github.com/rust-lang/rust/issues/65833>",
237                     )
238                     .emit();
239                 true
240             }
241             _ => {
242                 struct_span_err!(
243                     self.tcx.sess,
244                     attr.span,
245                     E0518,
246                     "attribute should be applied to function or closure",
247                 )
248                 .span_label(*span, "not a function or closure")
249                 .emit();
250                 false
251             }
252         }
253     }
254
255     /// Checks if a `#[track_caller]` is applied to a non-naked function. Returns `true` if valid.
256     fn check_track_caller(
257         &self,
258         attr_span: &Span,
259         attrs: &'hir [Attribute],
260         span: &Span,
261         target: Target,
262     ) -> bool {
263         match target {
264             Target::Fn if attr::contains_name(attrs, sym::naked) => {
265                 struct_span_err!(
266                     self.tcx.sess,
267                     *attr_span,
268                     E0736,
269                     "cannot use `#[track_caller]` with `#[naked]`",
270                 )
271                 .emit();
272                 false
273             }
274             Target::Fn | Target::Method(MethodKind::Inherent) => true,
275             Target::Method(_) => {
276                 struct_span_err!(
277                     self.tcx.sess,
278                     *attr_span,
279                     E0738,
280                     "`#[track_caller]` may not be used on trait methods",
281                 )
282                 .emit();
283                 false
284             }
285             _ => {
286                 struct_span_err!(
287                     self.tcx.sess,
288                     *attr_span,
289                     E0739,
290                     "attribute should be applied to function"
291                 )
292                 .span_label(*span, "not a function")
293                 .emit();
294                 false
295             }
296         }
297     }
298
299     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
300     fn check_non_exhaustive(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
301         match target {
302             Target::Struct | Target::Enum => true,
303             _ => {
304                 struct_span_err!(
305                     self.tcx.sess,
306                     attr.span,
307                     E0701,
308                     "attribute can only be applied to a struct or enum"
309                 )
310                 .span_label(*span, "not a struct or enum")
311                 .emit();
312                 false
313             }
314         }
315     }
316
317     /// Checks if the `#[marker]` attribute on an `item` is valid. Returns `true` if valid.
318     fn check_marker(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
319         match target {
320             Target::Trait => true,
321             _ => {
322                 self.tcx
323                     .sess
324                     .struct_span_err(attr.span, "attribute can only be applied to a trait")
325                     .span_label(*span, "not a trait")
326                     .emit();
327                 false
328             }
329         }
330     }
331
332     /// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
333     fn check_target_feature(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
334         match target {
335             Target::Fn
336             | Target::Method(MethodKind::Trait { body: true })
337             | Target::Method(MethodKind::Inherent) => true,
338             _ => {
339                 self.tcx
340                     .sess
341                     .struct_span_err(attr.span, "attribute should be applied to a function")
342                     .span_label(*span, "not a function")
343                     .emit();
344                 false
345             }
346         }
347     }
348
349     /// Checks if the `#[repr]` attributes on `item` are valid.
350     fn check_repr(
351         &self,
352         attrs: &'hir [Attribute],
353         span: &Span,
354         target: Target,
355         item: Option<&Item<'_>>,
356     ) {
357         // Extract the names of all repr hints, e.g., [foo, bar, align] for:
358         // ```
359         // #[repr(foo)]
360         // #[repr(bar, align(8))]
361         // ```
362         let hints: Vec<_> = attrs
363             .iter()
364             .filter(|attr| attr.check_name(sym::repr))
365             .filter_map(|attr| attr.meta_item_list())
366             .flatten()
367             .collect();
368
369         let mut int_reprs = 0;
370         let mut is_c = false;
371         let mut is_simd = false;
372         let mut is_transparent = false;
373
374         for hint in &hints {
375             let (article, allowed_targets) = match hint.name_or_empty() {
376                 name @ sym::C | name @ sym::align => {
377                     is_c |= name == sym::C;
378                     match target {
379                         Target::Struct | Target::Union | Target::Enum => continue,
380                         _ => ("a", "struct, enum, or union"),
381                     }
382                 }
383                 sym::packed => {
384                     if target != Target::Struct && target != Target::Union {
385                         ("a", "struct or union")
386                     } else {
387                         continue;
388                     }
389                 }
390                 sym::simd => {
391                     is_simd = true;
392                     if target != Target::Struct { ("a", "struct") } else { continue }
393                 }
394                 sym::transparent => {
395                     is_transparent = true;
396                     match target {
397                         Target::Struct | Target::Union | Target::Enum => continue,
398                         _ => ("a", "struct, enum, or union"),
399                     }
400                 }
401                 sym::i8
402                 | sym::u8
403                 | sym::i16
404                 | sym::u16
405                 | sym::i32
406                 | sym::u32
407                 | sym::i64
408                 | sym::u64
409                 | sym::isize
410                 | sym::usize => {
411                     int_reprs += 1;
412                     if target != Target::Enum { ("an", "enum") } else { continue }
413                 }
414                 _ => continue,
415             };
416             self.emit_repr_error(
417                 hint.span(),
418                 *span,
419                 &format!("attribute should be applied to {}", allowed_targets),
420                 &format!("not {} {}", article, allowed_targets),
421             )
422         }
423
424         // Just point at all repr hints if there are any incompatibilities.
425         // This is not ideal, but tracking precisely which ones are at fault is a huge hassle.
426         let hint_spans = hints.iter().map(|hint| hint.span());
427
428         // Error on repr(transparent, <anything else>).
429         if is_transparent && hints.len() > 1 {
430             let hint_spans: Vec<_> = hint_spans.clone().collect();
431             span_err!(
432                 self.tcx.sess,
433                 hint_spans,
434                 E0692,
435                 "transparent {} cannot have other repr hints",
436                 target
437             );
438         }
439         // Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
440         if (int_reprs > 1)
441             || (is_simd && is_c)
442             || (int_reprs == 1 && is_c && item.map_or(false, |item| is_c_like_enum(item)))
443         {
444             let hint_spans: Vec<_> = hint_spans.collect();
445             span_warn!(self.tcx.sess, hint_spans, E0566, "conflicting representation hints");
446         }
447     }
448
449     fn emit_repr_error(
450         &self,
451         hint_span: Span,
452         label_span: Span,
453         hint_message: &str,
454         label_message: &str,
455     ) {
456         struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
457             .span_label(label_span, label_message)
458             .emit();
459     }
460
461     fn check_stmt_attributes(&self, stmt: &hir::Stmt) {
462         // When checking statements ignore expressions, they will be checked later
463         if let hir::StmtKind::Local(ref l) = stmt.kind {
464             for attr in l.attrs.iter() {
465                 if attr.check_name(sym::inline) {
466                     self.check_inline(DUMMY_HIR_ID, attr, &stmt.span, Target::Statement);
467                 }
468                 if attr.check_name(sym::repr) {
469                     self.emit_repr_error(
470                         attr.span,
471                         stmt.span,
472                         "attribute should not be applied to a statement",
473                         "not a struct, enum, or union",
474                     );
475                 }
476             }
477         }
478     }
479
480     fn check_expr_attributes(&self, expr: &hir::Expr) {
481         let target = match expr.kind {
482             hir::ExprKind::Closure(..) => Target::Closure,
483             _ => Target::Expression,
484         };
485         for attr in expr.attrs.iter() {
486             if attr.check_name(sym::inline) {
487                 self.check_inline(DUMMY_HIR_ID, attr, &expr.span, target);
488             }
489             if attr.check_name(sym::repr) {
490                 self.emit_repr_error(
491                     attr.span,
492                     expr.span,
493                     "attribute should not be applied to an expression",
494                     "not defining a struct, enum, or union",
495                 );
496             }
497         }
498     }
499
500     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
501         for attr in attrs {
502             if attr.check_name(sym::used) && target != Target::Static {
503                 self.tcx
504                     .sess
505                     .span_err(attr.span, "attribute must be applied to a `static` variable");
506             }
507         }
508     }
509 }
510
511 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
512     fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
513         NestedVisitorMap::OnlyBodies(&self.tcx.hir())
514     }
515
516     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
517         let target = Target::from_item(item);
518         self.check_attributes(item.hir_id, item.attrs, &item.span, target, Some(item));
519         intravisit::walk_item(self, item)
520     }
521
522     fn visit_trait_item(&mut self, trait_item: &'tcx TraitItem<'tcx>) {
523         let target = Target::from_trait_item(trait_item);
524         self.check_attributes(trait_item.hir_id, &trait_item.attrs, &trait_item.span, target, None);
525         intravisit::walk_trait_item(self, trait_item)
526     }
527
528     fn visit_foreign_item(&mut self, f_item: &'tcx hir::ForeignItem<'tcx>) {
529         let target = Target::from_foreign_item(f_item);
530         self.check_attributes(f_item.hir_id, &f_item.attrs, &f_item.span, target, None);
531         intravisit::walk_foreign_item(self, f_item)
532     }
533
534     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
535         let target = Target::from_impl_item(self.tcx, impl_item);
536         self.check_attributes(impl_item.hir_id, &impl_item.attrs, &impl_item.span, target, None);
537         intravisit::walk_impl_item(self, impl_item)
538     }
539
540     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
541         self.check_stmt_attributes(stmt);
542         intravisit::walk_stmt(self, stmt)
543     }
544
545     fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
546         self.check_expr_attributes(expr);
547         intravisit::walk_expr(self, expr)
548     }
549 }
550
551 fn is_c_like_enum(item: &Item<'_>) -> bool {
552     if let ItemKind::Enum(ref def, _) = item.kind {
553         for variant in def.variants {
554             match variant.data {
555                 hir::VariantData::Unit(..) => { /* continue */ }
556                 _ => return false,
557             }
558         }
559         true
560     } else {
561         false
562     }
563 }
564
565 fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: DefId) {
566     tcx.hir()
567         .visit_item_likes_in_module(module_def_id, &mut CheckAttrVisitor { tcx }.as_deep_visitor());
568 }
569
570 pub(crate) fn provide(providers: &mut Providers<'_>) {
571     *providers = Providers { check_mod_attrs, ..*providers };
572 }