rustc_lint/
pass_by_value.rs1use rustc_attr_data_structures::{AttributeKind, find_attr};
2use rustc_hir::def::Res;
3use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind};
4use rustc_middle::ty;
5use rustc_session::{declare_lint_pass, declare_tool_lint};
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)
49 if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::PassByValue(_)) =>
50 {
51 let name = cx.tcx.item_ident(def_id);
52 let path_segment = path.segments.last().unwrap();
53 return Some(format!("{}{}", name, gen_args(cx, path_segment)));
54 }
55 Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
56 if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() {
57 if find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::PassByValue(_)) {
58 return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
59 }
60 }
61 }
62 _ => (),
63 }
64 }
65
66 None
67}
68
69fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
70 if let Some(args) = &segment.args {
71 let params = args
72 .args
73 .iter()
74 .map(|arg| match arg {
75 GenericArg::Lifetime(lt) => lt.to_string(),
76 GenericArg::Type(ty) => {
77 cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
78 }
79 GenericArg::Const(c) => cx
80 .tcx
81 .sess
82 .source_map()
83 .span_to_snippet(c.span())
84 .unwrap_or_else(|_| "_".into()),
85 GenericArg::Infer(_) => String::from("_"),
86 })
87 .collect::<Vec<_>>();
88
89 if !params.is_empty() {
90 return format!("<{}>", params.join(", "));
91 }
92 }
93
94 String::new()
95}