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