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 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 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 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 && let adjustments = peel_derefs_adjustments(&**adjustments)
97 && let [adjustment] = adjustments
99 && let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment)
100 && let ExprKind::Unary(UnOp::Deref, dereferenced) =
101 peel_place_mappers(inner).kind
103 && typeck.expr_ty(dereferenced).is_raw_ptr()
105 && let method_did = match expr.kind {
106 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
142fn 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
153fn 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
163fn 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}