mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-28 02:29:44 +00:00
Merge pull request #19579 from ChayimFriedman2/cyclic-closure
fix: Prevent panics when there is a cyclic dependency between closures
This commit is contained in:
commit
8fb2b3e4d6
3 changed files with 65 additions and 7 deletions
|
|
@ -23,7 +23,7 @@ use hir_def::{
|
||||||
use hir_def::{Lookup, type_ref::TypeRefId};
|
use hir_def::{Lookup, type_ref::TypeRefId};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use stdx::{format_to, never};
|
use stdx::{format_to, never};
|
||||||
use syntax::utils::is_raw_identifier;
|
use syntax::utils::is_raw_identifier;
|
||||||
|
|
@ -107,9 +107,7 @@ impl InferenceContext<'_> {
|
||||||
)
|
)
|
||||||
.intern(Interner);
|
.intern(Interner);
|
||||||
self.deferred_closures.entry(closure_id).or_default();
|
self.deferred_closures.entry(closure_id).or_default();
|
||||||
if let Some(c) = self.current_closure {
|
self.add_current_closure_dependency(closure_id);
|
||||||
self.closure_dependencies.entry(c).or_default().push(closure_id);
|
|
||||||
}
|
|
||||||
(Some(closure_id), closure_ty, None)
|
(Some(closure_id), closure_ty, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1748,8 +1746,42 @@ impl InferenceContext<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert!(deferred_closures.is_empty(), "we should have analyzed all closures");
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_current_closure_dependency(&mut self, dep: ClosureId) {
|
||||||
|
if let Some(c) = self.current_closure {
|
||||||
|
if !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) {
|
||||||
|
self.closure_dependencies.entry(c).or_default().push(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dep_creates_cycle(
|
||||||
|
closure_dependencies: &FxHashMap<ClosureId, Vec<ClosureId>>,
|
||||||
|
visited: &mut FxHashSet<ClosureId>,
|
||||||
|
from: ClosureId,
|
||||||
|
to: ClosureId,
|
||||||
|
) -> bool {
|
||||||
|
if !visited.insert(from) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if from == to {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(deps) = closure_dependencies.get(&to) {
|
||||||
|
for dep in deps {
|
||||||
|
if dep_creates_cycle(closure_dependencies, visited, from, *dep) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call this only when the last span in the stack isn't a split.
|
/// Call this only when the last span in the stack isn't a split.
|
||||||
|
|
|
||||||
|
|
@ -1705,9 +1705,7 @@ impl InferenceContext<'_> {
|
||||||
if let TyKind::Closure(c, _) =
|
if let TyKind::Closure(c, _) =
|
||||||
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
||||||
{
|
{
|
||||||
if let Some(par) = self.current_closure {
|
self.add_current_closure_dependency(*c);
|
||||||
self.closure_dependencies.entry(par).or_default().push(*c);
|
|
||||||
}
|
|
||||||
self.deferred_closures.entry(*c).or_default().push((
|
self.deferred_closures.entry(*c).or_default().push((
|
||||||
derefed_callee.clone(),
|
derefed_callee.clone(),
|
||||||
callee_ty.clone(),
|
callee_ty.clone(),
|
||||||
|
|
|
||||||
|
|
@ -1004,4 +1004,32 @@ fn foo() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn closure_dependency_cycle_no_panic() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let closure;
|
||||||
|
// ^^^^^^^ impl Fn()
|
||||||
|
closure = || {
|
||||||
|
closure();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let closure1;
|
||||||
|
// ^^^^^^^^ impl Fn()
|
||||||
|
let closure2;
|
||||||
|
// ^^^^^^^^ impl Fn()
|
||||||
|
closure1 = || {
|
||||||
|
closure2();
|
||||||
|
};
|
||||||
|
closure2 = || {
|
||||||
|
closure1();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue