mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-24 17:16:53 +00:00
[ty] Make Module a Salsa ingredient
We want to write queries that depend on `Module` for caching. While it seems it can be done without making `Module` an ingredient, it seems it is best practice to do so. [best practice to do so]: https://github.com/astral-sh/ruff/pull/19408#discussion_r2215867301
This commit is contained in:
parent
905b9d7f51
commit
4573a0f6a0
24 changed files with 258 additions and 253 deletions
|
|
@ -1,10 +1,11 @@
|
|||
use std::fmt::Formatter;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruff_db::files::File;
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_stdlib::identifiers::is_identifier;
|
||||
use salsa::Database;
|
||||
use salsa::plumbing::AsId;
|
||||
|
||||
use super::path::SearchPath;
|
||||
use crate::Db;
|
||||
|
|
@ -12,14 +13,19 @@ use crate::module_name::ModuleName;
|
|||
use crate::module_resolver::path::SystemOrVendoredPathRef;
|
||||
|
||||
/// Representation of a Python module.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
pub struct Module {
|
||||
inner: Arc<ModuleInner>,
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq, salsa::Supertype, salsa::Update)]
|
||||
pub enum Module<'db> {
|
||||
File(FileModule<'db>),
|
||||
Namespace(NamespacePackage<'db>),
|
||||
}
|
||||
|
||||
// The Salsa heap is tracked separately.
|
||||
impl get_size2::GetSize for Module<'_> {}
|
||||
|
||||
#[salsa::tracked]
|
||||
impl Module {
|
||||
impl<'db> Module<'db> {
|
||||
pub(crate) fn file_module(
|
||||
db: &'db dyn Db,
|
||||
name: ModuleName,
|
||||
kind: ModuleKind,
|
||||
search_path: SearchPath,
|
||||
|
|
@ -27,67 +33,57 @@ impl Module {
|
|||
) -> Self {
|
||||
let known = KnownModule::try_from_search_path_and_name(&search_path, &name);
|
||||
|
||||
Self {
|
||||
inner: Arc::new(ModuleInner::FileModule {
|
||||
name,
|
||||
kind,
|
||||
search_path,
|
||||
file,
|
||||
known,
|
||||
}),
|
||||
}
|
||||
Self::File(FileModule::new(db, name, kind, search_path, file, known))
|
||||
}
|
||||
|
||||
pub(crate) fn namespace_package(name: ModuleName) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(ModuleInner::NamespacePackage { name }),
|
||||
}
|
||||
pub(crate) fn namespace_package(db: &'db dyn Db, name: ModuleName) -> Self {
|
||||
Self::Namespace(NamespacePackage::new(db, name))
|
||||
}
|
||||
|
||||
/// The absolute name of the module (e.g. `foo.bar`)
|
||||
pub fn name(&self) -> &ModuleName {
|
||||
match &*self.inner {
|
||||
ModuleInner::FileModule { name, .. } => name,
|
||||
ModuleInner::NamespacePackage { name, .. } => name,
|
||||
pub fn name(self, db: &'db dyn Database) -> &'db ModuleName {
|
||||
match self {
|
||||
Module::File(module) => module.name(db),
|
||||
Module::Namespace(ref package) => package.name(db),
|
||||
}
|
||||
}
|
||||
|
||||
/// The file to the source code that defines this module
|
||||
///
|
||||
/// This is `None` for namespace packages.
|
||||
pub fn file(&self) -> Option<File> {
|
||||
match &*self.inner {
|
||||
ModuleInner::FileModule { file, .. } => Some(*file),
|
||||
ModuleInner::NamespacePackage { .. } => None,
|
||||
pub fn file(self, db: &'db dyn Database) -> Option<File> {
|
||||
match self {
|
||||
Module::File(module) => Some(module.file(db)),
|
||||
Module::Namespace(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this a module that we special-case somehow? If so, which one?
|
||||
pub fn known(&self) -> Option<KnownModule> {
|
||||
match &*self.inner {
|
||||
ModuleInner::FileModule { known, .. } => *known,
|
||||
ModuleInner::NamespacePackage { .. } => None,
|
||||
pub fn known(self, db: &'db dyn Database) -> Option<KnownModule> {
|
||||
match self {
|
||||
Module::File(module) => module.known(db),
|
||||
Module::Namespace(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Does this module represent the given known module?
|
||||
pub fn is_known(&self, known_module: KnownModule) -> bool {
|
||||
self.known() == Some(known_module)
|
||||
pub fn is_known(self, db: &'db dyn Database, known_module: KnownModule) -> bool {
|
||||
self.known(db) == Some(known_module)
|
||||
}
|
||||
|
||||
/// The search path from which the module was resolved.
|
||||
pub(crate) fn search_path(&self) -> Option<&SearchPath> {
|
||||
match &*self.inner {
|
||||
ModuleInner::FileModule { search_path, .. } => Some(search_path),
|
||||
ModuleInner::NamespacePackage { .. } => None,
|
||||
pub(crate) fn search_path(self, db: &'db dyn Database) -> Option<&'db SearchPath> {
|
||||
match self {
|
||||
Module::File(module) => Some(module.search_path(db)),
|
||||
Module::Namespace(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether this module is a single-file module or a package
|
||||
pub fn kind(&self) -> ModuleKind {
|
||||
match &*self.inner {
|
||||
ModuleInner::FileModule { kind, .. } => *kind,
|
||||
ModuleInner::NamespacePackage { .. } => ModuleKind::Package,
|
||||
pub fn kind(self, db: &'db dyn Database) -> ModuleKind {
|
||||
match self {
|
||||
Module::File(module) => module.kind(db),
|
||||
Module::Namespace(_) => ModuleKind::Package,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,16 +94,13 @@ impl Module {
|
|||
///
|
||||
/// 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<'db>(&self, db: &'db dyn Db) -> &'db [Name] {
|
||||
self.clone()
|
||||
.all_submodules_inner(db, ())
|
||||
.as_deref()
|
||||
.unwrap_or_default()
|
||||
pub fn all_submodules(self, db: &'db dyn Db) -> &'db [Name] {
|
||||
self.all_submodules_inner(db).as_deref().unwrap_or_default()
|
||||
}
|
||||
|
||||
#[allow(clippy::ref_option, clippy::used_underscore_binding)]
|
||||
#[allow(clippy::ref_option)]
|
||||
#[salsa::tracked(returns(ref))]
|
||||
fn all_submodules_inner(self, db: &dyn Db, _dummy: ()) -> Option<Vec<Name>> {
|
||||
fn all_submodules_inner(self, db: &'db dyn Db) -> Option<Vec<Name>> {
|
||||
fn is_submodule(
|
||||
is_dir: bool,
|
||||
is_file: bool,
|
||||
|
|
@ -125,16 +118,14 @@ impl Module {
|
|||
// 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 ModuleInner::FileModule {
|
||||
kind: ModuleKind::Package,
|
||||
file,
|
||||
..
|
||||
} = &*self.inner
|
||||
else {
|
||||
let Module::File(module) = self else {
|
||||
return None;
|
||||
};
|
||||
if !matches!(module.kind(db), ModuleKind::Package) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let path = SystemOrVendoredPathRef::try_from_file(db, *file)?;
|
||||
let path = SystemOrVendoredPathRef::try_from_file(db, module.file(db))?;
|
||||
debug_assert!(
|
||||
matches!(path.file_name(), Some("__init__.py" | "__init__.pyi")),
|
||||
"expected package file `{:?}` to be `__init__.py` or `__init__.pyi`",
|
||||
|
|
@ -201,33 +192,41 @@ impl Module {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Module {
|
||||
impl std::fmt::Debug for Module<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Module")
|
||||
.field("name", &self.name())
|
||||
.field("kind", &self.kind())
|
||||
.field("file", &self.file())
|
||||
.field("search_path", &self.search_path())
|
||||
.field("known", &self.known())
|
||||
.finish()
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, get_size2::GetSize)]
|
||||
enum ModuleInner {
|
||||
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
|
||||
FileModule {
|
||||
name: ModuleName,
|
||||
kind: ModuleKind,
|
||||
search_path: SearchPath,
|
||||
file: File,
|
||||
known: Option<KnownModule>,
|
||||
},
|
||||
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
|
||||
#[salsa::tracked(debug)]
|
||||
pub struct FileModule<'db> {
|
||||
#[returns(ref)]
|
||||
name: ModuleName,
|
||||
kind: ModuleKind,
|
||||
#[returns(ref)]
|
||||
search_path: SearchPath,
|
||||
file: File,
|
||||
known: Option<KnownModule>,
|
||||
}
|
||||
|
||||
/// A namespace package. Namespace packages are special because
|
||||
/// there are multiple possible paths and they have no corresponding
|
||||
/// code file.
|
||||
NamespacePackage { name: ModuleName },
|
||||
/// A namespace package.
|
||||
///
|
||||
/// Namespace packages are special because there are
|
||||
/// multiple possible paths and they have no corresponding code file.
|
||||
#[salsa::tracked(debug)]
|
||||
pub struct NamespacePackage<'db> {
|
||||
#[returns(ref)]
|
||||
name: ModuleName,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue