Skip to main content

rustc_lint/
autorefs.rs

1use rustc_ast::{BorrowKind, UnOp};
2use rustc_hir::{Expr, ExprKind, Mutability, find_attr};
3use rustc_middle::ty::adjustment::{
4    Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref,
5};
6use rustc_session::{declare_lint, declare_lint_pass};
7
8use crate::lints::{
9    ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin,
10    ImplicitUnsafeAutorefsSuggestion,
11};
12use crate::{LateContext, LateLintPass, LintContext};
13
14#[doc =
r" The `dangerous_implicit_autorefs` lint checks for implicitly taken references"]
#[doc = r" to dereferences of raw pointers."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {"]
#[doc = r"     unsafe { &raw mut (*ptr)[..16] }"]
#[doc =
r"     //                      ^^^^^^ this calls `IndexMut::index_mut(&mut ..., ..16)`,"]
#[doc =
r"     //                             implicitly creating a reference"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" When working with raw pointers it's usually undesirable to create references,"]
#[doc =
r" since they inflict additional safety requirements. Unfortunately, it's possible"]
#[doc =
r" to take a reference to the dereference of a raw pointer implicitly, which inflicts"]
#[doc = r" the usual reference requirements."]
#[doc = r""]
#[doc =
r" If you are sure that you can soundly take a reference, then you can take it explicitly:"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {"]
#[doc = r"     unsafe { &raw mut (&mut *ptr)[..16] }"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc =
r" Otherwise try to find an alternative way to achieve your goals using only raw pointers:"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" use std::ptr;"]
#[doc = r""]
#[doc = r" fn fun(ptr: *mut [u8]) -> *mut [u8] {"]
#[doc = r"     ptr::slice_from_raw_parts_mut(ptr.cast(), 16)"]
#[doc = r" }"]
#[doc = r" ```"]
pub static DANGEROUS_IMPLICIT_AUTOREFS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "DANGEROUS_IMPLICIT_AUTOREFS",
            default_level: ::rustc_lint_defs::Deny,
            desc: "implicit reference to a dereference of a raw pointer",
            is_externally_loaded: false,
            report_in_external_macro: true,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
15    /// The `dangerous_implicit_autorefs` lint checks for implicitly taken references
16    /// to dereferences of raw pointers.
17    ///
18    /// ### Example
19    ///
20    /// ```rust,compile_fail
21    /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {
22    ///     unsafe { &raw mut (*ptr)[..16] }
23    ///     //                      ^^^^^^ this calls `IndexMut::index_mut(&mut ..., ..16)`,
24    ///     //                             implicitly creating a reference
25    /// }
26    /// ```
27    ///
28    /// {{produces}}
29    ///
30    /// ### Explanation
31    ///
32    /// When working with raw pointers it's usually undesirable to create references,
33    /// since they inflict additional safety requirements. Unfortunately, it's possible
34    /// to take a reference to the dereference of a raw pointer implicitly, which inflicts
35    /// the usual reference requirements.
36    ///
37    /// If you are sure that you can soundly take a reference, then you can take it explicitly:
38    ///
39    /// ```rust
40    /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {
41    ///     unsafe { &raw mut (&mut *ptr)[..16] }
42    /// }
43    /// ```
44    ///
45    /// Otherwise try to find an alternative way to achieve your goals using only raw pointers:
46    ///
47    /// ```rust
48    /// use std::ptr;
49    ///
50    /// fn fun(ptr: *mut [u8]) -> *mut [u8] {
51    ///     ptr::slice_from_raw_parts_mut(ptr.cast(), 16)
52    /// }
53    /// ```
54    pub DANGEROUS_IMPLICIT_AUTOREFS,
55    Deny,
56    "implicit reference to a dereference of a raw pointer",
57    report_in_external_macro
58}
59
60pub struct ImplicitAutorefs;
#[automatically_derived]
impl ::core::marker::Copy for ImplicitAutorefs { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ImplicitAutorefs { }
#[automatically_derived]
impl ::core::clone::Clone for ImplicitAutorefs {
    #[inline]
    fn clone(&self) -> ImplicitAutorefs { *self }
}
impl ::rustc_lint_defs::LintPass for ImplicitAutorefs {
    fn name(&self) -> &'static str { "ImplicitAutorefs" }
    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(),
                [DANGEROUS_IMPLICIT_AUTOREFS]))
    }
}
impl ImplicitAutorefs {
    #[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(),
                [DANGEROUS_IMPLICIT_AUTOREFS]))
    }
}declare_lint_pass!(ImplicitAutorefs => [DANGEROUS_IMPLICIT_AUTOREFS]);
61
62impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs {
63    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
64        // This logic has mostly been taken from
65        // <https://github.com/rust-lang/rust/pull/103735#issuecomment-1370420305>
66
67        // 5. Either of the following:
68        //   a. A deref followed by any non-deref place projection (that intermediate
69        //      deref will typically be auto-inserted).
70        //   b. A method call annotated with `#[rustc_no_implicit_refs]`.
71        //   c. A deref followed by a `&raw const` or `&raw mut`.
72        let mut is_coming_from_deref = false;
73        let inner = match expr.kind {
74            ExprKind::AddrOf(BorrowKind::Raw, _, inner) => match inner.kind {
75                ExprKind::Unary(UnOp::Deref, inner) => {
76                    is_coming_from_deref = true;
77                    inner
78                }
79                _ => return,
80            },
81            ExprKind::Index(base, _, _) => base,
82            ExprKind::MethodCall(_, inner, _, _) => {
83                // PERF: Checking of `#[rustc_no_implicit_refs]` is deferred below
84                // because checking for attribute is a bit costly.
85                inner
86            }
87            ExprKind::Field(inner, _) => inner,
88            _ => return,
89        };
90
91        let typeck = cx.typeck_results();
92        let adjustments_table = typeck.adjustments();
93
94        if let Some(adjustments) = adjustments_table.get(inner.hir_id)
95            // 4. Any number of automatically inserted deref/derefmut calls.
96            && let adjustments = peel_derefs_adjustments(&**adjustments)
97            // 3. An automatically inserted reference (might come from a deref).
98            && let [adjustment] = adjustments
99            && let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment)
100            && let ExprKind::Unary(UnOp::Deref, dereferenced) =
101                // 2. Any number of place projections.
102                peel_place_mappers(inner).kind
103            // 1. Deref of a raw pointer.
104            && typeck.expr_ty(dereferenced).is_raw_ptr()
105            && let method_did = match expr.kind {
106                // PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]`
107                ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
108                _ => None,
109            }
110            && method_did.map(|did| {

        #[allow(deprecated)]
        {
            {
                'done:
                    {
                    for i in cx.tcx.get_all_attrs(did) {
                        #[allow(unused_imports)]
                        use rustc_hir::attrs::AttributeKind::*;
                        let i: &rustc_hir::Attribute = i;
                        match i {
                            rustc_hir::Attribute::Parsed(RustcNoImplicitAutorefs) => {
                                break 'done Some(());
                            }
                            rustc_hir::Attribute::Unparsed(..) =>
                                {}
                                #[deny(unreachable_patterns)]
                                _ => {}
                        }
                    }
                    None
                }
            }
        }
    }.is_some()find_attr!(cx.tcx, did, RustcNoImplicitAutorefs)).unwrap_or(true)
111        {
112            cx.emit_span_lint(
113                DANGEROUS_IMPLICIT_AUTOREFS,
114                expr.span.source_callsite(),
115                ImplicitUnsafeAutorefsDiag {
116                    raw_ptr_span: dereferenced.span,
117                    raw_ptr_ty: typeck.expr_ty(dereferenced),
118                    origin: if through_overloaded_deref {
119                        ImplicitUnsafeAutorefsOrigin::OverloadedDeref
120                    } else {
121                        ImplicitUnsafeAutorefsOrigin::Autoref {
122                            autoref_span: inner.span,
123                            autoref_ty: typeck.expr_ty_adjusted(inner),
124                        }
125                    },
126                    method: method_did.map(|did| ImplicitUnsafeAutorefsMethodNote {
127                        def_span: cx.tcx.def_span(did),
128                        method_name: cx.tcx.item_name(did),
129                    }),
130                    suggestion: ImplicitUnsafeAutorefsSuggestion {
131                        mutbl: borrow_mutbl.ref_prefix_str(),
132                        deref: if is_coming_from_deref { "*" } else { "" },
133                        start_span: inner.span.shrink_to_lo(),
134                        end_span: inner.span.shrink_to_hi(),
135                    },
136                },
137            )
138        }
139    }
140}
141
142/// Peels expressions from `expr` that can map a place.
143fn peel_place_mappers<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
144    loop {
145        match expr.kind {
146            ExprKind::Index(base, _idx, _) => expr = &base,
147            ExprKind::Field(e, _) => expr = &e,
148            _ => break expr,
149        }
150    }
151}
152
153/// Peel derefs adjustments until the last last element.
154fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustment<'a>] {
155    while let [Adjustment { kind: Adjust::Deref(_), .. }, end @ ..] = adjs
156        && !end.is_empty()
157    {
158        adjs = end;
159    }
160    adjs
161}
162
163/// Test if some adjustment has some implicit borrow.
164///
165/// Returns `Some((mutability, was_an_overloaded_deref))` if the argument adjustment is
166/// an implicit borrow (or has an implicit borrow via an overloaded deref).
167fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> {
168    match kind {
169        &Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => {
170            Some((mutbl, true))
171        }
172        &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)),
173        Adjust::NeverToAny
174        | Adjust::Pointer(..)
175        | Adjust::ReborrowPin(..)
176        | Adjust::Deref(DerefAdjustKind::Builtin)
177        | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None,
178    }
179}