[ty] Restructure submodule query around File dependency

This makes caching of submodules independent of whether `Module`
is itself a Salsa ingredient. In fact, this makes the work done in
the prior commit superfluous. But we're possibly keeping it as an
ingredient for now since it's a bit of a tedious change and we might
need it in the near future.

Ref https://github.com/astral-sh/ruff/pull/19495#pullrequestreview-3045736715
This commit is contained in:
Andrew Gallant 2025-07-23 08:47:11 -04:00 committed by Andrew Gallant
parent 4573a0f6a0
commit cc5885e564

View file

@ -95,12 +95,43 @@ impl<'db> Module<'db> {
/// The names returned correspond to the "base" name of the module.
/// That is, `{self.name}.{basename}` should give the full module name.
pub fn all_submodules(self, db: &'db dyn Db) -> &'db [Name] {
self.all_submodules_inner(db).as_deref().unwrap_or_default()
self.all_submodules_inner(db).unwrap_or_default()
}
fn all_submodules_inner(self, db: &'db dyn Db) -> Option<&'db [Name]> {
// It would be complex and expensive to compute all submodules for
// namespace packages, since a namespace package doesn't correspond
// to a single file; it can span multiple directories across multiple
// search paths. For now, we only compute submodules for traditional
// packages that exist in a single directory on a single search path.
let Module::File(module) = self else {
return None;
};
if !matches!(module.kind(db), ModuleKind::Package) {
return None;
}
all_submodule_names_for_package(db, module.file(db)).as_deref()
}
}
impl std::fmt::Debug for Module<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
salsa::with_attached_database(|db| {
f.debug_struct("Module")
.field("name", &self.name(db))
.field("kind", &self.kind(db))
.field("file", &self.file(db))
.field("search_path", &self.search_path(db))
.field("known", &self.known(db))
.finish()
})
.unwrap_or_else(|| f.debug_tuple("Module").field(&self.as_id()).finish())
}
}
#[allow(clippy::ref_option)]
#[salsa::tracked(returns(ref))]
fn all_submodules_inner(self, db: &'db dyn Db) -> Option<Vec<Name>> {
fn all_submodule_names_for_package(db: &dyn Db, file: File) -> Option<Vec<Name>> {
fn is_submodule(
is_dir: bool,
is_file: bool,
@ -113,19 +144,7 @@ impl<'db> Module<'db> {
&& !matches!(basename, Some("__init__.py" | "__init__.pyi")))
}
// It would be complex and expensive to compute all submodules for
// namespace packages, since a namespace package doesn't correspond
// to a single file; it can span multiple directories across multiple
// search paths. For now, we only compute submodules for traditional
// packages that exist in a single directory on a single search path.
let Module::File(module) = self else {
return None;
};
if !matches!(module.kind(db), ModuleKind::Package) {
return None;
}
let path = SystemOrVendoredPathRef::try_from_file(db, module.file(db))?;
let path = SystemOrVendoredPathRef::try_from_file(db, file)?;
debug_assert!(
matches!(path.file_name(), Some("__init__.py" | "__init__.pyi")),
"expected package file `{:?}` to be `__init__.py` or `__init__.pyi`",
@ -190,22 +209,6 @@ impl<'db> Module<'db> {
.collect(),
})
}
}
impl std::fmt::Debug for Module<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
salsa::with_attached_database(|db| {
f.debug_struct("Module")
.field("name", &self.name(db))
.field("kind", &self.kind(db))
.field("file", &self.file(db))
.field("search_path", &self.search_path(db))
.field("known", &self.known(db))
.finish()
})
.unwrap_or_else(|| f.debug_tuple("Module").field(&self.as_id()).finish())
}
}
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
#[salsa::tracked(debug)]