Skip to content

Improve dangerous_implicit_aurorefs diagnostic output #140768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ lint_impl_trait_redundant_captures = all possible in-scope parameters are alread

lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer
.note = creating a reference requires the pointer target to be valid and imposes aliasing requirements
.raw_ptr = this raw pointer has type `{$raw_ptr_ty}`
.autoref = autoref is being applied to this expression, resulting in: `{$autoref_ty}`
Copy link
Member

@fmease fmease May 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about rephrasing "autoref is being applied" to sth. along the lines of "references to this expression are implicitly created", "references are implicitly applied ...", ...

Is that still something we want to pursue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I wanted to do that but given that the end result type is not going to have a visible reference I just removed the "autoref" note, and just show the deref one.

.overloaded_deref = references are created through calls to explicit `Deref(Mut)::deref(_mut)` implementations
.method_def = method calls to `{$method_name}` require a reference
.suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit

lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe
Expand Down
42 changes: 29 additions & 13 deletions compiler/rustc_lint/src/autorefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDer
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::sym;

use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion};
use crate::lints::{
ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin,
ImplicitUnsafeAutorefsSuggestion,
};
use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
Expand Down Expand Up @@ -92,25 +95,37 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs {
&& let adjustments = peel_derefs_adjustments(&**adjustments)
// 3. An automatically inserted reference (might come from a deref).
&& let [adjustment] = adjustments
&& let Some(borrow_mutbl) = has_implicit_borrow(adjustment)
&& let Some((borrow_mutbl, through_overloaded_deref)) = has_implicit_borrow(adjustment)
&& let ExprKind::Unary(UnOp::Deref, dereferenced) =
// 2. Any number of place projections.
peel_place_mappers(inner).kind
// 1. Deref of a raw pointer.
&& typeck.expr_ty(dereferenced).is_raw_ptr()
// PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]`
&& match expr.kind {
ExprKind::MethodCall(..) => matches!(
cx.typeck_results().type_dependent_def_id(expr.hir_id),
Some(def_id) if cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs)
),
_ => true,
&& let method_did = match expr.kind {
// PERF: 5. b. A method call annotated with `#[rustc_no_implicit_refs]`
ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
_ => None,
}
&& method_did.map(|did| cx.tcx.has_attr(did, sym::rustc_no_implicit_autorefs)).unwrap_or(true)
{
cx.emit_span_lint(
DANGEROUS_IMPLICIT_AUTOREFS,
expr.span.source_callsite(),
ImplicitUnsafeAutorefsDiag {
raw_ptr_span: dereferenced.span,
raw_ptr_ty: typeck.expr_ty(dereferenced),
origin: if through_overloaded_deref {
ImplicitUnsafeAutorefsOrigin::OverloadedDeref
} else {
ImplicitUnsafeAutorefsOrigin::Autoref {
autoref_span: inner.span,
autoref_ty: typeck.expr_ty_adjusted(inner),
}
},
method: method_did.map(|did| ImplicitUnsafeAutorefsMethodNote {
def_span: cx.tcx.def_span(did),
method_name: cx.tcx.item_name(did),
}),
suggestion: ImplicitUnsafeAutorefsSuggestion {
mutbl: borrow_mutbl.ref_prefix_str(),
deref: if is_coming_from_deref { "*" } else { "" },
Expand Down Expand Up @@ -146,11 +161,12 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen

/// Test if some adjustment has some implicit borrow.
///
/// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it.
fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<Mutability> {
/// Returns `Some((mutability, was_an_overloaded_deref))` if the argument adjustment is
/// an implicit borrow (or has an implicit borrow via an overloaded deref).
fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> {
match kind {
&Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some(mutbl),
&Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some(mutbl.into()),
&Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)),
&Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)),
Adjust::NeverToAny
| Adjust::Pointer(..)
| Adjust::ReborrowPin(..)
Expand Down
29 changes: 28 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,38 @@ pub(crate) enum ShadowedIntoIterDiagSub {
#[derive(LintDiagnostic)]
#[diag(lint_implicit_unsafe_autorefs)]
#[note]
pub(crate) struct ImplicitUnsafeAutorefsDiag {
pub(crate) struct ImplicitUnsafeAutorefsDiag<'a> {
#[label(lint_raw_ptr)]
pub raw_ptr_span: Span,
pub raw_ptr_ty: Ty<'a>,
#[subdiagnostic]
pub origin: ImplicitUnsafeAutorefsOrigin<'a>,
#[subdiagnostic]
pub method: Option<ImplicitUnsafeAutorefsMethodNote>,
#[subdiagnostic]
pub suggestion: ImplicitUnsafeAutorefsSuggestion,
}

#[derive(Subdiagnostic)]
pub(crate) enum ImplicitUnsafeAutorefsOrigin<'a> {
#[note(lint_autoref)]
Autoref {
#[primary_span]
autoref_span: Span,
autoref_ty: Ty<'a>,
},
#[note(lint_overloaded_deref)]
OverloadedDeref,
}

#[derive(Subdiagnostic)]
#[note(lint_method_def)]
pub(crate) struct ImplicitUnsafeAutorefsMethodNote {
#[primary_span]
pub def_span: Span,
pub method_name: Symbol,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")]
pub(crate) struct ImplicitUnsafeAutorefsSuggestion {
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/lint/implicit_autorefs.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) {
//~^ WARN implicit autoref
}

unsafe fn slice_ptr_len_because_of_msrv<T>(slice: *const [T]) {
let _ = (&(&(*slice))[..]).len();
//~^ WARN implicit autoref
//~^^ WARN implicit autoref
}

fn main() {}
6 changes: 6 additions & 0 deletions tests/ui/lint/implicit_autorefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,10 @@ unsafe fn test_string(ptr: *mut String) {
//~^ WARN implicit autoref
}

unsafe fn slice_ptr_len_because_of_msrv<T>(slice: *const [T]) {
let _ = (*slice)[..].len();
//~^ WARN implicit autoref
//~^^ WARN implicit autoref
}

fn main() {}
Loading
Loading