]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/pass_by_value.rs
migrate: `pass_by_value.rs`
[rust.git] / compiler / rustc_lint / src / pass_by_value.rs
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use crate::lints::PassByValueDiag;
4 use crate::{LateContext, LateLintPass, LintContext};
5 use rustc_hir as hir;
6 use rustc_hir::def::Res;
7 use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
8 use rustc_middle::ty;
9 use rustc_span::symbol::sym;
10
11 declare_tool_lint! {
12     /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to
13     /// always be passed by value. This is usually used for types that are thin wrappers around
14     /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which
15     /// is a reference to an `Interned<TyKind>`)
16     pub rustc::PASS_BY_VALUE,
17     Warn,
18     "pass by reference of a type flagged as `#[rustc_pass_by_value]`",
19     report_in_external_macro: true
20 }
21
22 declare_lint_pass!(PassByValue => [PASS_BY_VALUE]);
23
24 impl<'tcx> LateLintPass<'tcx> for PassByValue {
25     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
26         match &ty.kind {
27             TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
28                 if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
29                     if cx.tcx.impl_trait_ref(impl_did).is_some() {
30                         return;
31                     }
32                 }
33                 if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
34                     cx.emit_spanned_lint(
35                         PASS_BY_VALUE,
36                         ty.span,
37                         PassByValueDiag { ty: t.clone(), suggestion: ty.span },
38                     );
39                 }
40             }
41             _ => {}
42         }
43     }
44 }
45
46 fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<String> {
47     if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
48         match path.res {
49             Res::Def(_, def_id) if cx.tcx.has_attr(def_id, sym::rustc_pass_by_value) => {
50                 let name = cx.tcx.item_name(def_id).to_ident_string();
51                 let path_segment = path.segments.last().unwrap();
52                 return Some(format!("{}{}", name, gen_args(cx, path_segment)));
53             }
54             Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
55                 if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
56                     if cx.tcx.has_attr(adt.did(), sym::rustc_pass_by_value) {
57                         return Some(cx.tcx.def_path_str_with_substs(adt.did(), substs));
58                     }
59                 }
60             }
61             _ => (),
62         }
63     }
64
65     None
66 }
67
68 fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
69     if let Some(args) = &segment.args {
70         let params = args
71             .args
72             .iter()
73             .map(|arg| match arg {
74                 GenericArg::Lifetime(lt) => lt.to_string(),
75                 GenericArg::Type(ty) => {
76                     cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
77                 }
78                 GenericArg::Const(c) => {
79                     cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into())
80                 }
81                 GenericArg::Infer(_) => String::from("_"),
82             })
83             .collect::<Vec<_>>();
84
85         if !params.is_empty() {
86             return format!("<{}>", params.join(", "));
87         }
88     }
89
90     String::new()
91 }