mirror of https://github.com/astral-sh/ruff
Don't attempt to compute suggestions if the module being imported is an ancestor module or parent module of the current module
This commit is contained in:
parent
c55a3f7977
commit
79f3c8cf67
|
|
@ -47,7 +47,7 @@ use salsa;
|
||||||
use salsa::plumbing::AsId;
|
use salsa::plumbing::AsId;
|
||||||
|
|
||||||
use crate::module_name::{ModuleName, ModuleNameResolutionError};
|
use crate::module_name::{ModuleName, ModuleNameResolutionError};
|
||||||
use crate::module_resolver::resolve_module;
|
use crate::module_resolver::{file_to_module, resolve_module};
|
||||||
use crate::node_key::NodeKey;
|
use crate::node_key::NodeKey;
|
||||||
use crate::place::{
|
use crate::place::{
|
||||||
Boundness, LookupError, Place, PlaceAndQualifiers, builtins_module_scope, builtins_symbol,
|
Boundness, LookupError, Place, PlaceAndQualifiers, builtins_module_scope, builtins_symbol,
|
||||||
|
|
@ -4451,6 +4451,45 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// It's not safe to compute suggestions if the statement imports something
|
||||||
|
// from the current module, an ancestor module or a child module.
|
||||||
|
// Doing so often leads to cycles which eventually resolve, but result in
|
||||||
|
// catastrophic execution time. The reason is that
|
||||||
|
// `find_best_suggestion_for_unresolved_member()` calls `all_members()` on
|
||||||
|
// the module that this statement is importing something from,
|
||||||
|
// and if you call `all_members()` on a module-literal type it attempts to
|
||||||
|
// infer all types in the global scope of that module. If there are cyclic
|
||||||
|
// imports between modules in the same package, we'll end up calling
|
||||||
|
// `infer_import_from_definition` on the same `StmtImportFrom` node again and
|
||||||
|
// again until the cycle is resolved.
|
||||||
|
//
|
||||||
|
// If changing the logic in this closure, make sure that ty is still able to
|
||||||
|
// finish type checking the `dd-trace-py` repository in a reasonable amount of
|
||||||
|
// time. That repository has pathological cyclic imports in the
|
||||||
|
// `ddtrace/vendor/psutil` subdirectory.
|
||||||
|
let is_safe_to_compute_suggestions = || {
|
||||||
|
if !self.scope().file_scope_id(self.db()).is_global() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if import_is_self_referential {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if import_from.level != 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(current_module) = file_to_module(self.db(), self.file()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if module_name.starts_with(current_module.name()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if current_module.name().starts_with(&module_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_safe_to_compute_suggestions() {
|
||||||
if let Some(suggestion) = find_best_suggestion_for_unresolved_member(
|
if let Some(suggestion) = find_best_suggestion_for_unresolved_member(
|
||||||
self.db(),
|
self.db(),
|
||||||
module_ty,
|
module_ty,
|
||||||
|
|
@ -4460,6 +4499,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
diagnostic.set_primary_message(format_args!("Did you mean `{suggestion}`?",));
|
diagnostic.set_primary_message(format_args!("Did you mean `{suggestion}`?",));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_return_statement(&mut self, ret: &ast::StmtReturn) {
|
fn infer_return_statement(&mut self, ret: &ast::StmtReturn) {
|
||||||
if let Some(ty) = self.infer_optional_expression(ret.value.as_deref()) {
|
if let Some(ty) = self.infer_optional_expression(ret.value.as_deref()) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue