mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +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
@ -23,7 +23,7 @@ use hir_def::{
|
||||
use hir_def::{Lookup, type_ref::TypeRefId};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::{format_to, never};
|
||||
use syntax::utils::is_raw_identifier;
|
||||
@ -107,9 +107,7 @@ impl InferenceContext<'_> {
|
||||
)
|
||||
.intern(Interner);
|
||||
self.deferred_closures.entry(closure_id).or_default();
|
||||
if let Some(c) = self.current_closure {
|
||||
self.closure_dependencies.entry(c).or_default().push(closure_id);
|
||||
}
|
||||
self.add_current_closure_dependency(closure_id);
|
||||
(Some(closure_id), closure_ty, None)
|
||||
}
|
||||
};
|
||||
@ -1748,8 +1746,42 @@ impl InferenceContext<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(deferred_closures.is_empty(), "we should have analyzed all closures");
|
||||
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.
|
||||
|
@ -1705,9 +1705,7 @@ impl InferenceContext<'_> {
|
||||
if let TyKind::Closure(c, _) =
|
||||
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
||||
{
|
||||
if let Some(par) = self.current_closure {
|
||||
self.closure_dependencies.entry(par).or_default().push(*c);
|
||||
}
|
||||
self.add_current_closure_dependency(*c);
|
||||
self.deferred_closures.entry(*c).or_default().push((
|
||||
derefed_callee.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…
x
Reference in New Issue
Block a user