mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 04:19:13 +00:00
Add moved-out-of-ref
diagnostic
This commit is contained in:
parent
09d1265e1e
commit
b55fbd3ad7
11 changed files with 352 additions and 10 deletions
|
@ -1027,6 +1027,7 @@ fn main() {
|
|||
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
fn main() {
|
||||
match &false {
|
||||
&true => {}
|
||||
|
@ -1041,6 +1042,7 @@ fn main() {
|
|||
cov_mark::check_count!(validate_match_bailed_out, 1);
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
fn main() {
|
||||
match (&false,) {
|
||||
//^^^^^^^^^ error: missing match arm: `(&false,)` not covered
|
||||
|
|
|
@ -142,6 +142,8 @@ fn main() {
|
|||
fn missing_unsafe_diagnostic_with_static_mut() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
|
||||
struct Ty {
|
||||
a: u8,
|
||||
}
|
||||
|
@ -256,6 +258,7 @@ fn main() {
|
|||
fn add_unsafe_block_when_accessing_mutable_static() {
|
||||
check_fix(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
struct Ty {
|
||||
a: u8,
|
||||
}
|
||||
|
@ -374,6 +377,7 @@ fn main() {
|
|||
fn unsafe_expr_as_right_hand_side_of_assignment() {
|
||||
check_fix(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
static mut STATIC_MUT: u8 = 0;
|
||||
|
||||
fn main() {
|
||||
|
@ -396,6 +400,7 @@ fn main() {
|
|||
fn unsafe_expr_in_binary_plus() {
|
||||
check_fix(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
static mut STATIC_MUT: u8 = 0;
|
||||
|
||||
fn main() {
|
||||
|
|
154
crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
Normal file
154
crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use crate::{Diagnostic, DiagnosticsContext};
|
||||
use hir::HirDisplay;
|
||||
|
||||
// Diagnostic: moved-out-of-ref
|
||||
//
|
||||
// This diagnostic is triggered on moving non copy things out of references.
|
||||
pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic {
|
||||
Diagnostic::new(
|
||||
"moved-out-of-ref",
|
||||
format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)),
|
||||
ctx.sema.diagnostics_display_range(d.span.clone()).range,
|
||||
)
|
||||
.experimental() // spans are broken, and I'm not sure how precise we can detect copy types
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
// FIXME: spans are broken
|
||||
|
||||
#[test]
|
||||
fn move_by_explicit_deref() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct X;
|
||||
fn main() {
|
||||
let a = &X;
|
||||
let b = *a;
|
||||
//^ error: cannot move `X` out of reference
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_out_of_field() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
struct X;
|
||||
struct Y(X, i32);
|
||||
fn main() {
|
||||
let a = &Y(X, 5);
|
||||
let b = a.0;
|
||||
//^ error: cannot move `X` out of reference
|
||||
let y = a.1;
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_out_of_static() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
struct X;
|
||||
fn main() {
|
||||
static S: X = X;
|
||||
let s = S;
|
||||
//^ error: cannot move `X` out of reference
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_types() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: derive, copy
|
||||
|
||||
#[derive(Copy)]
|
||||
struct X<T>(T);
|
||||
struct Y;
|
||||
|
||||
fn consume<T>(_: X<T>) {
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = &X(Y);
|
||||
consume(*a);
|
||||
//^^^^^^^^^^^ error: cannot move `X<Y>` out of reference
|
||||
let a = &X(5);
|
||||
consume(*a);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_false_positive_simple() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy
|
||||
fn f(_: i32) {}
|
||||
fn main() {
|
||||
let x = &2;
|
||||
f(*x);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_false_positive_unknown_type() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: derive, copy
|
||||
fn f(x: &Unknown) -> Unknown {
|
||||
*x
|
||||
}
|
||||
|
||||
#[derive(Copy)]
|
||||
struct X<T>(T);
|
||||
|
||||
struct Y<T>(T);
|
||||
|
||||
fn g(x: &X<Unknown>) -> X<Unknown> {
|
||||
*x
|
||||
}
|
||||
|
||||
fn h(x: &Y<Unknown>) -> Y<Unknown> {
|
||||
// FIXME: we should show error for this, as `Y` is not copy
|
||||
// regardless of its generic parameter.
|
||||
*x
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_false_positive_dyn_fn() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy, fn
|
||||
fn f(x: &mut &mut dyn Fn()) {
|
||||
x();
|
||||
}
|
||||
|
||||
struct X<'a> {
|
||||
field: &'a mut dyn Fn(),
|
||||
}
|
||||
|
||||
fn f(x: &mut X<'_>) {
|
||||
(x.field)();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -340,6 +340,7 @@ fn main() {
|
|||
fn regression_14310() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
//- minicore: copy, builtin_impls
|
||||
fn clone(mut i: &!) -> ! {
|
||||
//^^^^^ 💡 weak: variable does not need to be mutable
|
||||
*i
|
||||
|
|
|
@ -38,6 +38,7 @@ mod handlers {
|
|||
pub(crate) mod missing_fields;
|
||||
pub(crate) mod missing_match_arms;
|
||||
pub(crate) mod missing_unsafe;
|
||||
pub(crate) mod moved_out_of_ref;
|
||||
pub(crate) mod mutability_errors;
|
||||
pub(crate) mod no_such_field;
|
||||
pub(crate) mod private_assoc_item;
|
||||
|
@ -283,6 +284,7 @@ pub fn diagnostics(
|
|||
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
|
||||
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
|
||||
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
|
||||
AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
|
||||
AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
|
||||
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
|
||||
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
|
||||
|
|
|
@ -121,6 +121,15 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
actual.sort_by_key(|(range, _)| range.start());
|
||||
if expected.is_empty() {
|
||||
// makes minicore smoke test debugable
|
||||
for (e, _) in &actual {
|
||||
eprintln!(
|
||||
"Code in range {e:?} = {}",
|
||||
&db.file_text(file_id)[usize::from(e.start())..usize::from(e.end())]
|
||||
)
|
||||
}
|
||||
}
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +165,11 @@ fn minicore_smoke_test() {
|
|||
|
||||
// Checks that there is no diagnostic in minicore for each flag.
|
||||
for flag in MiniCore::available_flags() {
|
||||
if flag == "clone" {
|
||||
// Clone without copy has `moved-out-of-ref`, so ignoring.
|
||||
// FIXME: Maybe we should merge copy and clone in a single flag?
|
||||
continue;
|
||||
}
|
||||
eprintln!("Checking minicore flag {flag}");
|
||||
check(MiniCore::from_flags([flag]));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue