| 
 | 1 | +use rustc_ast::{BorrowKind, UnOp};  | 
 | 2 | +use rustc_hir::{Expr, ExprKind, Mutability};  | 
 | 3 | +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref};  | 
 | 4 | +use rustc_session::{declare_lint, declare_lint_pass};  | 
 | 5 | +use rustc_span::sym;  | 
 | 6 | + | 
 | 7 | +use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion};  | 
 | 8 | +use crate::{LateContext, LateLintPass, LintContext};  | 
 | 9 | + | 
 | 10 | +declare_lint! {  | 
 | 11 | +    /// The `dangerous_implicit_autorefs` lint checks for implicitly taken references  | 
 | 12 | +    /// to dereferences of raw pointers.  | 
 | 13 | +    ///  | 
 | 14 | +    /// ### Example  | 
 | 15 | +    ///  | 
 | 16 | +    /// ```rust  | 
 | 17 | +    /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {  | 
 | 18 | +    ///     &raw mut (*ptr)[..16]  | 
 | 19 | +    ///     //             ^^^^^^ this calls `IndexMut::index_mut(&mut ..., ..16)`,  | 
 | 20 | +    ///     //                    implicitly creating a reference  | 
 | 21 | +    /// }  | 
 | 22 | +    /// ```  | 
 | 23 | +    ///  | 
 | 24 | +    /// {{produces}}  | 
 | 25 | +    ///  | 
 | 26 | +    /// ### Explanation  | 
 | 27 | +    ///  | 
 | 28 | +    /// When working with raw pointers it's usually undesirable to create references,  | 
 | 29 | +    /// since they inflict additional safety requirements. Unfortunately, it's possible  | 
 | 30 | +    /// to take a reference to the dereference of a raw pointer implicitly, which inflicts  | 
 | 31 | +    /// the usual reference requirements.  | 
 | 32 | +    ///  | 
 | 33 | +    /// If you are sure that you can soundly take a reference, then you can take it explicitly:  | 
 | 34 | +    ///  | 
 | 35 | +    /// ```rust  | 
 | 36 | +    /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {  | 
 | 37 | +    ///     &raw mut (&mut *ptr)[..16]  | 
 | 38 | +    /// }  | 
 | 39 | +    /// ```  | 
 | 40 | +    ///  | 
 | 41 | +    /// Otherwise try to find an alternative way to achive your goals using only raw pointers:  | 
 | 42 | +    ///  | 
 | 43 | +    /// ```rust  | 
 | 44 | +    /// use std::slice;  | 
 | 45 | +    ///  | 
 | 46 | +    /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] {  | 
 | 47 | +    ///     slice::from_raw_parts_mut(ptr.cast(), 16)  | 
 | 48 | +    /// }  | 
 | 49 | +    /// ```  | 
 | 50 | +    pub DANGEROUS_IMPLICIT_AUTOREFS,  | 
 | 51 | +    Warn,  | 
 | 52 | +    "implicit reference to a dereference of a raw pointer",  | 
 | 53 | +    report_in_external_macro  | 
 | 54 | +}  | 
 | 55 | + | 
 | 56 | +declare_lint_pass!(ImplicitAutorefs => [DANGEROUS_IMPLICIT_AUTOREFS]);  | 
 | 57 | + | 
 | 58 | +impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs {  | 
 | 59 | +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {  | 
 | 60 | +        // This logic has mostly been taken from  | 
 | 61 | +        // <https://github.com/rust-lang/rust/pull/103735#issuecomment-1370420305>  | 
 | 62 | + | 
 | 63 | +        // 5. Either of the following:  | 
 | 64 | +        //   a. A deref followed by any non-deref place projection (that intermediate  | 
 | 65 | +        //      deref will typically be auto-inserted).  | 
 | 66 | +        //   b. A method call annotated with `#[rustc_no_implicit_refs]`.  | 
 | 67 | +        //   c. A deref followed by a `&raw const` or `&raw mut`.  | 
 | 68 | +        let mut is_coming_from_deref = false;  | 
 | 69 | +        let inner = match expr.kind {  | 
 | 70 | +            ExprKind::AddrOf(BorrowKind::Raw, _, inner) => match inner.kind {  | 
 | 71 | +                ExprKind::Unary(UnOp::Deref, inner) => {  | 
 | 72 | +                    is_coming_from_deref = true;  | 
 | 73 | +                    inner  | 
 | 74 | +                }  | 
 | 75 | +                _ => return,  | 
 | 76 | +            },  | 
 | 77 | +            ExprKind::Index(base, _, _) => base,  | 
 | 78 | +            ExprKind::MethodCall(_, inner, _, _)  | 
 | 79 | +                if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)  | 
 | 80 | +                    && cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) =>  | 
 | 81 | +            {  | 
 | 82 | +                inner  | 
 | 83 | +            }  | 
 | 84 | +            ExprKind::Field(inner, _) => inner,  | 
 | 85 | +            _ => return,  | 
 | 86 | +        };  | 
 | 87 | + | 
 | 88 | +        let typeck = cx.typeck_results();  | 
 | 89 | +        let adjustments_table = typeck.adjustments();  | 
 | 90 | + | 
 | 91 | +        if let Some(adjustments) = adjustments_table.get(inner.hir_id)  | 
 | 92 | +            // 4. Any number of automatically inserted deref/derefmut calls.  | 
 | 93 | +            && let adjustments = peel_derefs_adjustments(&**adjustments)  | 
 | 94 | +            // 3. An automatically inserted reference (might come from a deref).  | 
 | 95 | +            && let [adjustment] = adjustments  | 
 | 96 | +            && let Some(borrow_mutbl) = has_implicit_borrow(adjustment)  | 
 | 97 | +            && let ExprKind::Unary(UnOp::Deref, dereferenced) =  | 
 | 98 | +                // 2. Any number of place projections.  | 
 | 99 | +                peel_place_mappers(inner).kind  | 
 | 100 | +            // 1. Deref of a raw pointer.  | 
 | 101 | +            && typeck.expr_ty(dereferenced).is_raw_ptr()  | 
 | 102 | +        {  | 
 | 103 | +            cx.emit_span_lint(  | 
 | 104 | +                DANGEROUS_IMPLICIT_AUTOREFS,  | 
 | 105 | +                expr.span.source_callsite(),  | 
 | 106 | +                ImplicitUnsafeAutorefsDiag {  | 
 | 107 | +                    suggestion: ImplicitUnsafeAutorefsSuggestion {  | 
 | 108 | +                        mutbl: borrow_mutbl.ref_prefix_str(),  | 
 | 109 | +                        deref: if is_coming_from_deref { "*" } else { "" },  | 
 | 110 | +                        start_span: inner.span.shrink_to_lo(),  | 
 | 111 | +                        end_span: inner.span.shrink_to_hi(),  | 
 | 112 | +                    },  | 
 | 113 | +                },  | 
 | 114 | +            )  | 
 | 115 | +        }  | 
 | 116 | +    }  | 
 | 117 | +}  | 
 | 118 | + | 
 | 119 | +/// Peels expressions from `expr` that can map a place.  | 
 | 120 | +fn peel_place_mappers<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {  | 
 | 121 | +    loop {  | 
 | 122 | +        match expr.kind {  | 
 | 123 | +            ExprKind::Index(base, _idx, _) => expr = &base,  | 
 | 124 | +            ExprKind::Field(e, _) => expr = &e,  | 
 | 125 | +            _ => break expr,  | 
 | 126 | +        }  | 
 | 127 | +    }  | 
 | 128 | +}  | 
 | 129 | + | 
 | 130 | +/// Peel derefs adjustments until the last last element.  | 
 | 131 | +fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustment<'a>] {  | 
 | 132 | +    while let [Adjustment { kind: Adjust::Deref(_), .. }, end @ ..] = adjs  | 
 | 133 | +        && !end.is_empty()  | 
 | 134 | +    {  | 
 | 135 | +        adjs = end;  | 
 | 136 | +    }  | 
 | 137 | +    adjs  | 
 | 138 | +}  | 
 | 139 | + | 
 | 140 | +/// Test if some adjustment has some implicit borrow.  | 
 | 141 | +///  | 
 | 142 | +/// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it.  | 
 | 143 | +fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<Mutability> {  | 
 | 144 | +    match kind {  | 
 | 145 | +        &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some(mutbl),  | 
 | 146 | +        &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some(mutbl.into()),  | 
 | 147 | +        Adjust::NeverToAny  | 
 | 148 | +        | Adjust::Pointer(..)  | 
 | 149 | +        | Adjust::ReborrowPin(..)  | 
 | 150 | +        | Adjust::Deref(None)  | 
 | 151 | +        | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None,  | 
 | 152 | +    }  | 
 | 153 | +}  | 
0 commit comments