diff --git a/Cargo.lock b/Cargo.lock index dfacb64588..e49d754447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3073,6 +3073,7 @@ name = "ruff_memory_usage" version = "0.0.0" dependencies = [ "get-size2", + "ordermap", ] [[package]] diff --git a/crates/ruff_db/src/file_revision.rs b/crates/ruff_db/src/file_revision.rs index 8662653299..1f320ec7ae 100644 --- a/crates/ruff_db/src/file_revision.rs +++ b/crates/ruff_db/src/file_revision.rs @@ -9,7 +9,7 @@ use crate::system::file_time_now; /// * The last modification time of the file. /// * The hash of the file's content. /// * The revision as it comes from an external system, for example the LSP. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)] pub struct FileRevision(u128); impl FileRevision { diff --git a/crates/ruff_db/src/files.rs b/crates/ruff_db/src/files.rs index 9393ee4b7c..9f6bcbbdd1 100644 --- a/crates/ruff_db/src/files.rs +++ b/crates/ruff_db/src/files.rs @@ -289,7 +289,7 @@ impl std::panic::RefUnwindSafe for Files {} /// # Ordering /// Ordering is based on the file's salsa-assigned id and not on its values. /// The id may change between runs. -#[salsa::input] +#[salsa::input(heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct File { /// The path of the file (immutable). @@ -521,7 +521,7 @@ impl VirtualFile { // The types in here need to be public because they're salsa ingredients but we // don't want them to be publicly accessible. That's why we put them into a private module. mod private { - #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] + #[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)] pub enum FileStatus { /// The file exists. #[default] diff --git a/crates/ruff_db/src/files/file_root.rs b/crates/ruff_db/src/files/file_root.rs index 4c134f532f..7eb948286a 100644 --- a/crates/ruff_db/src/files/file_root.rs +++ b/crates/ruff_db/src/files/file_root.rs @@ -16,7 +16,7 @@ use crate::system::{SystemPath, SystemPathBuf}; /// The main usage of file roots is to determine a file's durability. But it can also be used /// to make a salsa query dependent on whether a file in a root has changed without writing any /// manual invalidation logic. -#[salsa::input(debug)] +#[salsa::input(debug, heap_size=ruff_memory_usage::heap_size)] pub struct FileRoot { /// The path of a root is guaranteed to never change. #[returns(deref)] @@ -37,7 +37,7 @@ impl FileRoot { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)] pub enum FileRootKind { /// The root of a project. Project, diff --git a/crates/ruff_db/src/files/path.rs b/crates/ruff_db/src/files/path.rs index ebe6e7261a..557a70cdf9 100644 --- a/crates/ruff_db/src/files/path.rs +++ b/crates/ruff_db/src/files/path.rs @@ -11,7 +11,7 @@ use std::fmt::{Display, Formatter}; /// * a file stored on the [host system](crate::system::System). /// * a virtual file stored on the [host system](crate::system::System). /// * a vendored file stored in the [vendored file system](crate::vendored::VendoredFileSystem). -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)] pub enum FilePath { /// Path to a file on the [host system](crate::system::System). System(SystemPathBuf), diff --git a/crates/ruff_db/src/system/path.rs b/crates/ruff_db/src/system/path.rs index 06e29e3a40..d0ff92e5d8 100644 --- a/crates/ruff_db/src/system/path.rs +++ b/crates/ruff_db/src/system/path.rs @@ -762,7 +762,7 @@ impl SystemVirtualPath { } /// An owned, virtual path on [`System`](`super::System`) (akin to [`String`]). -#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord)] +#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord, get_size2::GetSize)] pub struct SystemVirtualPathBuf(String); impl SystemVirtualPathBuf { diff --git a/crates/ruff_memory_usage/Cargo.toml b/crates/ruff_memory_usage/Cargo.toml index 1b87de4bbf..fd6b408303 100644 --- a/crates/ruff_memory_usage/Cargo.toml +++ b/crates/ruff_memory_usage/Cargo.toml @@ -12,6 +12,7 @@ license = { workspace = true } [dependencies] get-size2 = { workspace = true } +ordermap = { workspace = true } [lints] workspace = true diff --git a/crates/ruff_memory_usage/src/lib.rs b/crates/ruff_memory_usage/src/lib.rs index d10c64283a..d2002bc6bb 100644 --- a/crates/ruff_memory_usage/src/lib.rs +++ b/crates/ruff_memory_usage/src/lib.rs @@ -1,6 +1,7 @@ use std::sync::{LazyLock, Mutex}; use get_size2::{GetSize, StandardTracker}; +use ordermap::OrderSet; /// Returns the memory usage of the provided object, using a global tracker to avoid /// double-counting shared objects. @@ -12,3 +13,8 @@ pub fn heap_size(value: &T) -> usize { .get_heap_size_with_tracker(&mut *TRACKER.lock().unwrap()) .0 } + +/// An implementation of [`GetSize::get_heap_size`] for [`OrderSet`]. +pub fn order_set_heap_size(set: &OrderSet) -> usize { + (set.capacity() * T::get_stack_size()) + set.iter().map(heap_size).sum::() +} diff --git a/crates/ty_project/src/db.rs b/crates/ty_project/src/db.rs index 1a0e833af7..1315811866 100644 --- a/crates/ty_project/src/db.rs +++ b/crates/ty_project/src/db.rs @@ -128,18 +128,49 @@ impl ProjectDatabase { /// to the CLI after a typechecker run. pub fn salsa_memory_dump(&self) -> SalsaMemoryDump { let memory_usage = ::memory_usage(self); - let mut ingredients = memory_usage.structs; - let mut memos = memory_usage.queries.into_iter().collect::>(); + let mut ingredients = memory_usage + .structs + .into_iter() + .filter(|ingredient| ingredient.count() > 0) + .collect::>(); + let mut memos = memory_usage + .queries + .into_iter() + .filter(|(_, memos)| memos.count() > 0) + .collect::>(); - ingredients.sort_by_key(|ingredient| cmp::Reverse(ingredient.size_of_fields())); - memos.sort_by_key(|(_, memo)| cmp::Reverse(memo.size_of_fields())); + ingredients.sort_by_key(|ingredient| { + let heap_size = ingredient.heap_size_of_fields().unwrap_or_else(|| { + // Salsa currently does not expose a way to track the heap size of interned + // query arguments. + if !ingredient.debug_name().contains("interned_arguments") { + tracing::warn!( + "expected `heap_size` to be provided by Salsa struct `{}`", + ingredient.debug_name() + ); + } + + 0 + }); + + cmp::Reverse(ingredient.size_of_fields() + heap_size) + }); + + memos.sort_by_key(|(query, memo)| { + let heap_size = memo.heap_size_of_fields().unwrap_or_else(|| { + tracing::warn!("expected `heap_size` to be provided by Salsa query `{query}`"); + 0 + }); + + cmp::Reverse(memo.size_of_fields() + heap_size) + }); let mut total_fields = 0; let mut total_metadata = 0; for ingredient in &ingredients { - total_metadata += ingredient.size_of_metadata(); total_fields += ingredient.size_of_fields(); total_fields += ingredient.heap_size_of_fields().unwrap_or(0); + total_metadata += ingredient.size_of_metadata(); } let mut total_memo_fields = 0; @@ -171,7 +202,7 @@ impl std::fmt::Debug for ProjectDatabase { } } -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, get_size2::GetSize)] #[cfg_attr(test, derive(serde::Serialize))] pub enum CheckMode { /// Checks the open files in the project. @@ -281,12 +312,15 @@ impl SalsaMemoryDump { writeln!(f, "=======SALSA STRUCTS=======")?; for ingredient in ingredients { + let size_of_fields = + ingredient.size_of_fields() + ingredient.heap_size_of_fields().unwrap_or(0); + writeln!( f, "{:<50} metadata={:<8} fields={:<8} count={}", format!("`{}`", ingredient.debug_name()), format!("{:.2}MB", bytes_to_mb(ingredient.size_of_metadata())), - format!("{:.2}MB", bytes_to_mb(ingredient.size_of_fields())), + format!("{:.2}MB", bytes_to_mb(size_of_fields)), ingredient.count() )?; } @@ -294,13 +328,16 @@ impl SalsaMemoryDump { writeln!(f, "=======SALSA QUERIES=======")?; for (query_fn, memo) in memos { + let size_of_fields = + memo.size_of_fields() + memo.heap_size_of_fields().unwrap_or(0); + writeln!(f, "`{query_fn} -> {}`", memo.debug_name())?; writeln!( f, " metadata={:<8} fields={:<8} count={}", format!("{:.2}MB", bytes_to_mb(memo.size_of_metadata())), - format!("{:.2}MB", bytes_to_mb(memo.size_of_fields())), + format!("{:.2}MB", bytes_to_mb(size_of_fields)), memo.count() )?; } diff --git a/crates/ty_project/src/files.rs b/crates/ty_project/src/files.rs index 6d60a13eb8..079a42d2a8 100644 --- a/crates/ty_project/src/files.rs +++ b/crates/ty_project/src/files.rs @@ -20,7 +20,7 @@ use crate::{IOErrorDiagnostic, Project}; /// so no query can be depending on the contents of the indexed files before that. All subsequent mutations to /// the indexed files must go through `IndexedMut`, which uses the Salsa setter `project.set_file_set` to /// ensure that Salsa always knows when the set of indexed files have changed. -#[derive(Debug)] +#[derive(Debug, get_size2::GetSize)] pub struct IndexedFiles { state: std::sync::Mutex, } @@ -102,7 +102,7 @@ impl Default for IndexedFiles { } } -#[derive(Debug)] +#[derive(Debug, get_size2::GetSize)] enum State { /// The files of a package haven't been indexed yet. Lazy, @@ -150,7 +150,7 @@ pub struct Indexed<'db> { _lifetime: PhantomData<&'db ()>, } -#[derive(Debug)] +#[derive(Debug, get_size2::GetSize)] struct IndexedInner { files: FxHashSet, diagnostics: Vec, diff --git a/crates/ty_project/src/glob.rs b/crates/ty_project/src/glob.rs index e98a1e881c..7127f55f41 100644 --- a/crates/ty_project/src/glob.rs +++ b/crates/ty_project/src/glob.rs @@ -13,7 +13,7 @@ mod portable; /// Path filtering based on an an exclude and include glob pattern set. /// /// Exclude patterns take precedence over includes. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)] pub struct IncludeExcludeFilter { include: IncludeFilter, exclude: ExcludeFilter, diff --git a/crates/ty_project/src/glob/exclude.rs b/crates/ty_project/src/glob/exclude.rs index 4f236db196..ab657c0c76 100644 --- a/crates/ty_project/src/glob/exclude.rs +++ b/crates/ty_project/src/glob/exclude.rs @@ -23,7 +23,7 @@ use crate::glob::portable::AbsolutePortableGlobPattern; /// Two filters are equal if they're constructed from the same patterns (including order). /// Two filters that exclude the exact same files but were constructed from different patterns aren't considered /// equal. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub(crate) struct ExcludeFilter { ignore: Gitignore, } @@ -118,10 +118,12 @@ impl ExcludeFilterBuilder { /// Two ignore matches are only equal if they're constructed from the same patterns (including order). /// Two matchers that were constructed from different patterns but result in /// including the same files don't compare equal. -#[derive(Clone)] +#[derive(Clone, get_size2::GetSize)] struct Gitignore { + #[get_size(ignore)] set: GlobSet, globs: Vec, + #[get_size(ignore)] matches: Option>>>, } @@ -185,7 +187,7 @@ enum Match { Allow, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] struct IgnoreGlob { /// The pattern that was originally parsed. original: String, diff --git a/crates/ty_project/src/glob/include.rs b/crates/ty_project/src/glob/include.rs index c3c61f26f2..e133d13a95 100644 --- a/crates/ty_project/src/glob/include.rs +++ b/crates/ty_project/src/glob/include.rs @@ -29,13 +29,20 @@ const DFA_SIZE_LIMIT: usize = 1_000_000; /// /// Because of that, two filters that include the exact same files but were /// constructed from different patterns (or even just order) compare unequal. -#[derive(Clone)] +#[derive(Clone, get_size2::GetSize)] pub(crate) struct IncludeFilter { + #[get_size(ignore)] glob_set: GlobSet, original_patterns: Box<[String]>, + #[get_size(size_fn = dfa_memory_usage)] dfa: Option>>, } +#[allow(clippy::ref_option)] +fn dfa_memory_usage(dfa: &Option>>) -> usize { + dfa.as_ref().map(dfa::dense::DFA::memory_usage).unwrap_or(0) +} + impl IncludeFilter { /// Whether the file matches any of the globs. pub(crate) fn match_file(&self, path: impl AsRef) -> bool { diff --git a/crates/ty_project/src/lib.rs b/crates/ty_project/src/lib.rs index dcf1d2f0ae..f9809541b4 100644 --- a/crates/ty_project/src/lib.rs +++ b/crates/ty_project/src/lib.rs @@ -55,7 +55,7 @@ pub fn default_lints_registry() -> LintRegistry { /// 2. Running `ruff check` with different target versions results in different programs (settings) but /// it remains the same project. That's why program is a narrowed view of the project only /// holding on to the most fundamental settings required for checking. -#[salsa::input] +#[salsa::input(heap_size=ruff_memory_usage::heap_size)] #[derive(Debug)] pub struct Project { /// The files that are open in the project, [`None`] if there are no open files. @@ -636,7 +636,7 @@ impl Iterator for ProjectFilesIter<'_> { impl FusedIterator for ProjectFilesIter<'_> {} -#[derive(Debug, Clone)] +#[derive(Debug, Clone, get_size2::GetSize)] pub struct IOErrorDiagnostic { file: Option, error: IOErrorKind, @@ -652,7 +652,7 @@ impl IOErrorDiagnostic { } } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, get_size2::GetSize)] enum IOErrorKind { #[error(transparent)] Walk(#[from] walk::WalkError), diff --git a/crates/ty_project/src/metadata.rs b/crates/ty_project/src/metadata.rs index 4afefec310..62931b3e64 100644 --- a/crates/ty_project/src/metadata.rs +++ b/crates/ty_project/src/metadata.rs @@ -19,7 +19,7 @@ pub mod pyproject; pub mod settings; pub mod value; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, get_size2::GetSize)] #[cfg_attr(test, derive(serde::Serialize))] pub struct ProjectMetadata { pub(super) name: Name, diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs index 83c23cf791..cc008bbd09 100644 --- a/crates/ty_project/src/metadata/options.rs +++ b/crates/ty_project/src/metadata/options.rs @@ -36,7 +36,16 @@ use ty_python_semantic::{ }; #[derive( - Debug, Default, Clone, PartialEq, Eq, Combine, Serialize, Deserialize, OptionsMetadata, + Debug, + Default, + Clone, + PartialEq, + Eq, + Combine, + Serialize, + Deserialize, + OptionsMetadata, + get_size2::GetSize, )] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -395,7 +404,16 @@ impl Options { } #[derive( - Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata, + Debug, + Default, + Clone, + Eq, + PartialEq, + Combine, + Serialize, + Deserialize, + OptionsMetadata, + get_size2::GetSize, )] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -516,7 +534,16 @@ pub struct EnvironmentOptions { } #[derive( - Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata, + Debug, + Default, + Clone, + Eq, + PartialEq, + Combine, + Serialize, + Deserialize, + OptionsMetadata, + get_size2::GetSize, )] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -679,11 +706,14 @@ impl SrcOptions { } } -#[derive(Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, Hash)] +#[derive( + Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, Hash, get_size2::GetSize, +)] #[serde(rename_all = "kebab-case", transparent)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct Rules { #[cfg_attr(feature = "schemars", schemars(with = "schema::Rules"))] + #[get_size(ignore)] // TODO: Add `GetSize` support for `OrderMap`. inner: OrderMap, RangedValue, BuildHasherDefault>, } @@ -1002,7 +1032,9 @@ impl GlobFilterContext { } /// The diagnostic output format. -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] +#[derive( + Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, get_size2::GetSize, +)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub enum OutputFormat { @@ -1035,7 +1067,16 @@ impl From for DiagnosticFormat { } #[derive( - Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata, + Debug, + Default, + Clone, + Eq, + PartialEq, + Combine, + Serialize, + Deserialize, + OptionsMetadata, + get_size2::GetSize, )] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -1095,7 +1136,18 @@ pub struct TerminalOptions { /// [tool.ty.overrides.rules] /// possibly-unresolved-reference = "ignore" /// ``` -#[derive(Debug, Default, Clone, PartialEq, Eq, Combine, Serialize, Deserialize, RustDoc)] +#[derive( + Debug, + Default, + Clone, + PartialEq, + Eq, + Combine, + Serialize, + Deserialize, + RustDoc, + get_size2::GetSize, +)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[serde(transparent)] pub struct OverridesOptions(Vec>); @@ -1119,7 +1171,16 @@ impl Deref for OverridesOptions { } #[derive( - Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata, + Debug, + Default, + Clone, + Eq, + PartialEq, + Combine, + Serialize, + Deserialize, + OptionsMetadata, + get_size2::GetSize, )] #[serde(rename_all = "kebab-case", deny_unknown_fields)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -1339,7 +1400,7 @@ impl RangedValue { } /// The options for an override but without the include/exclude patterns. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Combine)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Combine, get_size2::GetSize)] pub(super) struct InnerOverrideOptions { /// Raw rule options as specified in the configuration. /// Used when multiple overrides match a file and need to be merged. @@ -1463,7 +1524,7 @@ pub enum TyTomlError { TomlSyntax(#[from] toml::de::Error), } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] pub struct OptionDiagnostic { id: DiagnosticId, message: String, diff --git a/crates/ty_project/src/metadata/settings.rs b/crates/ty_project/src/metadata/settings.rs index 11bd0f92c2..347daa57e2 100644 --- a/crates/ty_project/src/metadata/settings.rs +++ b/crates/ty_project/src/metadata/settings.rs @@ -20,7 +20,7 @@ use crate::{Db, glob::IncludeExcludeFilter}; /// This can be achieved by adding a salsa query for the type checking specific settings. /// /// Settings that are part of [`ty_python_semantic::ProgramSettings`] are not included here. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)] pub struct Settings { pub(super) rules: Arc, pub(super) terminal: TerminalSettings, @@ -56,20 +56,20 @@ impl Settings { } } -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default, get_size2::GetSize)] pub struct TerminalSettings { pub output_format: OutputFormat, pub error_on_warning: bool, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub struct SrcSettings { pub respect_ignore_files: bool, pub files: IncludeExcludeFilter, } /// A single configuration override that applies to files matching specific patterns. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub struct Override { /// File pattern filter to determine which files this override applies to. pub(super) files: IncludeExcludeFilter, diff --git a/crates/ty_project/src/metadata/value.rs b/crates/ty_project/src/metadata/value.rs index fdbee185a9..95a157a451 100644 --- a/crates/ty_project/src/metadata/value.rs +++ b/crates/ty_project/src/metadata/value.rs @@ -16,7 +16,7 @@ use std::sync::Arc; use toml::Spanned; use ty_combine::Combine; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub enum ValueSource { /// Value loaded from a project's configuration file. /// @@ -84,7 +84,7 @@ impl Drop for ValueSourceGuard { /// /// This ensures that two resolved configurations are identical even if the position of a value has changed /// or if the values were loaded from different sources. -#[derive(Clone, serde::Serialize)] +#[derive(Clone, serde::Serialize, get_size2::GetSize)] #[serde(transparent)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct RangedValue { @@ -325,6 +325,7 @@ where Ord, Hash, Combine, + get_size2::GetSize, )] #[serde(transparent)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] @@ -394,6 +395,7 @@ impl fmt::Display for RelativePathBuf { Ord, Hash, Combine, + get_size2::GetSize, )] #[serde(transparent)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] diff --git a/crates/ty_project/src/walk.rs b/crates/ty_project/src/walk.rs index 74ddc46d5c..ea161e187d 100644 --- a/crates/ty_project/src/walk.rs +++ b/crates/ty_project/src/walk.rs @@ -310,7 +310,7 @@ impl<'a> ProjectFilesWalker<'a> { } } -#[derive(Error, Debug, Clone)] +#[derive(Error, Debug, Clone, get_size2::GetSize)] pub(crate) enum WalkError { #[error("`{path}`: {error}")] IOPathError { path: SystemPathBuf, error: String }, diff --git a/crates/ty_python_semantic/src/module_resolver/module.rs b/crates/ty_python_semantic/src/module_resolver/module.rs index 29722cd376..8e0a89a155 100644 --- a/crates/ty_python_semantic/src/module_resolver/module.rs +++ b/crates/ty_python_semantic/src/module_resolver/module.rs @@ -211,7 +211,7 @@ fn all_submodule_names_for_package(db: &dyn Db, file: File) -> Option> } /// A module that resolves to a file (`lib.py` or `package/__init__.py`) -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub struct FileModule<'db> { #[returns(ref)] name: ModuleName, @@ -226,7 +226,7 @@ pub struct FileModule<'db> { /// /// Namespace packages are special because there are /// multiple possible paths and they have no corresponding code file. -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub struct NamespacePackage<'db> { #[returns(ref)] name: ModuleName, diff --git a/crates/ty_python_semantic/src/module_resolver/resolver.rs b/crates/ty_python_semantic/src/module_resolver/resolver.rs index d178105990..52335901ac 100644 --- a/crates/ty_python_semantic/src/module_resolver/resolver.rs +++ b/crates/ty_python_semantic/src/module_resolver/resolver.rs @@ -45,7 +45,7 @@ pub fn resolve_real_module<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Op } /// Which files should be visible when doing a module query -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, get_size2::GetSize)] pub(crate) enum ModuleResolveMode { /// Stubs are allowed to appear. /// @@ -60,7 +60,7 @@ pub(crate) enum ModuleResolveMode { StubsNotAllowed, } -#[salsa::interned] +#[salsa::interned(heap_size=ruff_memory_usage::heap_size)] #[derive(Debug)] pub(crate) struct ModuleResolveModeIngredient<'db> { mode: ModuleResolveMode, @@ -172,7 +172,7 @@ pub(crate) fn search_paths(db: &dyn Db, resolve_mode: ModuleResolveMode) -> Sear Program::get(db).search_paths(db).iter(db, resolve_mode) } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub struct SearchPaths { /// Search paths that have been statically determined purely from reading /// ty's configuration settings. These shouldn't ever change unless the @@ -643,7 +643,7 @@ impl<'db> Iterator for PthFileIterator<'db> { /// A thin wrapper around `ModuleName` to make it a Salsa ingredient. /// /// This is needed because Salsa requires that all query arguments are salsa ingredients. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] struct ModuleNameIngredient<'db> { #[returns(ref)] pub(super) name: ModuleName, diff --git a/crates/ty_python_semantic/src/module_resolver/typeshed.rs b/crates/ty_python_semantic/src/module_resolver/typeshed.rs index 432c9bd4fa..5b67ae3c77 100644 --- a/crates/ty_python_semantic/src/module_resolver/typeshed.rs +++ b/crates/ty_python_semantic/src/module_resolver/typeshed.rs @@ -74,7 +74,7 @@ pub(crate) enum TypeshedVersionsParseErrorKind { VersionParseError(#[from] PythonVersionDeserializationError), } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub(crate) struct TypeshedVersions(FxHashMap); impl TypeshedVersions { @@ -230,7 +230,7 @@ impl fmt::Display for TypeshedVersions { } } -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)] pub(crate) enum PyVersionRange { AvailableFrom(RangeFrom), AvailableWithin(RangeInclusive), diff --git a/crates/ty_python_semantic/src/program.rs b/crates/ty_python_semantic/src/program.rs index 760e934d41..8f06527951 100644 --- a/crates/ty_python_semantic/src/program.rs +++ b/crates/ty_python_semantic/src/program.rs @@ -13,7 +13,7 @@ use ruff_text_size::TextRange; use salsa::Durability; use salsa::Setter; -#[salsa::input(singleton)] +#[salsa::input(singleton, heap_size=ruff_memory_usage::heap_size)] pub struct Program { #[returns(ref)] pub python_version_with_source: PythonVersionWithSource, @@ -93,7 +93,7 @@ pub struct ProgramSettings { pub search_paths: SearchPaths, } -#[derive(Clone, Debug, Eq, PartialEq, Default)] +#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)] pub enum PythonVersionSource { /// Value loaded from a project's configuration file. ConfigFile(PythonVersionFileSource), @@ -123,7 +123,7 @@ pub enum PythonVersionSource { /// Information regarding the file and [`TextRange`] of the configuration /// from which we inferred the Python version. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] pub struct PythonVersionFileSource { path: Arc, range: Option, @@ -145,7 +145,7 @@ impl PythonVersionFileSource { } } -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)] pub struct PythonVersionWithSource { pub version: PythonVersion, pub source: PythonVersionSource, diff --git a/crates/ty_python_semantic/src/python_platform.rs b/crates/ty_python_semantic/src/python_platform.rs index 573165f82e..6f0c0fbab4 100644 --- a/crates/ty_python_semantic/src/python_platform.rs +++ b/crates/ty_python_semantic/src/python_platform.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; /// The target platform to assume when resolving types. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] #[cfg_attr( feature = "serde", derive(serde::Serialize, serde::Deserialize, ruff_macros::RustDoc), diff --git a/crates/ty_python_semantic/src/semantic_index/definition.rs b/crates/ty_python_semantic/src/semantic_index/definition.rs index f10630b1d7..50e89a1180 100644 --- a/crates/ty_python_semantic/src/semantic_index/definition.rs +++ b/crates/ty_python_semantic/src/semantic_index/definition.rs @@ -22,7 +22,7 @@ use crate::unpack::{Unpack, UnpackPosition}; /// because a new scope gets inserted before the `Definition` or a new place is inserted /// before this `Definition`. However, the ID can be considered stable and it is okay to use /// `Definition` in cross-module` salsa queries or as a field on other salsa tracked structs. -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub struct Definition<'db> { /// The file in which the definition occurs. pub file: File, @@ -645,7 +645,7 @@ impl DefinitionCategory { /// [`DefinitionKind`] fields in salsa tracked structs should be tracked (attributed with `#[tracked]`) /// because the kind is a thin wrapper around [`AstNodeRef`]. See the [`AstNodeRef`] documentation /// for an in-depth explanation of why this is necessary. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub enum DefinitionKind<'db> { Import(ImportDefinitionKind), ImportFrom(ImportFromDefinitionKind), @@ -834,7 +834,7 @@ impl DefinitionKind<'_> { } } -#[derive(Copy, Clone, Debug, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize)] pub(crate) enum TargetKind<'db> { Sequence(UnpackPosition, Unpack<'db>), /// Name, attribute, or subscript. @@ -850,7 +850,7 @@ impl<'db> From)>> for TargetKind<'db> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct StarImportDefinitionKind { node: AstNodeRef, symbol_id: ScopedSymbolId, @@ -880,7 +880,7 @@ impl StarImportDefinitionKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct MatchPatternDefinitionKind { pattern: AstNodeRef, identifier: AstNodeRef, @@ -902,7 +902,7 @@ impl MatchPatternDefinitionKind { /// But if the target is an attribute or subscript, its definition is not in the comprehension's scope; /// it is in the scope in which the root variable is bound. /// TODO: currently we don't model this correctly and simply assume that it is in a scope outside the comprehension. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct ComprehensionDefinitionKind<'db> { target_kind: TargetKind<'db>, iterable: AstNodeRef, @@ -933,7 +933,7 @@ impl<'db> ComprehensionDefinitionKind<'db> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct ImportDefinitionKind { node: AstNodeRef, alias_index: usize, @@ -954,7 +954,7 @@ impl ImportDefinitionKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct ImportFromDefinitionKind { node: AstNodeRef, alias_index: usize, @@ -975,7 +975,7 @@ impl ImportFromDefinitionKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct AssignmentDefinitionKind<'db> { target_kind: TargetKind<'db>, value: AstNodeRef, @@ -996,7 +996,7 @@ impl<'db> AssignmentDefinitionKind<'db> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct AnnotatedAssignmentDefinitionKind { annotation: AstNodeRef, value: Option>, @@ -1017,7 +1017,7 @@ impl AnnotatedAssignmentDefinitionKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct WithItemDefinitionKind<'db> { target_kind: TargetKind<'db>, context_expr: AstNodeRef, @@ -1043,7 +1043,7 @@ impl<'db> WithItemDefinitionKind<'db> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct ForStmtDefinitionKind<'db> { target_kind: TargetKind<'db>, iterable: AstNodeRef, @@ -1069,7 +1069,7 @@ impl<'db> ForStmtDefinitionKind<'db> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, get_size2::GetSize)] pub struct ExceptHandlerDefinitionKind { handler: AstNodeRef, is_star: bool, diff --git a/crates/ty_python_semantic/src/semantic_index/expression.rs b/crates/ty_python_semantic/src/semantic_index/expression.rs index 72e6160c7c..3f6f159d17 100644 --- a/crates/ty_python_semantic/src/semantic_index/expression.rs +++ b/crates/ty_python_semantic/src/semantic_index/expression.rs @@ -10,7 +10,7 @@ use salsa; /// a type expression. For example, in `self.x: = `, the /// `` is inferred as a type expression, while `` is inferred /// as a normal expression. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] pub(crate) enum ExpressionKind { Normal, TypeExpression, @@ -31,7 +31,7 @@ pub(crate) enum ExpressionKind { /// * a return type of a cross-module query /// * a field of a type that is a return type of a cross-module query /// * an argument of a cross-module query -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub(crate) struct Expression<'db> { /// The file in which the expression occurs. pub(crate) file: File, diff --git a/crates/ty_python_semantic/src/semantic_index/predicate.rs b/crates/ty_python_semantic/src/semantic_index/predicate.rs index d898c67bb1..7f96c13630 100644 --- a/crates/ty_python_semantic/src/semantic_index/predicate.rs +++ b/crates/ty_python_semantic/src/semantic_index/predicate.rs @@ -117,7 +117,7 @@ pub(crate) enum PredicateNode<'db> { StarImportPlaceholder(StarImportPlaceholderPredicate<'db>), } -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) enum ClassPatternKind { Irrefutable, Refutable, @@ -130,7 +130,7 @@ impl ClassPatternKind { } /// Pattern kinds for which we support type narrowing and/or static reachability analysis. -#[derive(Debug, Clone, Hash, PartialEq, salsa::Update)] +#[derive(Debug, Clone, Hash, PartialEq, salsa::Update, get_size2::GetSize)] pub(crate) enum PatternPredicateKind<'db> { Singleton(Singleton), Value(Expression<'db>), @@ -140,7 +140,7 @@ pub(crate) enum PatternPredicateKind<'db> { Unsupported, } -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub(crate) struct PatternPredicate<'db> { pub(crate) file: File, @@ -206,7 +206,7 @@ impl<'db> PatternPredicate<'db> { /// - If it resolves to a possibly bound symbol, then the predicate resolves to [`Truthiness::Ambiguous`] /// /// [Truthiness]: [crate::types::Truthiness] -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub(crate) struct StarImportPlaceholderPredicate<'db> { pub(crate) importing_file: File, diff --git a/crates/ty_python_semantic/src/semantic_index/scope.rs b/crates/ty_python_semantic/src/semantic_index/scope.rs index 29c262ef4c..6c3fcdbd71 100644 --- a/crates/ty_python_semantic/src/semantic_index/scope.rs +++ b/crates/ty_python_semantic/src/semantic_index/scope.rs @@ -14,7 +14,7 @@ use crate::{ }; /// A cross-module identifier of a scope that can be used as a salsa query parameter. -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub struct ScopeId<'db> { pub file: File, diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3d0d57d145..babb3e01c1 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -385,7 +385,7 @@ pub(crate) use todo_type; /// # Ordering /// Ordering is based on the property instance's salsa-assigned id and not on its values. /// The id may change between runs, or when the property instance was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct PropertyInstanceType<'db> { getter: Option>, @@ -6079,7 +6079,7 @@ impl<'db> From<&Type<'db>> for Type<'db> { /// This is represented as an enum (with some variants using `Cow`), and not an `FnMut` trait, /// since we sometimes have to apply type mappings lazily (e.g., to the signature of a function /// literal). -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub enum TypeMapping<'a, 'db> { /// Applies a specialization to the type Specialization(Specialization<'db>), @@ -6610,7 +6610,7 @@ impl<'db> InvalidTypeExpression<'db> { } /// Data regarding a `warnings.deprecated` or `typing_extensions.deprecated` decorator. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct DeprecatedInstance<'db> { /// The message for the deprecation @@ -6622,7 +6622,7 @@ impl get_size2::GetSize for DeprecatedInstance<'_> {} /// Contains information about instances of `dataclasses.Field`, typically created using /// `dataclasses.field()`. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct FieldInstance<'db> { /// The type of the default value for this field. This is derived from the `default` or @@ -6648,7 +6648,7 @@ impl<'db> FieldInstance<'db> { /// Whether this typevar was created via the legacy `TypeVar` constructor, using PEP 695 syntax, /// or an implicit typevar like `Self` was used. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub enum TypeVarKind { /// `T = TypeVar("T")` Legacy, @@ -6694,7 +6694,7 @@ pub enum TypeVarKind { /// # Ordering /// Ordering is based on the type var instance's salsa-assigned id and not on its values. /// The id may change between runs, or when the type var instance was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct TypeVarInstance<'db> { /// The name of this TypeVar (e.g. `T`) @@ -6789,7 +6789,7 @@ impl<'db> TypeVarInstance<'db> { } /// Where a type variable is bound and usable. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub enum BindingContext<'db> { /// The definition of the generic class, function, or type alias that binds this typevar. Definition(Definition<'db>), @@ -6809,7 +6809,7 @@ impl<'db> BindingContext<'db> { /// A type variable that has been bound to a generic context, and which can be specialized to a /// concrete type. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct BoundTypeVarInstance<'db> { pub typevar: TypeVarInstance<'db>, @@ -6874,7 +6874,7 @@ impl<'db> BoundTypeVarInstance<'db> { } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub enum TypeVarVariance { Invariant, Covariant, @@ -6896,7 +6896,7 @@ impl TypeVarVariance { } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub enum TypeVarBoundOrConstraints<'db> { UpperBound(Type<'db>), Constraints(UnionType<'db>), @@ -7916,7 +7916,7 @@ impl From for Truthiness { /// # Ordering /// Ordering is based on the bounded method's salsa-assigned id and not on its values. /// The id may change between runs, or when the bounded method was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct BoundMethodType<'db> { /// The function that is being bound. Corresponds to the `__func__` attribute on a @@ -7992,7 +7992,7 @@ impl<'db> BoundMethodType<'db> { /// # Ordering /// Ordering is based on the callable type's salsa-assigned id and not on its values. /// The id may change between runs, or when the callable type was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct CallableType<'db> { #[returns(ref)] @@ -8270,7 +8270,7 @@ pub enum WrapperDescriptorKind { /// # Ordering /// Ordering is based on the module literal's salsa-assigned id and not on its values. /// The id may change between runs, or when the module literal was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct ModuleLiteralType<'db> { /// The imported module. @@ -8380,7 +8380,7 @@ impl<'db> ModuleLiteralType<'db> { /// # Ordering /// Ordering is based on the type alias's salsa-assigned id and not on its values. /// The id may change between runs, or when the alias was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct PEP695TypeAliasType<'db> { #[returns(ref)] @@ -8427,7 +8427,7 @@ impl<'db> PEP695TypeAliasType<'db> { /// # Ordering /// Ordering is based on the type alias's salsa-assigned id and not on its values. /// The id may change between runs, or when the alias was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct BareTypeAliasType<'db> { #[returns(ref)] @@ -8522,7 +8522,7 @@ pub(super) struct MetaclassCandidate<'db> { explicit_metaclass_of: ClassLiteral<'db>, } -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] pub struct UnionType<'db> { /// The union type includes values in any of these types. #[returns(deref)] @@ -8744,7 +8744,7 @@ impl<'db> UnionType<'db> { } } -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=IntersectionType::heap_size)] pub struct IntersectionType<'db> { /// The intersection type includes only values in all of these types. #[returns(ref)] @@ -8938,12 +8938,17 @@ impl<'db> IntersectionType<'db> { pub fn has_one_element(&self, db: &'db dyn Db) -> bool { (self.positive(db).len() + self.negative(db).len()) == 1 } + + fn heap_size((positive, negative): &(FxOrderSet>, FxOrderSet>)) -> usize { + ruff_memory_usage::order_set_heap_size(positive) + + ruff_memory_usage::order_set_heap_size(negative) + } } /// # Ordering /// Ordering is based on the string literal's salsa-assigned id and not on its value. /// The id may change between runs, or when the string literal was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct StringLiteralType<'db> { #[returns(deref)] @@ -8971,7 +8976,7 @@ impl<'db> StringLiteralType<'db> { /// # Ordering /// Ordering is based on the byte literal's salsa-assigned id and not on its value. /// The id may change between runs, or when the byte literal was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct BytesLiteralType<'db> { #[returns(deref)] @@ -8996,7 +9001,7 @@ impl<'db> BytesLiteralType<'db> { /// NO = 0 /// YES = 1 /// ``` -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct EnumLiteralType<'db> { /// A reference to the enum class this literal belongs to @@ -9017,7 +9022,7 @@ impl<'db> EnumLiteralType<'db> { /// Type that represents the set of all inhabitants (`dict` instances) that conform to /// a given `TypedDict` schema. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct TypedDictType<'db> { /// A reference to the class (inheriting from `typing.TypedDict`) that specifies the @@ -9104,7 +9109,7 @@ impl BoundSuperError<'_> { } } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, get_size2::GetSize)] pub enum SuperOwnerKind<'db> { Dynamic(DynamicType), Class(ClassType<'db>), @@ -9193,7 +9198,7 @@ impl<'db> From> for Type<'db> { } /// Represent a bound super object like `super(PivotClass, owner)` -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] pub struct BoundSuperType<'db> { pub pivot_class: ClassBase<'db>, pub owner: SuperOwnerKind<'db>, @@ -9381,7 +9386,7 @@ impl<'db> BoundSuperType<'db> { } } -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] pub struct TypeIsType<'db> { return_type: Type<'db>, /// The ID of the scope to which the place belongs diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 6de3a5fa5e..c69719c99f 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -212,7 +212,7 @@ impl CodeGeneratorKind { /// # Ordering /// Ordering is based on the generic aliases's salsa-assigned id and not on its values. /// The id may change between runs, or when the alias was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct GenericAlias<'db> { pub(crate) origin: ClassLiteral<'db>, @@ -1147,7 +1147,7 @@ pub(crate) struct Field<'db> { /// # Ordering /// Ordering is based on the class's id assigned by salsa and not on the class literal's values. /// The id may change between runs, or when the class literal was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct ClassLiteral<'db> { /// Name of the class at definition @@ -2998,7 +2998,7 @@ pub(super) enum SolidBaseKind { /// Feel free to expand this enum if you ever find yourself using the same class in multiple /// places. /// Note: good candidates are any classes in `[crate::module_resolver::module::KnownModule]` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] #[cfg_attr(test, derive(strum_macros::EnumIter))] pub enum KnownClass { // To figure out where an stdlib symbol is defined, you can go into `crates/ty_vendored` diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 9be755e333..17ab1342f6 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -120,6 +120,8 @@ bitflags! { } } +impl get_size2::GetSize for FunctionDecorators {} + impl FunctionDecorators { pub(super) fn from_decorator_type(db: &dyn Db, decorator_type: Type) -> Self { match decorator_type { @@ -184,7 +186,7 @@ impl Default for DataclassTransformerParams { /// Ordering is based on the function's id assigned by salsa and not on the function literal's /// values. The id may change between runs, or when the function literal was garbage collected and /// recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct OverloadLiteral<'db> { /// Name of the function at definition. @@ -406,7 +408,7 @@ impl<'db> OverloadLiteral<'db> { /// Ordering is based on the function's id assigned by salsa and not on the function literal's /// values. The id may change between runs, or when the function literal was garbage collected and /// recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct FunctionLiteral<'db> { pub(crate) last_definition: OverloadLiteral<'db>, @@ -433,6 +435,9 @@ pub struct FunctionLiteral<'db> { inherited_generic_context: Option>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for FunctionLiteral<'_> {} + fn walk_function_literal<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( db: &'db dyn Db, function: FunctionLiteral<'db>, @@ -609,7 +614,7 @@ impl<'db> FunctionLiteral<'db> { /// Represents a function type, which might be a non-generic function, or a specialization of a /// generic function. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct FunctionType<'db> { pub(crate) literal: FunctionLiteral<'db>, @@ -1057,7 +1062,15 @@ fn last_definition_signature_cycle_initial<'db>( /// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might /// have special behavior. #[derive( - Debug, Copy, Clone, PartialEq, Eq, Hash, strum_macros::EnumString, strum_macros::IntoStaticStr, + Debug, + Copy, + Clone, + PartialEq, + Eq, + Hash, + strum_macros::EnumString, + strum_macros::IntoStaticStr, + get_size2::GetSize, )] #[strum(serialize_all = "snake_case")] #[cfg_attr(test, derive(strum_macros::EnumIter))] diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 07dedb7041..d4610a4cb2 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -89,7 +89,7 @@ pub(crate) fn bind_typevar<'db>( /// # Ordering /// Ordering is based on the context's salsa-assigned id and not on its values. /// The id may change between runs, or when the context was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=GenericContext::heap_size)] #[derive(PartialOrd, Ord)] pub struct GenericContext<'db> { #[returns(ref)] @@ -363,6 +363,10 @@ impl<'db> GenericContext<'db> { .collect(); Self::new(db, variables) } + + fn heap_size((variables,): &(FxOrderSet>,)) -> usize { + ruff_memory_usage::order_set_heap_size(variables) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -390,7 +394,7 @@ impl std::fmt::Display for LegacyGenericBase { /// /// TODO: Handle nested specializations better, with actual parent links to the specialization of /// the lexically containing context. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] pub struct Specialization<'db> { pub(crate) generic_context: GenericContext<'db>, #[returns(deref)] @@ -401,6 +405,9 @@ pub struct Specialization<'db> { tuple_inner: Option>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for Specialization<'_> {} + pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( db: &'db dyn Db, specialization: Specialization<'db>, @@ -655,7 +662,7 @@ impl<'db> Specialization<'db> { /// /// You will usually use [`Specialization`] instead of this type. This type is used when we need to /// substitute types for type variables before we have fully constructed a [`Specialization`]. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct PartialSpecialization<'a, 'db> { generic_context: GenericContext<'db>, types: Cow<'a, [Type<'db>]>, diff --git a/crates/ty_python_semantic/src/types/protocol_class.rs b/crates/ty_python_semantic/src/types/protocol_class.rs index 15520d333f..bdcba1c5d6 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -70,7 +70,7 @@ impl<'db> Deref for ProtocolClassLiteral<'db> { /// # Ordering /// Ordering is based on the protocol interface member's salsa-assigned id and not on its members. /// The id may change between runs, or when the protocol instance members was garbage collected and recreated. -#[salsa::interned(debug)] +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub(super) struct ProtocolInterface<'db> { #[returns(ref)] @@ -241,7 +241,7 @@ impl<'db> ProtocolInterface<'db> { } } -#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update, get_size2::GetSize)] pub(super) struct ProtocolMemberData<'db> { kind: ProtocolMemberKind<'db>, qualifiers: TypeQualifiers, @@ -319,7 +319,7 @@ impl<'db> ProtocolMemberData<'db> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash, get_size2::GetSize)] enum ProtocolMemberKind<'db> { Method(CallableType<'db>), Property(PropertyInstanceType<'db>), diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index 121c3a9c55..ba94671fe0 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -125,7 +125,7 @@ impl TupleLength { /// # Ordering /// Ordering is based on the tuple's salsa-assigned id and not on its elements. /// The id may change between runs, or when the tuple was garbage collected and recreated. -#[salsa::interned(debug, constructor=new_internal)] +#[salsa::interned(debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct TupleType<'db> { #[returns(ref)] @@ -223,7 +223,7 @@ impl<'db> TupleType<'db> { // N.B. If this method is not Salsa-tracked, we take 10 minutes to check // `static-frame` as part of a mypy_primer run! This is because it's called // from `NominalInstanceType::class()`, which is a very hot method. - #[salsa::tracked] + #[salsa::tracked(heap_size=ruff_memory_usage::heap_size)] pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> { let tuple_class = KnownClass::Tuple .try_to_class_literal(db) @@ -302,7 +302,7 @@ pub(crate) type TupleSpec<'db> = Tuple>; /// /// Our tuple representation can hold instances of any Rust type. For tuples containing Python /// types, use [`TupleSpec`], which defines some additional type-specific methods. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct FixedLengthTuple(Vec); impl FixedLengthTuple { @@ -516,7 +516,7 @@ impl<'db> PySlice<'db> for FixedLengthTuple> { /// /// Our tuple representation can hold instances of any Rust type. For tuples containing Python /// types, use [`TupleSpec`], which defines some additional type-specific methods. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct VariableLengthTuple { pub(crate) prefix: Vec, pub(crate) variable: T, @@ -956,7 +956,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple> { /// /// Our tuple representation can hold instances of any Rust type. For tuples containing Python /// types, use [`TupleSpec`], which defines some additional type-specific methods. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub enum Tuple { Fixed(FixedLengthTuple), Variable(VariableLengthTuple), diff --git a/crates/ty_python_semantic/src/unpack.rs b/crates/ty_python_semantic/src/unpack.rs index 917ff71b4e..cb07f2570a 100644 --- a/crates/ty_python_semantic/src/unpack.rs +++ b/crates/ty_python_semantic/src/unpack.rs @@ -26,7 +26,7 @@ use crate::semantic_index::scope::{FileScopeId, ScopeId}; /// * a return type of a cross-module query /// * a field of a type that is a return type of a cross-module query /// * an argument of a cross-module query -#[salsa::tracked(debug)] +#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)] pub(crate) struct Unpack<'db> { pub(crate) file: File, @@ -46,6 +46,9 @@ pub(crate) struct Unpack<'db> { pub(crate) value: UnpackValue<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for Unpack<'_> {} + impl<'db> Unpack<'db> { pub(crate) fn target<'ast>( self, @@ -67,7 +70,7 @@ impl<'db> Unpack<'db> { } /// The expression that is being unpacked. -#[derive(Clone, Copy, Debug, Hash, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)] pub(crate) struct UnpackValue<'db> { /// The kind of unpack expression kind: UnpackKind, @@ -99,7 +102,7 @@ impl<'db> UnpackValue<'db> { } } -#[derive(Clone, Copy, Debug, Hash, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)] pub(crate) enum EvaluationMode { Sync, Async, @@ -119,7 +122,7 @@ impl EvaluationMode { } } -#[derive(Clone, Copy, Debug, Hash, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)] pub(crate) enum UnpackKind { /// An iterable expression like the one in a `for` loop or a comprehension. Iterable { mode: EvaluationMode }, @@ -130,7 +133,7 @@ pub(crate) enum UnpackKind { } /// The position of the target element in an unpacking. -#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update, get_size2::GetSize)] pub(crate) enum UnpackPosition { /// The target element is in the first position of the unpacking. First,