Skip to main content

rustc_lint/
pass_by_value.rs

1use rustc_hir::attrs::AttributeKind;
2use rustc_hir::def::Res;
3use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, find_attr};
4use rustc_middle::ty;
5use rustc_session::{declare_lint_pass, declare_tool_lint};
6
7use crate::lints::PassByValueDiag;
8use crate::{LateContext, LateLintPass, LintContext};
9
10#[doc =
r" The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to"]
#[doc =
r" always be passed by value. This is usually used for types that are thin wrappers around"]
#[doc =
r" references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which"]
#[doc = r" is a reference to an `Interned<TyKind>`)"]
pub static PASS_BY_VALUE: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: &"rustc::PASS_BY_VALUE",
            default_level: ::rustc_lint_defs::Warn,
            desc: "pass by reference of a type flagged as `#[rustc_pass_by_value]`",
            edition_lint_opts: None,
            report_in_external_macro: true,
            future_incompatible: None,
            is_externally_loaded: true,
            crate_level_only: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_tool_lint! {
11    /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to
12    /// always be passed by value. This is usually used for types that are thin wrappers around
13    /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which
14    /// is a reference to an `Interned<TyKind>`)
15    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
21pub struct PassByValue;
#[automatically_derived]
impl ::core::marker::Copy for PassByValue { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for PassByValue { }
#[automatically_derived]
impl ::core::clone::Clone for PassByValue {
    #[inline]
    fn clone(&self) -> PassByValue { *self }
}
impl ::rustc_lint_defs::LintPass for PassByValue {
    fn name(&self) -> &'static str { "PassByValue" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        <[_]>::into_vec(::alloc::boxed::box_new([PASS_BY_VALUE]))
    }
}
impl PassByValue {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        <[_]>::into_vec(::alloc::boxed::box_new([PASS_BY_VALUE]))
    }
}declare_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 cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() {
28                    return;
29                }
30                if let Some(t) = path_for_pass_by_value(cx, inner_ty) {
31                    cx.emit_span_lint(
32                        PASS_BY_VALUE,
33                        ty.span,
34                        PassByValueDiag { ty: t, suggestion: ty.span },
35                    );
36                }
37            }
38            _ => {}
39        }
40    }
41}
42
43fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<String> {
44    if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
45        match path.res {
46            Res::Def(_, def_id)
47                if {
    {
            'done:
                {
                for i in cx.tcx.get_all_attrs(def_id) {
                    let i: &rustc_hir::Attribute = i;
                    match i {
                        rustc_hir::Attribute::Parsed(AttributeKind::RustcPassByValue(_))
                            => {
                            break 'done Some(());
                        }
                        _ => {}
                    }
                }
                None
            }
        }.is_some()
}find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcPassByValue(_)) =>
48            {
49                let name = cx.tcx.item_ident(def_id);
50                let path_segment = path.segments.last().unwrap();
51                return Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}{1}", name,
                gen_args(cx, path_segment)))
    })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 {
    {
            'done:
                {
                for i in cx.tcx.get_all_attrs(adt.did()) {
                    let i: &rustc_hir::Attribute = i;
                    match i {
                        rustc_hir::Attribute::Parsed(AttributeKind::RustcPassByValue(_))
                            => {
                            break 'done Some(());
                        }
                        _ => {}
                    }
                }
                None
            }
        }.is_some()
}find_attr!(
56                        cx.tcx.get_all_attrs(adt.did()),
57                        AttributeKind::RustcPassByValue(_)
58                    ) {
59                        return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
60                    }
61                }
62            }
63            _ => (),
64        }
65    }
66
67    None
68}
69
70fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
71    if let Some(args) = &segment.args {
72        let params = args
73            .args
74            .iter()
75            .map(|arg| match arg {
76                GenericArg::Lifetime(lt) => lt.to_string(),
77                GenericArg::Type(ty) => {
78                    cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
79                }
80                GenericArg::Const(c) => {
81                    cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into())
82                }
83                GenericArg::Infer(_) => String::from("_"),
84            })
85            .collect::<Vec<_>>();
86
87        if !params.is_empty() {
88            return ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("<{0}>", params.join(", ")))
    })format!("<{}>", params.join(", "));
89        }
90    }
91
92    String::new()
93}