rustc_lint/
pass_by_value.rs
1use rustc_hir::def::Res;
2use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind};
3use rustc_middle::ty;
4use rustc_session::{declare_lint_pass, declare_tool_lint};
5use rustc_span::sym;
6
7use crate::lints::PassByValueDiag;
8use crate::{LateContext, LateLintPass, LintContext};
9
10declare_tool_lint! {
11 pub rustc::PASS_BY_VALUE,
16 Warn,
17 "pass by reference of a type flagged as `#[rustc_pass_by_value]`",
18 report_in_external_macro: true
19}
20
21declare_lint_pass!(PassByValue => [PASS_BY_VALUE]);
22
23impl<'tcx> LateLintPass<'tcx> for PassByValue {
24 fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
25 match &ty.kind {
26 TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
27 if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
28 if cx.tcx.impl_trait_ref(impl_did).is_some() {
29 return;
30 }
31 }
32 if let Some(t) = path_for_pass_by_value(cx, inner_ty) {
33 cx.emit_span_lint(
34 PASS_BY_VALUE,
35 ty.span,
36 PassByValueDiag { ty: t, suggestion: ty.span },
37 );
38 }
39 }
40 _ => {}
41 }
42 }
43}
44
45fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<String> {
46 if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
47 match path.res {
48 Res::Def(_, def_id) if cx.tcx.has_attr(def_id, sym::rustc_pass_by_value) => {
49 let name = cx.tcx.item_ident(def_id);
50 let path_segment = path.segments.last().unwrap();
51 return Some(format!("{}{}", name, gen_args(cx, path_segment)));
52 }
53 Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
54 if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() {
55 if cx.tcx.has_attr(adt.did(), sym::rustc_pass_by_value) {
56 return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
57 }
58 }
59 }
60 _ => (),
61 }
62 }
63
64 None
65}
66
67fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
68 if let Some(args) = &segment.args {
69 let params = args
70 .args
71 .iter()
72 .map(|arg| match arg {
73 GenericArg::Lifetime(lt) => lt.to_string(),
74 GenericArg::Type(ty) => {
75 cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
76 }
77 GenericArg::Const(c) => cx
78 .tcx
79 .sess
80 .source_map()
81 .span_to_snippet(c.span())
82 .unwrap_or_else(|_| "_".into()),
83 GenericArg::Infer(_) => String::from("_"),
84 })
85 .collect::<Vec<_>>();
86
87 if !params.is_empty() {
88 return format!("<{}>", params.join(", "));
89 }
90 }
91
92 String::new()
93}