mirror of https://github.com/astral-sh/ruff
[red-knot] Use iterative approach to collect overloads (#17607)
## Summary This PR updates the `to_overloaded` method to use an iterative approach instead of a recursive one. Refer to https://github.com/astral-sh/ruff/pull/17585#discussion_r2056804587 for context. The main benefit here is that it avoids calling the `to_overloaded` function in a recursive manner which is a salsa query. So, this is a bit hand wavy but we should also see less memory used because the cache will only contain a single entry which should be the entire overload chain. Previously, the recursive approach would mean that each of the function involved in an overload chain would have a cache entry. This reduce in memory shouldn't be too much and I haven't looked at the actual data for it. ## Test Plan Existing test cases should pass.
This commit is contained in:
parent
8d2c79276d
commit
9937064761
|
|
@ -6308,64 +6308,54 @@ impl<'db> FunctionType<'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
function: FunctionType<'db>,
|
function: FunctionType<'db>,
|
||||||
) -> Option<OverloadedFunction<'db>> {
|
) -> Option<OverloadedFunction<'db>> {
|
||||||
// The semantic model records a use for each function on the name node. This is used here
|
let mut current = function;
|
||||||
// to get the previous function definition with the same name.
|
let mut overloads = vec![];
|
||||||
let scope = function.definition(db).scope(db);
|
|
||||||
let use_def = semantic_index(db, scope.file(db)).use_def_map(scope.file_scope_id(db));
|
loop {
|
||||||
let use_id = function
|
// The semantic model records a use for each function on the name node. This is used
|
||||||
|
// here to get the previous function definition with the same name.
|
||||||
|
let scope = current.definition(db).scope(db);
|
||||||
|
let use_def =
|
||||||
|
semantic_index(db, scope.file(db)).use_def_map(scope.file_scope_id(db));
|
||||||
|
let use_id = current
|
||||||
.body_scope(db)
|
.body_scope(db)
|
||||||
.node(db)
|
.node(db)
|
||||||
.expect_function()
|
.expect_function()
|
||||||
.name
|
.name
|
||||||
.scoped_use_id(db, scope);
|
.scoped_use_id(db, scope);
|
||||||
|
|
||||||
if let Symbol::Type(Type::FunctionLiteral(function_literal), Boundness::Bound) =
|
let Symbol::Type(Type::FunctionLiteral(previous), Boundness::Bound) =
|
||||||
symbol_from_bindings(db, use_def.bindings_at_use(use_id))
|
symbol_from_bindings(db, use_def.bindings_at_use(use_id))
|
||||||
{
|
else {
|
||||||
match function_literal.to_overloaded(db) {
|
break;
|
||||||
None => {
|
};
|
||||||
debug_assert!(
|
|
||||||
!function_literal.has_known_decorator(db, FunctionDecorators::OVERLOAD),
|
if previous.has_known_decorator(db, FunctionDecorators::OVERLOAD) {
|
||||||
"Expected `Some(OverloadedFunction)` if the previous function was an overload"
|
overloads.push(previous);
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(OverloadedFunction {
|
|
||||||
implementation: Some(_),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
// If the previous overloaded function already has an implementation, then this
|
|
||||||
// new signature completely replaces it.
|
|
||||||
}
|
|
||||||
Some(OverloadedFunction {
|
|
||||||
overloads,
|
|
||||||
implementation: None,
|
|
||||||
}) => {
|
|
||||||
return Some(
|
|
||||||
if function.has_known_decorator(db, FunctionDecorators::OVERLOAD) {
|
|
||||||
let mut overloads = overloads.clone();
|
|
||||||
overloads.push(function);
|
|
||||||
OverloadedFunction {
|
|
||||||
overloads,
|
|
||||||
implementation: None,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
OverloadedFunction {
|
break;
|
||||||
overloads: overloads.clone(),
|
|
||||||
implementation: Some(function),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if function.has_known_decorator(db, FunctionDecorators::OVERLOAD) {
|
current = previous;
|
||||||
Some(OverloadedFunction {
|
}
|
||||||
overloads: vec![function],
|
|
||||||
implementation: None,
|
// Overloads are inserted in reverse order, from bottom to top.
|
||||||
})
|
overloads.reverse();
|
||||||
} else {
|
|
||||||
|
let implementation = if function.has_known_decorator(db, FunctionDecorators::OVERLOAD) {
|
||||||
|
overloads.push(function);
|
||||||
None
|
None
|
||||||
|
} else {
|
||||||
|
Some(function)
|
||||||
|
};
|
||||||
|
|
||||||
|
if overloads.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(OverloadedFunction {
|
||||||
|
overloads,
|
||||||
|
implementation,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue