introduce desperate module resolution

This commit is contained in:
Aria Desires 2025-12-01 19:59:08 -05:00
parent b52cc05102
commit 5883fc1554
17 changed files with 222 additions and 122 deletions

View File

@ -1,5 +1,5 @@
use ruff_db::files::FilePath;
use ty_python_semantic::{ModuleName, resolve_module, resolve_real_module};
use ty_python_semantic::{ModuleName, resolve_module_old, resolve_real_module_old};
use crate::ModuleDb;
use crate::collector::CollectedImport;
@ -70,13 +70,13 @@ impl<'a> Resolver<'a> {
/// Resolves a module name to a module.
pub(crate) fn resolve_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
let module = resolve_module(self.db, module_name)?;
let module = resolve_module_old(self.db, module_name)?;
Some(module.file(self.db)?.path(self.db))
}
/// Resolves a module name to a module (stubs not allowed).
fn resolve_real_module(&self, module_name: &ModuleName) -> Option<&'a FilePath> {
let module = resolve_real_module(self.db, module_name)?;
let module = resolve_real_module_old(self.db, module_name)?;
Some(module.file(self.db)?.path(self.db))
}
}

View File

@ -15,7 +15,7 @@ use ty_project::metadata::pyproject::{PyProject, Tool};
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
use ty_project::watch::{ChangeEvent, ProjectWatcher, directory_watcher};
use ty_project::{Db, ProjectDatabase, ProjectMetadata};
use ty_python_semantic::{Module, ModuleName, PythonPlatform, resolve_module};
use ty_python_semantic::{Module, ModuleName, PythonPlatform, resolve_module_old};
struct TestCase {
db: ProjectDatabase,
@ -232,7 +232,8 @@ impl TestCase {
}
fn module<'c>(&'c self, name: &str) -> Module<'c> {
resolve_module(self.db(), &ModuleName::new(name).unwrap()).expect("module to be present")
resolve_module_old(self.db(), &ModuleName::new(name).unwrap())
.expect("module to be present")
}
fn sorted_submodule_names(&self, parent_module_name: &str) -> Vec<String> {
@ -811,7 +812,7 @@ fn directory_moved_to_project() -> anyhow::Result<()> {
.with_context(|| "Failed to create __init__.py")?;
std::fs::write(a_original_path.as_std_path(), "").with_context(|| "Failed to create a.py")?;
let sub_a_module = resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap());
let sub_a_module = resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap());
assert_eq!(sub_a_module, None);
case.assert_indexed_project_files([bar]);
@ -832,7 +833,7 @@ fn directory_moved_to_project() -> anyhow::Result<()> {
.expect("a.py to exist");
// `import sub.a` should now resolve
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
case.assert_indexed_project_files([bar, init_file, a_file]);
@ -848,7 +849,7 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
])?;
let bar = case.system_file(case.project_path("bar.py")).unwrap();
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
let sub_path = case.project_path("sub");
let init_file = case
@ -870,7 +871,7 @@ fn directory_moved_to_trash() -> anyhow::Result<()> {
case.apply_changes(changes, None);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(!init_file.exists(case.db()));
assert!(!a_file.exists(case.db()));
@ -890,8 +891,8 @@ fn directory_renamed() -> anyhow::Result<()> {
let bar = case.system_file(case.project_path("bar.py")).unwrap();
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module(case.db(), &ModuleName::new_static("foo.baz").unwrap()).is_none());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("foo.baz").unwrap()).is_none());
let sub_path = case.project_path("sub");
let sub_init = case
@ -915,9 +916,9 @@ fn directory_renamed() -> anyhow::Result<()> {
case.apply_changes(changes, None);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
// `import foo.baz` should now resolve
assert!(resolve_module(case.db(), &ModuleName::new_static("foo.baz").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("foo.baz").unwrap()).is_some());
// The old paths are no longer tracked
assert!(!sub_init.exists(case.db()));
@ -950,7 +951,7 @@ fn directory_deleted() -> anyhow::Result<()> {
let bar = case.system_file(case.project_path("bar.py")).unwrap();
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_some());
let sub_path = case.project_path("sub");
@ -970,7 +971,7 @@ fn directory_deleted() -> anyhow::Result<()> {
case.apply_changes(changes, None);
// `import sub.a` should no longer resolve
assert!(resolve_module(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("sub.a").unwrap()).is_none());
assert!(!init_file.exists(case.db()));
assert!(!a_file.exists(case.db()));
@ -999,7 +1000,7 @@ fn search_path() -> anyhow::Result<()> {
let site_packages = case.root_path().join("site_packages");
assert_eq!(
resolve_module(case.db(), &ModuleName::new("a").unwrap()),
resolve_module_old(case.db(), &ModuleName::new("a").unwrap()),
None
);
@ -1009,7 +1010,7 @@ fn search_path() -> anyhow::Result<()> {
case.apply_changes(changes, None);
assert!(resolve_module(case.db(), &ModuleName::new_static("a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("a").unwrap()).is_some());
case.assert_indexed_project_files([case.system_file(case.project_path("bar.py")).unwrap()]);
Ok(())
@ -1022,7 +1023,7 @@ fn add_search_path() -> anyhow::Result<()> {
let site_packages = case.project_path("site_packages");
std::fs::create_dir_all(site_packages.as_std_path())?;
assert!(resolve_module(case.db(), &ModuleName::new_static("a").unwrap()).is_none());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("a").unwrap()).is_none());
// Register site-packages as a search path.
case.update_options(Options {
@ -1040,7 +1041,7 @@ fn add_search_path() -> anyhow::Result<()> {
case.apply_changes(changes, None);
assert!(resolve_module(case.db(), &ModuleName::new_static("a").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new_static("a").unwrap()).is_some());
Ok(())
}
@ -1172,7 +1173,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
// Unset the custom typeshed directory.
assert_eq!(
resolve_module(case.db(), &ModuleName::new("os").unwrap()),
resolve_module_old(case.db(), &ModuleName::new("os").unwrap()),
None
);
@ -1187,7 +1188,7 @@ fn changed_versions_file() -> anyhow::Result<()> {
case.apply_changes(changes, None);
assert!(resolve_module(case.db(), &ModuleName::new("os").unwrap()).is_some());
assert!(resolve_module_old(case.db(), &ModuleName::new("os").unwrap()).is_some());
Ok(())
}
@ -1410,7 +1411,7 @@ mod unix {
Ok(())
})?;
let baz = resolve_module(case.db(), &ModuleName::new_static("bar.baz").unwrap())
let baz = resolve_module_old(case.db(), &ModuleName::new_static("bar.baz").unwrap())
.expect("Expected bar.baz to exist in site-packages.");
let baz_project = case.project_path("bar/baz.py");
let baz_file = baz.file(case.db()).unwrap();
@ -1486,7 +1487,7 @@ mod unix {
Ok(())
})?;
let baz = resolve_module(case.db(), &ModuleName::new_static("bar.baz").unwrap())
let baz = resolve_module_old(case.db(), &ModuleName::new_static("bar.baz").unwrap())
.expect("Expected bar.baz to exist in site-packages.");
let baz_file = baz.file(case.db()).unwrap();
let bar_baz = case.project_path("bar/baz.py");
@ -1591,7 +1592,7 @@ mod unix {
Ok(())
})?;
let baz = resolve_module(case.db(), &ModuleName::new_static("bar.baz").unwrap())
let baz = resolve_module_old(case.db(), &ModuleName::new_static("bar.baz").unwrap())
.expect("Expected bar.baz to exist in site-packages.");
let baz_site_packages_path =
case.project_path(".venv/lib/python3.12/site-packages/bar/baz.py");
@ -1854,11 +1855,11 @@ fn rename_files_casing_only() -> anyhow::Result<()> {
let mut case = setup([("lib.py", "class Foo: ...")])?;
assert!(
resolve_module(case.db(), &ModuleName::new("lib").unwrap()).is_some(),
resolve_module_old(case.db(), &ModuleName::new("lib").unwrap()).is_some(),
"Expected `lib` module to exist."
);
assert_eq!(
resolve_module(case.db(), &ModuleName::new("Lib").unwrap()),
resolve_module_old(case.db(), &ModuleName::new("Lib").unwrap()),
None,
"Expected `Lib` module not to exist"
);
@ -1891,13 +1892,13 @@ fn rename_files_casing_only() -> anyhow::Result<()> {
// Resolving `lib` should now fail but `Lib` should now succeed
assert_eq!(
resolve_module(case.db(), &ModuleName::new("lib").unwrap()),
resolve_module_old(case.db(), &ModuleName::new("lib").unwrap()),
None,
"Expected `lib` module to no longer exist."
);
assert!(
resolve_module(case.db(), &ModuleName::new("Lib").unwrap()).is_some(),
resolve_module_old(case.db(), &ModuleName::new("Lib").unwrap()).is_some(),
"Expected `Lib` module to exist"
);

View File

@ -166,7 +166,7 @@ impl<'db> DunderAllNamesCollector<'db> {
) -> Option<&'db FxHashSet<Name>> {
let module_name =
ModuleName::from_import_statement(self.db, self.file, import_from).ok()?;
let module = resolve_module(self.db, &module_name)?;
let module = resolve_module(self.db, self.file, &module_name)?;
dunder_all_names(self.db, module.file(self.db)?)
}

View File

@ -13,7 +13,8 @@ pub use diagnostic::add_inferred_python_version_hint_to_diagnostic;
pub use module_name::{ModuleName, ModuleNameResolutionError};
pub use module_resolver::{
KnownModule, Module, SearchPath, SearchPathValidationError, SearchPaths, all_modules,
list_modules, resolve_module, resolve_real_module, system_module_search_paths,
list_modules, resolve_module, resolve_module_old, resolve_real_module, resolve_real_module_old,
system_module_search_paths,
};
pub use program::{
Program, ProgramSettings, PythonVersionFileSource, PythonVersionSource,

View File

@ -6,7 +6,9 @@ pub use module::Module;
pub use path::{SearchPath, SearchPathValidationError};
pub use resolver::SearchPaths;
pub(crate) use resolver::file_to_module;
pub use resolver::{resolve_module, resolve_real_module};
pub use resolver::{
resolve_module, resolve_module_old, resolve_real_module, resolve_real_module_old,
};
use ruff_db::system::SystemPath;
use crate::Db;

View File

@ -33,14 +33,40 @@ use super::module::{Module, ModuleKind};
use super::path::{ModulePath, SearchPath, SearchPathValidationError, SystemOrVendoredPathRef};
/// Resolves a module name to a module.
pub fn resolve_module<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Option<Module<'db>> {
pub fn resolve_module<'db>(
db: &'db dyn Db,
importing_file: File,
module_name: &ModuleName,
) -> Option<Module<'db>> {
let interned_name = ModuleNameIngredient::new(db, module_name, ModuleResolveMode::StubsAllowed);
resolve_module_query(db, interned_name)
.or_else(|| desperately_resolve_module(db, importing_file, interned_name))
}
pub fn resolve_module_old<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Option<Module<'db>> {
let interned_name = ModuleNameIngredient::new(db, module_name, ModuleResolveMode::StubsAllowed);
resolve_module_query(db, interned_name)
}
/// Resolves a module name to a module (stubs not allowed).
pub fn resolve_real_module<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Option<Module<'db>> {
pub fn resolve_real_module<'db>(
db: &'db dyn Db,
importing_file: File,
module_name: &ModuleName,
) -> Option<Module<'db>> {
let interned_name =
ModuleNameIngredient::new(db, module_name, ModuleResolveMode::StubsNotAllowed);
resolve_module_query(db, interned_name)
.or_else(|| desperately_resolve_module(db, importing_file, interned_name))
}
pub fn resolve_real_module_old<'db>(
db: &'db dyn Db,
module_name: &ModuleName,
) -> Option<Module<'db>> {
let interned_name =
ModuleNameIngredient::new(db, module_name, ModuleResolveMode::StubsNotAllowed);
@ -116,6 +142,43 @@ fn resolve_module_query<'db>(
Some(module)
}
fn desperately_resolve_module<'db>(
db: &'db dyn Db,
importing_file: File,
module_name: ModuleNameIngredient<'db>,
) -> Option<Module<'db>> {
let name = module_name.name(db);
let mode = module_name.mode(db);
let _span = tracing::trace_span!("desperately_resolve_module", %name).entered();
let Some(resolved) = desperately_resolve_name(db, importing_file, name, mode) else {
tracing::debug!("Module `{name}` not found while looking in parent dirs");
return None;
};
let module = match resolved {
ResolvedName::FileModule(module) => {
tracing::trace!(
"Resolved module `{name}` to `{path}`",
path = module.file.path(db)
);
Module::file_module(
db,
name.clone(),
module.kind,
module.search_path,
module.file,
)
}
ResolvedName::NamespacePackage => {
tracing::trace!("Module `{name}` is a namespace package");
Module::namespace_package(db, name.clone())
}
};
Some(module)
}
/// Resolves the module for the given path.
///
/// Returns `None` if the path is not a module locatable via any of the known search paths.
@ -154,7 +217,7 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module<'_>> {
// If it doesn't, then that means that multiple modules have the same name in different
// root paths, but that the module corresponding to `path` is in a lower priority search path,
// in which case we ignore it.
let module = resolve_module(db, &module_name)?;
let module = resolve_module(db, file, &module_name)?;
let module_file = module.file(db)?;
if file.path(db) == module_file.path(db) {
@ -162,10 +225,10 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module<'_>> {
} else if file.source_type(db) == PySourceType::Python
&& module_file.source_type(db) == PySourceType::Stub
{
// If a .py and .pyi are both defined, the .pyi will be the one returned by `resolve_module().file`,
// If a .py and .pyi are both defined, the .pyi will be the one returned by `resolve_module_old().file`,
// which would make us erroneously believe the `.py` is *not* also this module (breaking things
// like relative imports). So here we try `resolve_real_module().file` to cover both cases.
let module = resolve_real_module(db, &module_name)?;
let module = resolve_real_module(db, file, &module_name)?;
let module_file = module.file(db)?;
if file.path(db) == module_file.path(db) {
return Some(module);
@ -808,6 +871,35 @@ fn resolve_name(db: &dyn Db, name: &ModuleName, mode: ModuleResolveMode) -> Opti
None
}
fn desperately_resolve_name(
db: &dyn Db,
importing_file: File,
name: &ModuleName,
mode: ModuleResolveMode,
) -> Option<ResolvedName> {
let program = Program::get(db);
let python_version = program.python_version(db);
let resolver_state = ResolverContext::new(db, python_version, mode);
let name = RelaxedModuleName::new(name);
let system = db.system();
let importing_path = importing_file.path(db).as_system_path()?;
for dir in importing_path.ancestors() {
let pyproject = dir.join("pyproject.toml");
if system.path_exists(&pyproject) {
let search_path = SearchPath::extra(system, dir.to_owned()).ok()?;
return match resolve_name_in_search_path(&resolver_state, &name, &search_path) {
Ok((_, _, ResolvedName::FileModule(module))) => {
Some(ResolvedName::FileModule(module))
}
Ok((_, _, ResolvedName::NamespacePackage)) => Some(ResolvedName::NamespacePackage),
Err(_) => None,
};
}
}
None
}
#[derive(Debug)]
enum ResolvedName {
/// A module that resolves to a file.
@ -1354,11 +1446,11 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
Some(&foo_module),
resolve_module(&db, &foo_module_name).as_ref()
resolve_module_old(&db, &foo_module_name).as_ref()
);
assert_eq!("foo", foo_module.name(&db));
@ -1380,11 +1472,11 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
Some(&foo_module),
resolve_module(&db, &foo_module_name).as_ref()
resolve_module_old(&db, &foo_module_name).as_ref()
);
assert_eq!("foo", foo_module.name(&db));
@ -1412,11 +1504,11 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
Some(&foo_module),
resolve_module(&db, &foo_module_name).as_ref()
resolve_module_old(&db, &foo_module_name).as_ref()
);
assert_eq!("foo", foo_module.name(&db));
@ -1439,7 +1531,7 @@ mod tests {
.build();
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
let builtins = resolve_module_old(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(
builtins.file(&db).unwrap().path(&db),
@ -1463,7 +1555,7 @@ mod tests {
.build();
let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
let builtins = resolve_module_old(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(
builtins.file(&db).unwrap().path(&db),
@ -1484,11 +1576,11 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, &functools_module_name).as_ref()
resolve_module_old(&db, &functools_module_name).as_ref()
);
assert_eq!(&stdlib, functools_module.search_path(&db).unwrap());
@ -1541,7 +1633,7 @@ mod tests {
let existing_modules = create_module_names(&["asyncio", "functools", "xml.etree"]);
for module_name in existing_modules {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
let resolved_module = resolve_module_old(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path(&db).unwrap();
@ -1594,7 +1686,7 @@ mod tests {
for module_name in nonexisting_modules {
assert!(
resolve_module(&db, &module_name).is_none(),
resolve_module_old(&db, &module_name).is_none(),
"Unexpectedly resolved a module for {module_name}"
);
}
@ -1637,7 +1729,7 @@ mod tests {
]);
for module_name in existing_modules {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
let resolved_module = resolve_module_old(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib")
});
let search_path = resolved_module.search_path(&db).unwrap();
@ -1673,7 +1765,7 @@ mod tests {
let nonexisting_modules = create_module_names(&["importlib", "xml", "xml.etree"]);
for module_name in nonexisting_modules {
assert!(
resolve_module(&db, &module_name).is_none(),
resolve_module_old(&db, &module_name).is_none(),
"Unexpectedly resolved a module for {module_name}"
);
}
@ -1695,11 +1787,11 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(
Some(&functools_module),
resolve_module(&db, &functools_module_name).as_ref()
resolve_module_old(&db, &functools_module_name).as_ref()
);
assert_eq!(&src, functools_module.search_path(&db).unwrap());
assert_eq!(ModuleKind::Module, functools_module.kind(&db));
@ -1722,7 +1814,7 @@ mod tests {
.build();
let pydoc_data_topics_name = ModuleName::new_static("pydoc_data.topics").unwrap();
let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap();
let pydoc_data_topics = resolve_module_old(&db, &pydoc_data_topics_name).unwrap();
assert_eq!("pydoc_data.topics", pydoc_data_topics.name(&db));
assert_eq!(pydoc_data_topics.search_path(&db).unwrap(), &stdlib);
@ -1739,7 +1831,7 @@ mod tests {
.build();
let foo_path = src.join("foo/__init__.py");
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!("foo", foo_module.name(&db));
assert_eq!(&src, foo_module.search_path(&db).unwrap());
@ -1766,7 +1858,7 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&src, foo_module.search_path(&db).unwrap());
@ -1789,8 +1881,9 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_real = resolve_real_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo = resolve_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_real =
resolve_real_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_stub = src.join("foo.pyi");
assert_eq!(&src, foo.search_path(&db).unwrap());
@ -1815,7 +1908,7 @@ mod tests {
let TestCase { db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let baz_module =
resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
resolve_module_old(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
let baz_path = src.join("foo/bar/baz.py");
assert_eq!(&src, baz_module.search_path(&db).unwrap());
@ -1839,7 +1932,7 @@ mod tests {
.with_site_packages_files(&[("foo.py", "")])
.build();
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_module = resolve_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_src_path = src.join("foo.py");
assert_eq!(&src, foo_module.search_path(&db).unwrap());
@ -1910,8 +2003,8 @@ mod tests {
},
);
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let bar_module = resolve_module(&db, &ModuleName::new_static("bar").unwrap()).unwrap();
let foo_module = resolve_module_old(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let bar_module = resolve_module_old(&db, &ModuleName::new_static("bar").unwrap()).unwrap();
assert_ne!(foo_module, bar_module);
@ -1946,7 +2039,7 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
let foo_pieces = (
foo_module.name(&db).clone(),
foo_module.file(&db),
@ -1967,7 +2060,7 @@ mod tests {
// Re-query the foo module. The foo module should still be cached
// because `bar.py` isn't relevant for resolving `foo`.
let foo_module2 = resolve_module(&db, &foo_module_name);
let foo_module2 = resolve_module_old(&db, &foo_module_name);
let foo_pieces2 = foo_module2.map(|foo_module2| {
(
foo_module2.name(&db).clone(),
@ -1994,14 +2087,14 @@ mod tests {
let foo_path = src.join("foo.py");
let foo_module_name = ModuleName::new_static("foo").unwrap();
assert_eq!(resolve_module(&db, &foo_module_name), None);
assert_eq!(resolve_module_old(&db, &foo_module_name), None);
// Now write the foo file
db.write_file(&foo_path, "x = 1")?;
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
let foo_module = resolve_module_old(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(foo_file, foo_module.file(&db).unwrap());
Ok(())
@ -2015,7 +2108,7 @@ mod tests {
let TestCase { mut db, src, .. } = TestCaseBuilder::new().with_src_files(SRC).build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist");
let foo_module = resolve_module_old(&db, &foo_module_name).expect("foo module to exist");
let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&foo_init_path, foo_module.file(&db).unwrap().path(&db));
@ -2027,7 +2120,7 @@ mod tests {
File::sync_path(&mut db, &foo_init_path);
File::sync_path(&mut db, foo_init_path.parent().unwrap());
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
let foo_module = resolve_module_old(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(&src.join("foo.py"), foo_module.file(&db).unwrap().path(&db));
Ok(())
@ -2053,7 +2146,7 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap();
let stdlib_functools_path = stdlib.join("functools.pyi");
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(&db).unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file(&db).unwrap()),
@ -2066,7 +2159,7 @@ mod tests {
let site_packages_functools_path = site_packages.join("functools.py");
db.write_file(&site_packages_functools_path, "f: int")
.unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
let functools_file = functools_module.file(&db).unwrap();
let functools_search_path = functools_module.search_path(&db).unwrap().clone();
let events = db.take_salsa_events();
@ -2101,7 +2194,7 @@ mod tests {
.build();
let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(&db).unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file(&db).unwrap()),
@ -2112,7 +2205,7 @@ mod tests {
// since first-party files take higher priority in module resolution:
let src_functools_path = src.join("functools.py");
db.write_file(&src_functools_path, "FOO: int").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(&db).unwrap(), &src);
assert_eq!(
Ok(functools_module.file(&db).unwrap()),
@ -2143,7 +2236,7 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap();
let src_functools_path = src.join("functools.py");
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(&db).unwrap(), &src);
assert_eq!(
Ok(functools_module.file(&db).unwrap()),
@ -2156,7 +2249,7 @@ mod tests {
.remove_file(&src_functools_path)
.unwrap();
File::sync_path(&mut db, &src_functools_path);
let functools_module = resolve_module(&db, &functools_module_name).unwrap();
let functools_module = resolve_module_old(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(&db).unwrap(), &stdlib);
assert_eq!(
Ok(functools_module.file(&db).unwrap()),
@ -2178,8 +2271,8 @@ mod tests {
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_bar_module_name = ModuleName::new_static("foo.bar").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
let foo_bar_module = resolve_module_old(&db, &foo_bar_module_name).unwrap();
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
@ -2207,11 +2300,11 @@ mod tests {
// Lines with leading whitespace in `.pth` files do not parse:
let foo_module_name = ModuleName::new_static("foo").unwrap();
assert_eq!(resolve_module(&db, &foo_module_name), None);
assert_eq!(resolve_module_old(&db, &foo_module_name), None);
// Lines with trailing whitespace in `.pth` files do:
let bar_module_name = ModuleName::new_static("bar").unwrap();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
let bar_module = resolve_module_old(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file(&db).unwrap().path(&db),
&FilePath::system("/y/src/bar.py")
@ -2230,7 +2323,7 @@ mod tests {
.build();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
@ -2278,10 +2371,10 @@ not_a_directory
let b_module_name = ModuleName::new_static("b").unwrap();
let spam_module_name = ModuleName::new_static("spam").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
let b_module = resolve_module(&db, &b_module_name).unwrap();
let spam_module = resolve_module(&db, &spam_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
let a_module = resolve_module_old(&db, &a_module_name).unwrap();
let b_module = resolve_module_old(&db, &b_module_name).unwrap();
let spam_module = resolve_module_old(&db, &spam_module_name).unwrap();
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
@ -2315,14 +2408,14 @@ not_a_directory
let foo_module_name = ModuleName::new_static("foo").unwrap();
let bar_module_name = ModuleName::new_static("bar").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
&FilePath::system("/x/src/foo.py")
);
db.clear_salsa_events();
let bar_module = resolve_module(&db, &bar_module_name).unwrap();
let bar_module = resolve_module_old(&db, &bar_module_name).unwrap();
assert_eq!(
bar_module.file(&db).unwrap().path(&db),
&FilePath::system("/y/src/bar.py")
@ -2352,7 +2445,7 @@ not_a_directory
db.write_files(x_directory).unwrap();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
&FilePath::system("/x/src/foo.py")
@ -2364,7 +2457,7 @@ not_a_directory
File::sync_path(&mut db, &site_packages.join("_foo.pth"));
assert_eq!(resolve_module(&db, &foo_module_name), None);
assert_eq!(resolve_module_old(&db, &foo_module_name), None);
}
#[test]
@ -2379,7 +2472,7 @@ not_a_directory
db.write_files(x_directory).unwrap();
let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let foo_module = resolve_module_old(&db, &foo_module_name).unwrap();
let src_path = SystemPathBuf::from("/x/src");
assert_eq!(
foo_module.file(&db).unwrap().path(&db),
@ -2392,7 +2485,7 @@ not_a_directory
db.memory_file_system().remove_directory(&src_path).unwrap();
File::sync_path(&mut db, &src_path.join("foo.py"));
File::sync_path(&mut db, &src_path);
assert_eq!(resolve_module(&db, &foo_module_name), None);
assert_eq!(resolve_module_old(&db, &foo_module_name), None);
}
#[test]
@ -2452,7 +2545,7 @@ not_a_directory
// The editable installs discovered from the `.pth` file in the first `site-packages` directory
// take precedence over the second `site-packages` directory...
let a_module_name = ModuleName::new_static("a").unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap();
let a_module = resolve_module_old(&db, &a_module_name).unwrap();
assert_eq!(
a_module.file(&db).unwrap().path(&db),
&editable_install_location
@ -2466,7 +2559,7 @@ not_a_directory
// ...But now that the `.pth` file in the first `site-packages` directory has been deleted,
// the editable install no longer exists, so the module now resolves to the file in the
// second `site-packages` directory
let a_module = resolve_module(&db, &a_module_name).unwrap();
let a_module = resolve_module_old(&db, &a_module_name).unwrap();
assert_eq!(
a_module.file(&db).unwrap().path(&db),
&system_site_packages_location
@ -2524,12 +2617,12 @@ not_a_directory
// Now try to resolve the module `A` (note the capital `A` instead of `a`).
let a_module_name = ModuleName::new_static("A").unwrap();
assert_eq!(resolve_module(&db, &a_module_name), None);
assert_eq!(resolve_module_old(&db, &a_module_name), None);
// Now lookup the same module using the lowercase `a` and it should
// resolve to the file in the system site-packages
let a_module_name = ModuleName::new_static("a").unwrap();
let a_module = resolve_module(&db, &a_module_name).expect("a.py to resolve");
let a_module = resolve_module_old(&db, &a_module_name).expect("a.py to resolve");
assert!(
a_module
.file(&db)

View File

@ -1,7 +1,7 @@
use ruff_db::files::File;
use crate::dunder_all::dunder_all_names;
use crate::module_resolver::{KnownModule, file_to_module};
use crate::module_resolver::{KnownModule, file_to_module, resolve_module_old};
use crate::semantic_index::definition::{Definition, DefinitionState};
use crate::semantic_index::place::{PlaceExprRef, ScopedPlaceId};
use crate::semantic_index::scope::ScopeId;
@ -14,7 +14,7 @@ use crate::types::{
Truthiness, Type, TypeAndQualifiers, TypeQualifiers, UnionBuilder, UnionType, binding_type,
declaration_type, todo_type,
};
use crate::{Db, FxOrderSet, Program, resolve_module};
use crate::{Db, FxOrderSet, Program};
pub(crate) use implicit_globals::{
module_type_implicit_global_declaration, module_type_implicit_global_symbol,
@ -379,7 +379,7 @@ pub(crate) fn imported_symbol<'db>(
/// and should not be used when a symbol is being explicitly imported from the `builtins` module
/// (e.g. `from builtins import int`).
pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> PlaceAndQualifiers<'db> {
resolve_module(db, &KnownModule::Builtins.name())
resolve_module_old(db, &KnownModule::Builtins.name())
.and_then(|module| {
let file = module.file(db)?;
Some(
@ -409,7 +409,7 @@ pub(crate) fn known_module_symbol<'db>(
known_module: KnownModule,
symbol: &str,
) -> PlaceAndQualifiers<'db> {
resolve_module(db, &known_module.name())
resolve_module_old(db, &known_module.name())
.and_then(|module| {
let file = module.file(db)?;
Some(imported_symbol(db, file, symbol, None))
@ -448,7 +448,7 @@ pub(crate) fn builtins_module_scope(db: &dyn Db) -> Option<ScopeId<'_>> {
///
/// Can return `None` if a custom typeshed is used that is missing the core module in question.
fn core_module_scope(db: &dyn Db, core_module: KnownModule) -> Option<ScopeId<'_>> {
let module = resolve_module(db, &core_module.name())?;
let module = resolve_module_old(db, &core_module.name())?;
Some(global_scope(db, module.file(db)?))
}

View File

@ -1580,7 +1580,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
continue;
};
let Some(module) = resolve_module(self.db, &module_name) else {
let Some(module) = resolve_module(self.db, self.file, &module_name) else {
continue;
};

View File

@ -250,7 +250,9 @@ impl<'db> Visitor<'db> for ExportFinder<'db> {
for export in
ModuleName::from_import_statement(self.db, self.file, node)
.ok()
.and_then(|module_name| resolve_module(self.db, &module_name))
.and_then(|module_name| {
resolve_module(self.db, self.file, &module_name)
})
.iter()
.flat_map(|module| {
module

View File

@ -100,7 +100,7 @@ impl<'db> SemanticModel<'db> {
pub fn resolve_module(&self, module: Option<&str>, level: u32) -> Option<Module<'db>> {
let module_name =
ModuleName::from_identifier_parts(self.db, self.file, module, level).ok()?;
resolve_module(self.db, &module_name)
resolve_module(self.db, self.file, &module_name)
}
/// Returns completions for symbols available in a `import <CURSOR>` context.
@ -140,7 +140,7 @@ impl<'db> SemanticModel<'db> {
&self,
module_name: &ModuleName,
) -> Vec<Completion<'db>> {
let Some(module) = resolve_module(self.db, module_name) else {
let Some(module) = resolve_module(self.db, self.file, module_name) else {
tracing::debug!("Could not resolve module from `{module_name:?}`");
return vec![];
};
@ -150,7 +150,7 @@ impl<'db> SemanticModel<'db> {
/// Returns completions for symbols available in the given module as if
/// it were imported by this model's `File`.
fn module_completions(&self, module_name: &ModuleName) -> Vec<Completion<'db>> {
let Some(module) = resolve_module(self.db, module_name) else {
let Some(module) = resolve_module(self.db, self.file, module_name) else {
tracing::debug!("Could not resolve module from `{module_name:?}`");
return vec![];
};

View File

@ -12618,7 +12618,7 @@ impl<'db> ModuleLiteralType<'db> {
let relative_submodule_name = ModuleName::new(name)?;
let mut absolute_submodule_name = self.module(db).name(db).clone();
absolute_submodule_name.extend(&relative_submodule_name);
let submodule = resolve_module(db, &absolute_submodule_name)?;
let submodule = resolve_module(db, importing_file, &absolute_submodule_name)?;
Some(Type::module_literal(db, importing_file, submodule))
}

View File

@ -5809,7 +5809,7 @@ impl SlotsKind {
mod tests {
use super::*;
use crate::db::tests::setup_db;
use crate::module_resolver::resolve_module;
use crate::module_resolver::resolve_module_old;
use crate::{PythonVersionSource, PythonVersionWithSource};
use salsa::Setter;
use strum::IntoEnumIterator;
@ -5825,7 +5825,8 @@ mod tests {
});
for class in KnownClass::iter() {
let class_name = class.name(&db);
let class_module = resolve_module(&db, &class.canonical_module(&db).name()).unwrap();
let class_module =
resolve_module_old(&db, &class.canonical_module(&db).name()).unwrap();
assert_eq!(
KnownClass::try_from_file_and_name(

View File

@ -1887,7 +1887,7 @@ impl KnownFunction {
let Some(module_name) = ModuleName::new(module_name) else {
return;
};
let Some(module) = resolve_module(db, &module_name) else {
let Some(module) = resolve_module(db, file, &module_name) else {
return;
};

View File

@ -1342,7 +1342,7 @@ mod resolve_definition {
use crate::semantic_index::definition::{Definition, DefinitionKind, module_docstring};
use crate::semantic_index::scope::{NodeWithScopeKind, ScopeId};
use crate::semantic_index::{global_scope, place_table, semantic_index, use_def_map};
use crate::{Db, ModuleName, resolve_module, resolve_real_module};
use crate::{Db, ModuleName, resolve_module, resolve_real_module_old};
/// Represents the result of resolving an import to either a specific definition or
/// a specific range within a file.
@ -1440,7 +1440,7 @@ mod resolve_definition {
};
// Resolve the module to its file
let Some(resolved_module) = resolve_module(db, &module_name) else {
let Some(resolved_module) = resolve_module(db, file, &module_name) else {
return Vec::new(); // Module not found, return empty list
};
@ -1527,7 +1527,7 @@ mod resolve_definition {
else {
return Vec::new();
};
let Some(resolved_module) = resolve_module(db, &module_name) else {
let Some(resolved_module) = resolve_module(db, file, &module_name) else {
return Vec::new();
};
resolved_module.file(db)
@ -1636,7 +1636,7 @@ mod resolve_definition {
// It's definitely a stub, so now rerun module resolution but with stubs disabled.
let stub_module = file_to_module(db, stub_file_for_module_lookup)?;
trace!("Found stub module: {}", stub_module.name(db));
let real_module = resolve_real_module(db, stub_module.name(db))?;
let real_module = resolve_real_module_old(db, stub_module.name(db))?;
trace!("Found real module: {}", real_module.name(db));
let real_file = real_module.file(db)?;
trace!("Found real file: {}", real_file.path(db));

View File

@ -22,7 +22,8 @@ use super::{
use crate::diagnostic::format_enumeration;
use crate::module_name::{ModuleName, ModuleNameResolutionError};
use crate::module_resolver::{
KnownModule, ModuleResolveMode, file_to_module, resolve_module, search_paths,
KnownModule, ModuleResolveMode, file_to_module, resolve_module, resolve_module_old,
search_paths,
};
use crate::node_key::NodeKey;
use crate::place::{
@ -5922,7 +5923,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
) else {
return false;
};
resolve_module(self.db(), &module_name).is_some()
resolve_module(self.db(), self.file(), &module_name).is_some()
}) {
diagnostic
.help("The module can be resolved if the number of leading dots is reduced");
@ -6159,7 +6160,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
};
if resolve_module(self.db(), &module_name).is_none() {
if resolve_module(self.db(), self.file(), &module_name).is_none() {
self.report_unresolved_import(import_from.into(), module_ref.range(), *level, module);
}
}
@ -6177,7 +6178,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
return;
};
let Some(module) = resolve_module(self.db(), &module_name) else {
let Some(module) = resolve_module(self.db(), self.file(), &module_name) else {
self.add_unknown_declaration_with_binding(alias.into(), definition);
return;
};
@ -6362,7 +6363,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
return;
};
let Some(module) = resolve_module(self.db(), &thispackage_name) else {
let Some(module) = resolve_module(self.db(), self.file(), &thispackage_name) else {
self.add_binding(import_from.into(), definition, |_, _| Type::unknown());
return;
};
@ -6593,7 +6594,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
fn module_type_from_name(&self, module_name: &ModuleName) -> Option<Type<'db>> {
resolve_module(self.db(), module_name)
resolve_module(self.db(), self.file(), module_name)
.map(|module| Type::module_literal(self.db(), self.file(), module))
}
@ -9172,7 +9173,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
{
let mut maybe_submodule_name = module_name.clone();
maybe_submodule_name.extend(&relative_submodule);
if resolve_module(db, &maybe_submodule_name).is_some() {
if resolve_module_old(db, &maybe_submodule_name).is_some() {
if let Some(builder) = self
.context
.report_lint(&POSSIBLY_MISSING_ATTRIBUTE, attribute)

View File

@ -3,8 +3,7 @@
use super::{ClassType, Type, class::KnownClass};
use crate::db::Db;
use crate::module_resolver::{KnownModule, file_to_module};
use crate::resolve_module;
use crate::module_resolver::{KnownModule, file_to_module, resolve_module_old};
use crate::semantic_index::place::ScopedPlaceId;
use crate::semantic_index::{FileScopeId, place_table, use_def_map};
use crate::types::TypeDefinition;
@ -544,7 +543,7 @@ impl SpecialFormType {
self.definition_modules()
.iter()
.find_map(|module| {
let file = resolve_module(db, &module.name())?.file(db)?;
let file = resolve_module_old(db, &module.name())?.file(db)?;
let scope = FileScopeId::global().to_scope_id(db, file);
let symbol_id = place_table(db, scope).symbol_id(self.name())?;

View File

@ -21,7 +21,7 @@ use ty_python_semantic::types::{UNDEFINED_REVEAL, check_types};
use ty_python_semantic::{
Module, Program, ProgramSettings, PythonEnvironment, PythonPlatform, PythonVersionSource,
PythonVersionWithSource, SearchPath, SearchPathSettings, SysPrefixPathOrigin, list_modules,
resolve_module,
resolve_module_old,
};
mod assertion;
@ -566,7 +566,7 @@ struct ModuleInconsistency<'db> {
fn run_module_resolution_consistency_test(db: &db::Db) -> Result<(), Vec<ModuleInconsistency<'_>>> {
let mut errs = vec![];
for from_list in list_modules(db) {
errs.push(match resolve_module(db, from_list.name(db)) {
errs.push(match resolve_module_old(db, from_list.name(db)) {
None => ModuleInconsistency {
db,
from_list,