Skip to content

Commit 84d0a71

Browse files
committed
Add moved-out-of-ref diagnostic
1 parent 9b33874 commit 84d0a71

File tree

12 files changed

+358
-15
lines changed

12 files changed

+358
-15
lines changed

crates/hir-ty/src/chalk_ext.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,15 @@ impl TyExt for Ty {
334334
};
335335
let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
336336
let env = db.trait_environment_for_body(owner);
337+
let env = env.env.add_clauses(
338+
Interner,
339+
[WhereClause::Implemented(
340+
TyBuilder::trait_ref(db, copy_trait).push(TyKind::Error.intern(Interner)).build(),
341+
)
342+
.cast(Interner)],
343+
);
337344
let goal = Canonical {
338-
value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
345+
value: InEnvironment::new(&env, trait_ref.cast(Interner)),
339346
binders: CanonicalVarKinds::empty(Interner),
340347
};
341348
db.trait_solve(crate_id, None, goal).is_some()

crates/hir-ty/src/infer/expr.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,18 @@ impl<'a> InferenceContext<'a> {
908908
match fn_x {
909909
FnTrait::FnOnce => (),
910910
FnTrait::FnMut => {
911-
if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Mut, _, _)) {
911+
if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
912+
if adjustments
913+
.last()
914+
.map(|x| matches!(x.kind, Adjust::Borrow(_)))
915+
.unwrap_or(true)
916+
{
917+
// prefer reborrow to move
918+
adjustments
919+
.push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() });
920+
adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone()))
921+
}
922+
} else {
912923
adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
913924
}
914925
}

crates/hir-ty/src/mir/borrowck.rs

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use la_arena::ArenaMap;
1010
use stdx::never;
1111
use triomphe::Arc;
1212

13-
use crate::{db::HirDatabase, ClosureId};
13+
use crate::{db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt};
1414

1515
use super::{
1616
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
@@ -24,10 +24,17 @@ pub enum MutabilityReason {
2424
Not,
2525
}
2626

27+
#[derive(Debug, Clone, PartialEq, Eq)]
28+
pub struct MovedOutOfRef {
29+
pub ty: Ty,
30+
pub span: MirSpan,
31+
}
32+
2733
#[derive(Debug, Clone, PartialEq, Eq)]
2834
pub struct BorrowckResult {
2935
pub mir_body: Arc<MirBody>,
3036
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
37+
pub moved_out_of_ref: Vec<MovedOutOfRef>,
3138
}
3239

3340
fn all_mir_bodies(
@@ -68,12 +75,107 @@ pub fn borrowck_query(
6875
let r = all_mir_bodies(db, def)
6976
.map(|body| {
7077
let body = body?;
71-
Ok(BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body })
78+
Ok(BorrowckResult {
79+
mutability_of_locals: mutability_of_locals(&body),
80+
moved_out_of_ref: moved_out_of_ref(db, &body),
81+
mir_body: body,
82+
})
7283
})
7384
.collect::<Result<Vec<_>, MirLowerError>>()?;
7485
Ok(r.into())
7586
}
7687

88+
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
89+
let mut result = vec![];
90+
let mut for_operand = |op: &Operand, span: MirSpan| match op {
91+
Operand::Copy(p) | Operand::Move(p) => {
92+
let mut ty: Ty = body.locals[p.local].ty.clone();
93+
let mut is_dereference_of_ref = false;
94+
for proj in &p.projection {
95+
if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
96+
is_dereference_of_ref = true;
97+
}
98+
ty = proj.projected_ty(ty, db, |c, subst, f| {
99+
let (def, _) = db.lookup_intern_closure(c.into());
100+
let infer = db.infer(def);
101+
let (captures, _) = infer.closure_info(&c);
102+
let parent_subst = ClosureSubst(subst).parent_subst();
103+
captures
104+
.get(f)
105+
.expect("broken closure field")
106+
.ty
107+
.clone()
108+
.substitute(Interner, parent_subst)
109+
});
110+
}
111+
if is_dereference_of_ref && !ty.clone().is_copy(db, body.owner) {
112+
result.push(MovedOutOfRef { span, ty });
113+
}
114+
}
115+
Operand::Constant(_) | Operand::Static(_) => (),
116+
};
117+
for (_, block) in body.basic_blocks.iter() {
118+
for statement in &block.statements {
119+
match &statement.kind {
120+
StatementKind::Assign(_, r) => match r {
121+
Rvalue::ShallowInitBoxWithAlloc(_) => (),
122+
Rvalue::ShallowInitBox(o, _)
123+
| Rvalue::UnaryOp(_, o)
124+
| Rvalue::Cast(_, o, _)
125+
| Rvalue::Repeat(o, _)
126+
| Rvalue::Use(o) => for_operand(o, statement.span),
127+
Rvalue::CopyForDeref(_)
128+
| Rvalue::Discriminant(_)
129+
| Rvalue::Len(_)
130+
| Rvalue::Ref(_, _) => (),
131+
Rvalue::CheckedBinaryOp(_, o1, o2) => {
132+
for_operand(o1, statement.span);
133+
for_operand(o2, statement.span);
134+
}
135+
Rvalue::Aggregate(_, ops) => {
136+
for op in ops {
137+
for_operand(op, statement.span);
138+
}
139+
}
140+
},
141+
StatementKind::Deinit(_)
142+
| StatementKind::StorageLive(_)
143+
| StatementKind::StorageDead(_)
144+
| StatementKind::Nop => (),
145+
}
146+
}
147+
match &block.terminator {
148+
Some(terminator) => match &terminator.kind {
149+
TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
150+
TerminatorKind::FalseEdge { .. }
151+
| TerminatorKind::FalseUnwind { .. }
152+
| TerminatorKind::Goto { .. }
153+
| TerminatorKind::Resume
154+
| TerminatorKind::GeneratorDrop
155+
| TerminatorKind::Abort
156+
| TerminatorKind::Return
157+
| TerminatorKind::Unreachable
158+
| TerminatorKind::Drop { .. } => (),
159+
TerminatorKind::DropAndReplace { value, .. } => {
160+
for_operand(value, terminator.span);
161+
}
162+
TerminatorKind::Call { func, args, .. } => {
163+
for_operand(func, terminator.span);
164+
args.iter().for_each(|x| for_operand(x, terminator.span));
165+
}
166+
TerminatorKind::Assert { cond, .. } => {
167+
for_operand(cond, terminator.span);
168+
}
169+
TerminatorKind::Yield { value, .. } => {
170+
for_operand(value, terminator.span);
171+
}
172+
},
173+
None => (),
174+
}
175+
}
176+
result
177+
}
178+
77179
fn is_place_direct(lvalue: &Place) -> bool {
78180
!lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
79181
}

crates/hir/src/diagnostics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ diagnostics![
4646
MissingFields,
4747
MissingMatchArms,
4848
MissingUnsafe,
49+
MovedOutOfRef,
4950
NeedMut,
5051
NoSuchField,
5152
PrivateAssocItem,
@@ -252,3 +253,9 @@ pub struct NeedMut {
252253
pub struct UnusedMut {
253254
pub local: Local,
254255
}
256+
257+
#[derive(Debug)]
258+
pub struct MovedOutOfRef {
259+
pub ty: Type,
260+
pub span: InFile<SyntaxNodePtr>,
261+
}

crates/hir/src/lib.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ pub use crate::{
9090
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
9191
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
9292
MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
93-
NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap,
94-
TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel,
95-
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
96-
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
93+
MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
94+
ReplaceFilterMapNextWithFindMap, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro,
95+
UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport,
96+
UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro,
97+
UnusedMut,
9798
},
9899
has_source::HasSource,
99100
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -1570,6 +1571,26 @@ impl DefWithBody {
15701571
if let Ok(borrowck_results) = db.borrowck(self.into()) {
15711572
for borrowck_result in borrowck_results.iter() {
15721573
let mir_body = &borrowck_result.mir_body;
1574+
for moof in &borrowck_result.moved_out_of_ref {
1575+
let span: InFile<SyntaxNodePtr> = match moof.span {
1576+
mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
1577+
Ok(s) => s.map(|x| x.into()),
1578+
Err(_) => continue,
1579+
},
1580+
mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) {
1581+
Ok(s) => s.map(|x| match x {
1582+
Either::Left(e) => e.into(),
1583+
Either::Right(e) => e.into(),
1584+
}),
1585+
Err(_) => continue,
1586+
},
1587+
mir::MirSpan::Unknown => continue,
1588+
};
1589+
acc.push(
1590+
MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span }
1591+
.into(),
1592+
)
1593+
}
15731594
let mol = &borrowck_result.mutability_of_locals;
15741595
for (binding_id, _) in hir_body.bindings.iter() {
15751596
let Some(&local) = mir_body.binding_locals.get(binding_id) else {

crates/ide-diagnostics/src/handlers/missing_match_arms.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,7 @@ fn main() {
10271027

10281028
check_diagnostics(
10291029
r#"
1030+
//- minicore: copy
10301031
fn main() {
10311032
match &false {
10321033
&true => {}
@@ -1041,6 +1042,7 @@ fn main() {
10411042
cov_mark::check_count!(validate_match_bailed_out, 1);
10421043
check_diagnostics(
10431044
r#"
1045+
//- minicore: copy
10441046
fn main() {
10451047
match (&false,) {
10461048
//^^^^^^^^^ error: missing match arm: `(&false,)` not covered

crates/ide-diagnostics/src/handlers/missing_unsafe.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ fn main() {
142142
fn missing_unsafe_diagnostic_with_static_mut() {
143143
check_diagnostics(
144144
r#"
145+
//- minicore: copy
146+
145147
struct Ty {
146148
a: u8,
147149
}
@@ -256,6 +258,7 @@ fn main() {
256258
fn add_unsafe_block_when_accessing_mutable_static() {
257259
check_fix(
258260
r#"
261+
//- minicore: copy
259262
struct Ty {
260263
a: u8,
261264
}
@@ -374,6 +377,7 @@ fn main() {
374377
fn unsafe_expr_as_right_hand_side_of_assignment() {
375378
check_fix(
376379
r#"
380+
//- minicore: copy
377381
static mut STATIC_MUT: u8 = 0;
378382
379383
fn main() {
@@ -396,6 +400,7 @@ fn main() {
396400
fn unsafe_expr_in_binary_plus() {
397401
check_fix(
398402
r#"
403+
//- minicore: copy
399404
static mut STATIC_MUT: u8 = 0;
400405
401406
fn main() {

0 commit comments

Comments
 (0)