Skip to main content

rustc_lint/
disallowed_pass_by_ref.rs

1use rustc_hir::def::Res;
2use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, find_attr};
3use rustc_middle::ty;
4use rustc_session::{declare_lint_pass, declare_tool_lint};
5
6use crate::lints::DisallowedPassByRefDiag;
7use crate::{LateContext, LateLintPass, LintContext};
8
9#[doc =
r" The `disallowed_pass_by_ref` lint detects if types marked with `#[rustc_pass_by_value]` are"]
#[doc =
r" passed by reference. Types with this marker are usually thin wrappers around references, so"]
#[doc =
r" there is no benefit to an extra layer of indirection. (Example: `Ty` which is a reference"]
#[doc = r" to an `Interned<TyKind>`)"]
pub static DISALLOWED_PASS_BY_REF: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: &"rustc::DISALLOWED_PASS_BY_REF",
            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! {
10    /// The `disallowed_pass_by_ref` lint detects if types marked with `#[rustc_pass_by_value]` are
11    /// passed by reference. Types with this marker are usually thin wrappers around references, so
12    /// there is no benefit to an extra layer of indirection. (Example: `Ty` which is a reference
13    /// to an `Interned<TyKind>`)
14    pub rustc::DISALLOWED_PASS_BY_REF,
15    Warn,
16    "pass by reference of a type flagged as `#[rustc_pass_by_value]`",
17    report_in_external_macro: true
18}
19
20pub struct DisallowedPassByRef;
#[automatically_derived]
impl ::core::marker::Copy for DisallowedPassByRef { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DisallowedPassByRef { }
#[automatically_derived]
impl ::core::clone::Clone for DisallowedPassByRef {
    #[inline]
    fn clone(&self) -> DisallowedPassByRef { *self }
}
impl ::rustc_lint_defs::LintPass for DisallowedPassByRef {
    fn name(&self) -> &'static str { "DisallowedPassByRef" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [DISALLOWED_PASS_BY_REF]))
    }
}
impl DisallowedPassByRef {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [DISALLOWED_PASS_BY_REF]))
    }
}declare_lint_pass!(DisallowedPassByRef => [DISALLOWED_PASS_BY_REF]);
21
22impl<'tcx> LateLintPass<'tcx> for DisallowedPassByRef {
23    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
24        match &ty.kind {
25            TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
26                if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() {
27                    return;
28                }
29                if let Some(t) = path_for_rustc_pass_by_value(cx, inner_ty) {
30                    cx.emit_span_lint(
31                        DISALLOWED_PASS_BY_REF,
32                        ty.span,
33                        DisallowedPassByRefDiag { ty: t, suggestion: ty.span },
34                    );
35                }
36            }
37            _ => {}
38        }
39    }
40}
41
42fn path_for_rustc_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<String> {
43    if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
44        match path.res {
45            Res::Def(_, def_id) if {
        {
            'done:
                {
                for i in
                    ::rustc_hir::attrs::HasAttrs::get_attrs(def_id, &cx.tcx) {
                    #[allow(unused_imports)]
                    use rustc_hir::attrs::AttributeKind::*;
                    let i: &rustc_hir::Attribute = i;
                    match i {
                        rustc_hir::Attribute::Parsed(RustcPassByValue(_)) => {
                            break 'done Some(());
                        }
                        rustc_hir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(cx.tcx, def_id, RustcPassByValue(_)) => {
46                let name = cx.tcx.item_ident(def_id);
47                let path_segment = path.segments.last().unwrap();
48                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)));
49            }
50            Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
51                if let ty::Adt(adt, args) =
52                    cx.tcx.type_of(did).instantiate_identity().skip_norm_wip().kind()
53                {
54                    if {
        {
            'done:
                {
                for i in
                    ::rustc_hir::attrs::HasAttrs::get_attrs(adt.did(), &cx.tcx)
                    {
                    #[allow(unused_imports)]
                    use rustc_hir::attrs::AttributeKind::*;
                    let i: &rustc_hir::Attribute = i;
                    match i {
                        rustc_hir::Attribute::Parsed(RustcPassByValue(_)) => {
                            break 'done Some(());
                        }
                        rustc_hir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(cx.tcx, adt.did(), RustcPassByValue(_)) {
55                        return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
56                    }
57                }
58            }
59            _ => (),
60        }
61    }
62
63    None
64}
65
66fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
67    if let Some(args) = &segment.args {
68        let params = args
69            .args
70            .iter()
71            .map(|arg| match arg {
72                GenericArg::Lifetime(lt) => lt.to_string(),
73                GenericArg::Type(ty) => {
74                    cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
75                }
76                GenericArg::Const(c) => {
77                    cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into())
78                }
79                GenericArg::Infer(_) => String::from("_"),
80            })
81            .collect::<Vec<_>>();
82
83        if !params.is_empty() {
84            return ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("<{0}>", params.join(", ")))
    })format!("<{}>", params.join(", "));
85        }
86    }
87
88    String::new()
89}