From 6f7b1c9bb3eb7da3c0235cf11ed956d8cbf2f328 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 26 Jun 2025 17:27:51 -0400 Subject: [PATCH] [ty] Add environment variable to dump Salsa memory usage stats (#18928) ## Summary Setting `TY_MEMORY_REPORT=full` will generate and print a memory usage report to the CLI after a `ty check` run: ``` =======SALSA STRUCTS======= `Definition` metadata=7.24MB fields=17.38MB count=181062 `Expression` metadata=4.45MB fields=5.94MB count=92804 `member_lookup_with_policy_::interned_arguments` metadata=1.97MB fields=2.25MB count=35176 ... =======SALSA QUERIES======= `File -> ty_python_semantic::semantic_index::SemanticIndex` metadata=11.46MB fields=88.86MB count=1638 `Definition -> ty_python_semantic::types::infer::TypeInference` metadata=24.52MB fields=86.68MB count=146018 `File -> ruff_db::parsed::ParsedModule` metadata=0.12MB fields=69.06MB count=1642 ... =======SALSA SUMMARY======= TOTAL MEMORY USAGE: 577.61MB struct metadata = 29.00MB struct fields = 35.68MB memo metadata = 103.87MB memo fields = 409.06MB ``` Eventually, we should integrate these numbers into CI in some form. The one limitation currently is that heap allocations in salsa structs (e.g. interned values) are not tracked, but memoized values should have full coverage. We may also want a peak memory usage counter (that accounts for non-salsa memory), but that is relatively simple to profile manually (e.g. `time -v ty check`) and would require a compile-time option to avoid runtime overhead. --- Cargo.lock | 150 +++++++++++++++- Cargo.toml | 8 +- crates/ruff_db/Cargo.toml | 5 +- crates/ruff_db/src/diagnostic/mod.rs | 24 +-- crates/ruff_db/src/files.rs | 3 + crates/ruff_db/src/parsed.rs | 20 ++- crates/ruff_db/src/source.rs | 24 ++- crates/ruff_db/src/system/path.rs | 6 + crates/ruff_db/src/vendored/path.rs | 6 + crates/ruff_index/Cargo.toml | 1 + crates/ruff_index/src/vec.rs | 2 +- crates/ruff_python_ast/Cargo.toml | 2 + crates/ruff_python_ast/generate.py | 5 + crates/ruff_python_ast/src/generated.rs | 76 ++++++++ crates/ruff_python_ast/src/int.rs | 2 + crates/ruff_python_ast/src/name.rs | 1 + crates/ruff_python_ast/src/node_index.rs | 3 +- crates/ruff_python_ast/src/nodes.rs | 68 +++++++ crates/ruff_python_ast/src/python_version.rs | 1 + crates/ruff_python_parser/Cargo.toml | 5 +- crates/ruff_python_parser/src/error.rs | 20 +-- crates/ruff_python_parser/src/lib.rs | 4 +- .../ruff_python_parser/src/semantic_errors.rs | 14 +- crates/ruff_python_parser/src/token.rs | 6 +- crates/ruff_source_file/Cargo.toml | 2 + crates/ruff_source_file/src/lib.rs | 2 + crates/ruff_source_file/src/line_index.rs | 3 + crates/ruff_text_size/Cargo.toml | 2 + crates/ruff_text_size/src/range.rs | 1 + crates/ruff_text_size/src/size.rs | 1 + crates/ty/src/lib.rs | 7 + crates/ty_project/Cargo.toml | 1 + crates/ty_project/src/db.rs | 166 ++++++++++++++++++ crates/ty_project/src/lib.rs | 4 +- crates/ty_project/src/metadata/settings.rs | 8 +- crates/ty_python_semantic/Cargo.toml | 1 + crates/ty_python_semantic/src/ast_node_ref.rs | 2 + crates/ty_python_semantic/src/dunder_all.rs | 2 +- crates/ty_python_semantic/src/lint.rs | 8 +- crates/ty_python_semantic/src/list.rs | 8 +- crates/ty_python_semantic/src/module_name.rs | 2 +- .../src/module_resolver/module.rs | 8 +- .../src/module_resolver/path.rs | 4 +- .../src/module_resolver/resolver.rs | 6 +- crates/ty_python_semantic/src/node_key.rs | 2 +- crates/ty_python_semantic/src/place.rs | 10 +- .../ty_python_semantic/src/semantic_index.rs | 48 +++-- .../src/semantic_index/ast_ids.rs | 7 +- .../src/semantic_index/builder.rs | 4 +- .../src/semantic_index/definition.rs | 23 ++- .../src/semantic_index/expression.rs | 3 + .../semantic_index/narrowing_constraints.rs | 4 +- .../src/semantic_index/place.rs | 23 ++- .../src/semantic_index/predicate.rs | 12 +- .../src/semantic_index/re_exports.rs | 2 +- .../reachability_constraints.rs | 6 +- .../src/semantic_index/use_def.rs | 7 +- .../src/semantic_index/use_def/place_state.rs | 14 +- crates/ty_python_semantic/src/suppression.rs | 20 +-- crates/ty_python_semantic/src/types.rs | 87 +++++++-- crates/ty_python_semantic/src/types/class.rs | 35 +++- .../src/types/class_base.rs | 2 +- .../src/types/diagnostic.rs | 2 +- .../ty_python_semantic/src/types/function.rs | 12 +- .../ty_python_semantic/src/types/generics.rs | 3 + crates/ty_python_semantic/src/types/infer.rs | 14 +- .../ty_python_semantic/src/types/instance.rs | 14 +- crates/ty_python_semantic/src/types/mro.rs | 8 +- crates/ty_python_semantic/src/types/narrow.rs | 6 +- .../types/property_tests/type_generation.rs | 2 +- .../src/types/protocol_class.rs | 8 +- .../src/types/signatures.rs | 12 +- .../src/types/special_form.rs | 1 + .../src/types/subclass_of.rs | 4 +- crates/ty_python_semantic/src/types/tuple.rs | 3 + .../ty_python_semantic/src/types/unpacker.rs | 2 +- .../ty_python_semantic/src/util/get_size.rs | 15 ++ crates/ty_python_semantic/src/util/mod.rs | 1 + fuzz/Cargo.toml | 2 +- 79 files changed, 905 insertions(+), 207 deletions(-) create mode 100644 crates/ty_python_semantic/src/util/get_size.rs diff --git a/Cargo.lock b/Cargo.lock index b0e9121a72..ded61d1378 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,6 +169,36 @@ dependencies = [ "tempfile", ] +[[package]] +name = "attribute-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0053e96dd3bec5b4879c23a138d6ef26f2cb936c9cdc96274ac2b9ed44b5bb54" +dependencies = [ + "attribute-derive-macro", + "derive-where", + "manyhow", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463b53ad0fd5b460af4b1915fe045ff4d946d025fb6c4dc3337752eaa980f71b" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -519,6 +549,12 @@ dependencies = [ "regex-lite", ] +[[package]] +name = "collection_literals" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" + [[package]] name = "colorchoice" version = "1.0.3" @@ -808,6 +844,17 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "derive-where" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "diff" version = "0.1.13" @@ -1067,6 +1114,29 @@ dependencies = [ "version_check", ] +[[package]] +name = "get-size-derive2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea12180b12b82e9b7c01dfe91138208604961bb2bd7e93058d6786e5d770b104" +dependencies = [ + "attribute-derive", + "quote", + "syn", +] + +[[package]] +name = "get-size2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a37e4d438f7550dbd4938f1bcde41538653616513678d647665a7332ea3c030" +dependencies = [ + "compact_str", + "get-size-derive2", + "hashbrown 0.15.4", + "smallvec", +] + [[package]] name = "getopts" version = "0.2.21" @@ -1455,6 +1525,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + [[package]] name = "intrusive-collections" version = "0.9.7" @@ -1716,9 +1792,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1755,6 +1831,29 @@ dependencies = [ "url", ] +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "markdown" version = "1.0.0" @@ -2315,6 +2414,17 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -2391,6 +2501,28 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quote-use" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9619db1197b497a36178cfc736dc96b271fe918875fbf1344c436a7e93d0321e" +dependencies = [ + "quote", + "quote-use-macros", +] + +[[package]] +name = "quote-use-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ebfb7faafadc06a7ab141a6f67bcfb24cb8beb158c6fe933f2f035afa99f35" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "r-efi" version = "5.2.0" @@ -2680,6 +2812,7 @@ dependencies = [ "dunce", "etcetera", "filetime", + "get-size2", "glob", "ignore", "insta", @@ -2795,6 +2928,7 @@ dependencies = [ name = "ruff_index" version = "0.0.0" dependencies = [ + "get-size2", "ruff_macros", "salsa", "static_assertions", @@ -2907,6 +3041,7 @@ dependencies = [ "aho-corasick", "bitflags 2.9.1", "compact_str", + "get-size2", "is-macro", "itertools 0.14.0", "memchr", @@ -3006,6 +3141,7 @@ dependencies = [ "bitflags 2.9.1", "bstr", "compact_str", + "get-size2", "insta", "memchr", "ruff_annotate_snippets", @@ -3112,6 +3248,7 @@ dependencies = [ name = "ruff_source_file" version = "0.0.0" dependencies = [ + "get-size2", "memchr", "ruff_text_size", "serde", @@ -3121,6 +3258,7 @@ dependencies = [ name = "ruff_text_size" version = "0.0.0" dependencies = [ + "get-size2", "schemars", "serde", "serde_test", @@ -3249,7 +3387,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" version = "0.22.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=09627e450566f894956710a3fd923dc80462ae6d#09627e450566f894956710a3fd923dc80462ae6d" +source = "git+https://github.com/salsa-rs/salsa?rev=0666e2018bc35376b1ac4f98906f2d04d11e5fe4#0666e2018bc35376b1ac4f98906f2d04d11e5fe4" dependencies = [ "boxcar", "compact_str", @@ -3273,12 +3411,12 @@ dependencies = [ [[package]] name = "salsa-macro-rules" version = "0.22.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=09627e450566f894956710a3fd923dc80462ae6d#09627e450566f894956710a3fd923dc80462ae6d" +source = "git+https://github.com/salsa-rs/salsa?rev=0666e2018bc35376b1ac4f98906f2d04d11e5fe4#0666e2018bc35376b1ac4f98906f2d04d11e5fe4" [[package]] name = "salsa-macros" version = "0.22.0" -source = "git+https://github.com/salsa-rs/salsa.git?rev=09627e450566f894956710a3fd923dc80462ae6d#09627e450566f894956710a3fd923dc80462ae6d" +source = "git+https://github.com/salsa-rs/salsa?rev=0666e2018bc35376b1ac4f98906f2d04d11e5fe4#0666e2018bc35376b1ac4f98906f2d04d11e5fe4" dependencies = [ "proc-macro2", "quote", @@ -3991,6 +4129,7 @@ dependencies = [ "camino", "colored 3.0.0", "crossbeam", + "get-size2", "globset", "insta", "notify", @@ -4030,6 +4169,7 @@ dependencies = [ "countme", "dir-test", "drop_bomb", + "get-size2", "glob", "hashbrown 0.15.4", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index b9de8e5209..ccc6bb3728 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,12 @@ etcetera = { version = "0.10.0" } fern = { version = "0.7.0" } filetime = { version = "0.2.23" } getrandom = { version = "0.3.1" } +get-size2 = { version = "0.5.0", features = [ + "derive", + "smallvec", + "hashbrown", + "compact-str" +] } glob = { version = "0.3.1" } globset = { version = "0.4.14" } globwalk = { version = "0.9.1" } @@ -131,7 +137,7 @@ regex-automata = { version = "0.4.9" } rustc-hash = { version = "2.0.0" } rustc-stable-hash = { version = "0.1.2" } # When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml` -salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "09627e450566f894956710a3fd923dc80462ae6d" } +salsa = { git = "https://github.com/salsa-rs/salsa", rev = "0666e2018bc35376b1ac4f98906f2d04d11e5fe4" } schemars = { version = "0.8.16" } seahash = { version = "4.1.0" } serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/ruff_db/Cargo.toml b/crates/ruff_db/Cargo.toml index 270b35d448..fa73022e93 100644 --- a/crates/ruff_db/Cargo.toml +++ b/crates/ruff_db/Cargo.toml @@ -14,10 +14,10 @@ license = { workspace = true } ruff_annotate_snippets = { workspace = true } ruff_cache = { workspace = true, optional = true } ruff_notebook = { workspace = true } -ruff_python_ast = { workspace = true } +ruff_python_ast = { workspace = true, features = ["get-size"] } ruff_python_parser = { workspace = true } ruff_python_trivia = { workspace = true } -ruff_source_file = { workspace = true } +ruff_source_file = { workspace = true, features = ["get-size"] } ruff_text_size = { workspace = true } anstyle = { workspace = true } @@ -27,6 +27,7 @@ countme = { workspace = true } dashmap = { workspace = true } dunce = { workspace = true } filetime = { workspace = true } +get-size2 = { workspace = true } glob = { workspace = true } ignore = { workspace = true, optional = true } matchit = { workspace = true } diff --git a/crates/ruff_db/src/diagnostic/mod.rs b/crates/ruff_db/src/diagnostic/mod.rs index bb4633a9b2..f4d5d4fb9c 100644 --- a/crates/ruff_db/src/diagnostic/mod.rs +++ b/crates/ruff_db/src/diagnostic/mod.rs @@ -19,7 +19,7 @@ mod stylesheet; /// characteristics in the inputs given to the tool. Typically, but not always, /// a characteristic is a deficiency. An example of a characteristic that is /// _not_ a deficiency is the `reveal_type` diagnostic for our type checker. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] pub struct Diagnostic { /// The actual diagnostic. /// @@ -270,7 +270,7 @@ impl Diagnostic { } } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] struct DiagnosticInner { id: DiagnosticId, severity: Severity, @@ -342,7 +342,7 @@ impl Eq for RenderingSortKey<'_> {} /// Currently, the order in which sub-diagnostics are rendered relative to one /// another (for a single parent diagnostic) is the order in which they were /// attached to the diagnostic. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] pub struct SubDiagnostic { /// Like with `Diagnostic`, we box the `SubDiagnostic` to make it /// pointer-sized. @@ -443,7 +443,7 @@ impl SubDiagnostic { } } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] struct SubDiagnosticInner { severity: Severity, message: DiagnosticMessage, @@ -471,7 +471,7 @@ struct SubDiagnosticInner { /// /// Messages attached to annotations should also be as brief and specific as /// possible. Long messages could negative impact the quality of rendering. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] pub struct Annotation { /// The span of this annotation, corresponding to some subsequence of the /// user's input that we want to highlight. @@ -591,7 +591,7 @@ impl Annotation { /// /// These tags are used to provide additional information about the annotation. /// and are passed through to the language server protocol. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, get_size2::GetSize)] pub enum DiagnosticTag { /// Unused or unnecessary code. Used for unused parameters, unreachable code, etc. Unnecessary, @@ -605,7 +605,7 @@ pub enum DiagnosticTag { /// be in kebab case, e.g. `no-foo` (all lower case). /// /// Rules use kebab case, e.g. `no-foo`. -#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, get_size2::GetSize)] pub struct LintName(&'static str); impl LintName { @@ -645,7 +645,7 @@ impl PartialEq<&str> for LintName { } /// Uniquely identifies the kind of a diagnostic. -#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, get_size2::GetSize)] pub enum DiagnosticId { Panic, @@ -800,7 +800,7 @@ impl std::fmt::Display for DiagnosticId { /// /// This enum presents a unified interface to these two types for the sake of creating [`Span`]s and /// emitting diagnostics from both ty and ruff. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub enum UnifiedFile { Ty(File), Ruff(SourceFile), @@ -852,7 +852,7 @@ impl DiagnosticSource { /// It consists of a `File` and an optional range into that file. When the /// range isn't present, it semantically implies that the diagnostic refers to /// the entire file. For example, when the file should be executable but isn't. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub struct Span { file: UnifiedFile, range: Option, @@ -924,7 +924,7 @@ impl From for Span { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, get_size2::GetSize)] pub enum Severity { Info, Warning, @@ -1087,7 +1087,7 @@ impl std::fmt::Display for ConciseMessage<'_> { /// In most cases, callers shouldn't need to use this. Instead, there is /// a blanket trait implementation for `IntoDiagnosticMessage` for /// anything that implements `std::fmt::Display`. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)] pub struct DiagnosticMessage(Box); impl DiagnosticMessage { diff --git a/crates/ruff_db/src/files.rs b/crates/ruff_db/src/files.rs index cae4b51853..34c86626eb 100644 --- a/crates/ruff_db/src/files.rs +++ b/crates/ruff_db/src/files.rs @@ -308,6 +308,9 @@ pub struct File { count: Count, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for File {} + impl File { /// Reads the content of the file into a [`String`]. /// diff --git a/crates/ruff_db/src/parsed.rs b/crates/ruff_db/src/parsed.rs index f28d280f7e..dd98114e3a 100644 --- a/crates/ruff_db/src/parsed.rs +++ b/crates/ruff_db/src/parsed.rs @@ -2,6 +2,7 @@ use std::fmt::Formatter; use std::sync::Arc; use arc_swap::ArcSwapOption; +use get_size2::GetSize; use ruff_python_ast::{AnyRootNodeRef, ModModule, NodeIndex}; use ruff_python_parser::{ParseOptions, Parsed, parse_unchecked}; @@ -20,7 +21,7 @@ use crate::source::source_text; /// reflected in the changed AST offsets. /// The other reason is that Ruff's AST doesn't implement `Eq` which Salsa requires /// for determining if a query result is unchanged. -#[salsa::tracked(returns(ref), no_eq)] +#[salsa::tracked(returns(ref), no_eq, heap_size=get_size2::GetSize::get_heap_size)] pub fn parsed_module(db: &dyn Db, file: File) -> ParsedModule { let _span = tracing::trace_span!("parsed_module", ?file).entered(); @@ -44,9 +45,10 @@ pub fn parsed_module_impl(db: &dyn Db, file: File) -> Parsed { /// /// This type manages instances of the module AST. A particular instance of the AST /// is represented with the [`ParsedModuleRef`] type. -#[derive(Clone)] +#[derive(Clone, get_size2::GetSize)] pub struct ParsedModule { file: File, + #[get_size(size_fn = arc_swap_size)] inner: Arc>, } @@ -142,6 +144,18 @@ impl std::ops::Deref for ParsedModuleRef { } } +/// Returns the heap-size of the currently stored `T` in the `ArcSwap`. +fn arc_swap_size(arc_swap: &Arc>) -> usize +where + T: GetSize, +{ + if let Some(value) = &*arc_swap.load() { + T::get_heap_size(value) + } else { + 0 + } +} + mod indexed { use std::sync::Arc; @@ -150,7 +164,7 @@ mod indexed { use ruff_python_parser::Parsed; /// A wrapper around the AST that allows access to AST nodes by index. - #[derive(Debug)] + #[derive(Debug, get_size2::GetSize)] pub struct IndexedModule { index: Box<[AnyRootNodeRef<'static>]>, pub parsed: Parsed, diff --git a/crates/ruff_db/src/source.rs b/crates/ruff_db/src/source.rs index aac59a6060..824c17cae4 100644 --- a/crates/ruff_db/src/source.rs +++ b/crates/ruff_db/src/source.rs @@ -11,7 +11,7 @@ use crate::Db; use crate::files::{File, FilePath}; /// Reads the source text of a python text file (must be valid UTF8) or notebook. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub fn source_text(db: &dyn Db, file: File) -> SourceText { let path = file.path(db); let _span = tracing::trace_span!("source_text", file = %path).entered(); @@ -65,7 +65,7 @@ fn is_notebook(path: &FilePath) -> bool { /// The file containing the source text can either be a text file or a notebook. /// /// Cheap cloneable in `O(1)`. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, get_size2::GetSize)] pub struct SourceText { inner: Arc, } @@ -123,8 +123,9 @@ impl std::fmt::Debug for SourceText { } } -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, get_size2::GetSize)] struct SourceTextInner { + #[get_size(ignore)] count: Count, kind: SourceTextKind, read_error: Option, @@ -136,6 +137,19 @@ enum SourceTextKind { Notebook(Box), } +impl get_size2::GetSize for SourceTextKind { + fn get_heap_size(&self) -> usize { + match self { + SourceTextKind::Text(text) => text.get_heap_size(), + // TODO: The `get-size` derive does not support ignoring enum variants. + // + // Jupyter notebooks are not very relevant for memory profiling, and contain + // arbitrary JSON values that do not implement the `GetSize` trait. + SourceTextKind::Notebook(_) => 0, + } + } +} + impl From for SourceTextKind { fn from(value: String) -> Self { SourceTextKind::Text(value) @@ -148,7 +162,7 @@ impl From for SourceTextKind { } } -#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)] +#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone, get_size2::GetSize)] pub enum SourceTextError { #[error("Failed to read notebook: {0}`")] FailedToReadNotebook(String), @@ -157,7 +171,7 @@ pub enum SourceTextError { } /// Computes the [`LineIndex`] for `file`. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub fn line_index(db: &dyn Db, file: File) -> LineIndex { let _span = tracing::trace_span!("line_index", ?file).entered(); diff --git a/crates/ruff_db/src/system/path.rs b/crates/ruff_db/src/system/path.rs index 07ea7d0b71..fbba17ebce 100644 --- a/crates/ruff_db/src/system/path.rs +++ b/crates/ruff_db/src/system/path.rs @@ -503,6 +503,12 @@ impl ToOwned for SystemPath { #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct SystemPathBuf(#[cfg_attr(feature = "schemars", schemars(with = "String"))] Utf8PathBuf); +impl get_size2::GetSize for SystemPathBuf { + fn get_heap_size(&self) -> usize { + self.0.capacity() + } +} + impl SystemPathBuf { pub fn new() -> Self { Self(Utf8PathBuf::new()) diff --git a/crates/ruff_db/src/vendored/path.rs b/crates/ruff_db/src/vendored/path.rs index a8cb07a672..f32ce08239 100644 --- a/crates/ruff_db/src/vendored/path.rs +++ b/crates/ruff_db/src/vendored/path.rs @@ -87,6 +87,12 @@ impl ToOwned for VendoredPath { #[derive(Debug, Eq, PartialEq, Clone, Hash)] pub struct VendoredPathBuf(Utf8PathBuf); +impl get_size2::GetSize for VendoredPathBuf { + fn get_heap_size(&self) -> usize { + self.0.capacity() + } +} + impl Default for VendoredPathBuf { fn default() -> Self { Self::new() diff --git a/crates/ruff_index/Cargo.toml b/crates/ruff_index/Cargo.toml index a96fa738f6..df11c268cf 100644 --- a/crates/ruff_index/Cargo.toml +++ b/crates/ruff_index/Cargo.toml @@ -14,6 +14,7 @@ license = { workspace = true } doctest = false [dependencies] +get-size2 = { workspace = true } ruff_macros = { workspace = true } salsa = { workspace = true, optional = true } diff --git a/crates/ruff_index/src/vec.rs b/crates/ruff_index/src/vec.rs index fcbd878ae7..6c96e763ae 100644 --- a/crates/ruff_index/src/vec.rs +++ b/crates/ruff_index/src/vec.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut, RangeBounds}; /// An owned sequence of `T` indexed by `I` -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash, get_size2::GetSize)] #[repr(transparent)] pub struct IndexVec { pub raw: Vec, diff --git a/crates/ruff_python_ast/Cargo.toml b/crates/ruff_python_ast/Cargo.toml index 305d85097b..1b049102f6 100644 --- a/crates/ruff_python_ast/Cargo.toml +++ b/crates/ruff_python_ast/Cargo.toml @@ -22,6 +22,7 @@ ruff_text_size = { workspace = true } aho-corasick = { workspace = true } bitflags = { workspace = true } compact_str = { workspace = true } +get-size2 = { workspace = true, optional = true } is-macro = { workspace = true } itertools = { workspace = true } memchr = { workspace = true } @@ -40,6 +41,7 @@ serde = [ "dep:ruff_cache", "compact_str/serde", ] +get-size = ["dep:get-size2"] [lints] workspace = true diff --git a/crates/ruff_python_ast/generate.py b/crates/ruff_python_ast/generate.py index fd62df619e..2b8687b233 100644 --- a/crates/ruff_python_ast/generate.py +++ b/crates/ruff_python_ast/generate.py @@ -316,6 +316,7 @@ def write_owned_enum(out: list[str], ast: Ast) -> None: if group.doc is not None: write_rustdoc(out, group.doc) out.append("#[derive(Clone, Debug, PartialEq)]") + out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]') out.append(f"pub enum {group.owned_enum_ty} {{") for node in group.nodes: out.append(f"{node.variant}({node.ty}),") @@ -515,6 +516,7 @@ def write_ref_enum(out: list[str], ast: Ast) -> None: if group.doc is not None: write_rustdoc(out, group.doc) out.append("""#[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)]""") + out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]') out.append(f"""pub enum {group.ref_enum_ty}<'a> {{""") for node in group.nodes: if group.add_suffix_to_is_methods: @@ -604,6 +606,7 @@ def write_anynoderef(out: list[str], ast: Ast) -> None: out.append(""" /// A flattened enumeration of all AST nodes. #[derive(Copy, Clone, Debug, is_macro::Is, PartialEq)] + #[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum AnyNodeRef<'a> { """) for node in ast.all_nodes: @@ -782,6 +785,7 @@ def write_root_anynoderef(out: list[str], ast: Ast) -> None: /// `AnyNodeRef` has top-level `AnyNodeRef::ModModule` and `AnyNodeRef::ModExpression` /// variants. #[derive(Copy, Clone, Debug, PartialEq)] + #[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum AnyRootNodeRef<'a> { """) for group in ast.groups: @@ -963,6 +967,7 @@ def write_node(out: list[str], ast: Ast) -> None: + "".join(f", {derive}" for derive in node.derives) + ")]" ) + out.append('#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]') name = node.name out.append(f"pub struct {name} {{") out.append("pub node_index: crate::AtomicNodeIndex,") diff --git a/crates/ruff_python_ast/src/generated.rs b/crates/ruff_python_ast/src/generated.rs index cedf608921..935595705d 100644 --- a/crates/ruff_python_ast/src/generated.rs +++ b/crates/ruff_python_ast/src/generated.rs @@ -6,6 +6,7 @@ use crate::visitor::source_order::SourceOrderVisitor; /// See also [mod](https://docs.python.org/3/library/ast.html#ast.mod) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Mod { Module(crate::ModModule), Expression(crate::ModExpression), @@ -120,6 +121,7 @@ impl Mod { /// See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Stmt { FunctionDef(crate::StmtFunctionDef), ClassDef(crate::StmtClassDef), @@ -1292,6 +1294,7 @@ impl Stmt { /// See also [expr](https://docs.python.org/3/library/ast.html#ast.expr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Expr { BoolOp(crate::ExprBoolOp), Named(crate::ExprNamed), @@ -2832,6 +2835,7 @@ impl Expr { /// See also [excepthandler](https://docs.python.org/3/library/ast.html#ast.excepthandler) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum ExceptHandler { ExceptHandler(crate::ExceptHandlerExceptHandler), } @@ -2895,6 +2899,7 @@ impl ExceptHandler { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum InterpolatedStringElement { Interpolation(crate::InterpolatedElement), Literal(crate::InterpolatedStringLiteralElement), @@ -3009,6 +3014,7 @@ impl InterpolatedStringElement { /// See also [pattern](https://docs.python.org/3/library/ast.html#ast.pattern) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Pattern { MatchValue(crate::PatternMatchValue), MatchSingleton(crate::PatternMatchSingleton), @@ -3399,6 +3405,7 @@ impl Pattern { /// See also [type_param](https://docs.python.org/3/library/ast.html#ast.type_param) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum TypeParam { TypeVar(crate::TypeParamTypeVar), TypeVarTuple(crate::TypeParamTypeVarTuple), @@ -4838,6 +4845,7 @@ impl TypeParam { /// See also [mod](https://docs.python.org/3/library/ast.html#ast.mod) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum ModRef<'a> { Module(&'a crate::ModModule), Expression(&'a crate::ModExpression), @@ -4884,6 +4892,7 @@ impl crate::HasNodeIndex for ModRef<'_> { /// See also [stmt](https://docs.python.org/3/library/ast.html#ast.stmt) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum StmtRef<'a> { #[is(name = "function_def_stmt")] FunctionDef(&'a crate::StmtFunctionDef), @@ -5185,6 +5194,7 @@ impl crate::HasNodeIndex for StmtRef<'_> { /// See also [expr](https://docs.python.org/3/library/ast.html#ast.expr) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum ExprRef<'a> { #[is(name = "bool_op_expr")] BoolOp(&'a crate::ExprBoolOp), @@ -5574,6 +5584,7 @@ impl crate::HasNodeIndex for ExprRef<'_> { /// See also [excepthandler](https://docs.python.org/3/library/ast.html#ast.excepthandler) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum ExceptHandlerRef<'a> { ExceptHandler(&'a crate::ExceptHandlerExceptHandler), } @@ -5609,6 +5620,7 @@ impl crate::HasNodeIndex for ExceptHandlerRef<'_> { } #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum InterpolatedStringElementRef<'a> { Interpolation(&'a crate::InterpolatedElement), Literal(&'a crate::InterpolatedStringLiteralElement), @@ -5657,6 +5669,7 @@ impl crate::HasNodeIndex for InterpolatedStringElementRef<'_> { /// See also [pattern](https://docs.python.org/3/library/ast.html#ast.pattern) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum PatternRef<'a> { MatchValue(&'a crate::PatternMatchValue), MatchSingleton(&'a crate::PatternMatchSingleton), @@ -5763,6 +5776,7 @@ impl crate::HasNodeIndex for PatternRef<'_> { /// See also [type_param](https://docs.python.org/3/library/ast.html#ast.type_param) #[derive(Clone, Copy, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum TypeParamRef<'a> { TypeVar(&'a crate::TypeParamTypeVar), TypeVarTuple(&'a crate::TypeParamTypeVarTuple), @@ -5819,6 +5833,7 @@ impl crate::HasNodeIndex for TypeParamRef<'_> { /// A flattened enumeration of all AST nodes. #[derive(Copy, Clone, Debug, is_macro::Is, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum AnyNodeRef<'a> { ModModule(&'a crate::ModModule), ModExpression(&'a crate::ModExpression), @@ -7418,6 +7433,7 @@ impl AnyNodeRef<'_> { /// `AnyNodeRef` has top-level `AnyNodeRef::ModModule` and `AnyNodeRef::ModExpression` /// variants. #[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum AnyRootNodeRef<'a> { Mod(&'a Mod), Stmt(&'a Stmt), @@ -8935,6 +8951,7 @@ impl AnyNodeRef<'_> { /// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ModModule { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8943,6 +8960,7 @@ pub struct ModModule { /// See also [Module](https://docs.python.org/3/library/ast.html#ast.Module) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ModExpression { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8954,6 +8972,7 @@ pub struct ModExpression { /// /// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtFunctionDef { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8968,6 +8987,7 @@ pub struct StmtFunctionDef { /// See also [ClassDef](https://docs.python.org/3/library/ast.html#ast.ClassDef) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtClassDef { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8980,6 +9000,7 @@ pub struct StmtClassDef { /// See also [Return](https://docs.python.org/3/library/ast.html#ast.Return) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtReturn { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8988,6 +9009,7 @@ pub struct StmtReturn { /// See also [Delete](https://docs.python.org/3/library/ast.html#ast.Delete) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtDelete { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -8996,6 +9018,7 @@ pub struct StmtDelete { /// See also [TypeAlias](https://docs.python.org/3/library/ast.html#ast.TypeAlias) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtTypeAlias { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9006,6 +9029,7 @@ pub struct StmtTypeAlias { /// See also [Assign](https://docs.python.org/3/library/ast.html#ast.Assign) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtAssign { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9015,6 +9039,7 @@ pub struct StmtAssign { /// See also [AugAssign](https://docs.python.org/3/library/ast.html#ast.AugAssign) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtAugAssign { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9025,6 +9050,7 @@ pub struct StmtAugAssign { /// See also [AnnAssign](https://docs.python.org/3/library/ast.html#ast.AnnAssign) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtAnnAssign { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9039,6 +9065,7 @@ pub struct StmtAnnAssign { /// /// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtFor { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9052,6 +9079,7 @@ pub struct StmtFor { /// See also [While](https://docs.python.org/3/library/ast.html#ast.While) /// and [AsyncWhile](https://docs.python.org/3/library/ast.html#ast.AsyncWhile). #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtWhile { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9062,6 +9090,7 @@ pub struct StmtWhile { /// See also [If](https://docs.python.org/3/library/ast.html#ast.If) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtIf { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9075,6 +9104,7 @@ pub struct StmtIf { /// /// This type differs from the original Python AST, as it collapses the synchronous and asynchronous variants into a single type. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtWith { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9085,6 +9115,7 @@ pub struct StmtWith { /// See also [Match](https://docs.python.org/3/library/ast.html#ast.Match) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtMatch { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9094,6 +9125,7 @@ pub struct StmtMatch { /// See also [Raise](https://docs.python.org/3/library/ast.html#ast.Raise) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtRaise { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9104,6 +9136,7 @@ pub struct StmtRaise { /// See also [Try](https://docs.python.org/3/library/ast.html#ast.Try) /// and [TryStar](https://docs.python.org/3/library/ast.html#ast.TryStar) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtTry { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9116,6 +9149,7 @@ pub struct StmtTry { /// See also [Assert](https://docs.python.org/3/library/ast.html#ast.Assert) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtAssert { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9125,6 +9159,7 @@ pub struct StmtAssert { /// See also [Import](https://docs.python.org/3/library/ast.html#ast.Import) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtImport { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9133,6 +9168,7 @@ pub struct StmtImport { /// See also [ImportFrom](https://docs.python.org/3/library/ast.html#ast.ImportFrom) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtImportFrom { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9143,6 +9179,7 @@ pub struct StmtImportFrom { /// See also [Global](https://docs.python.org/3/library/ast.html#ast.Global) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtGlobal { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9151,6 +9188,7 @@ pub struct StmtGlobal { /// See also [Nonlocal](https://docs.python.org/3/library/ast.html#ast.Nonlocal) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtNonlocal { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9159,6 +9197,7 @@ pub struct StmtNonlocal { /// See also [Expr](https://docs.python.org/3/library/ast.html#ast.Expr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtExpr { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9167,6 +9206,7 @@ pub struct StmtExpr { /// See also [Pass](https://docs.python.org/3/library/ast.html#ast.Pass) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtPass { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9174,6 +9214,7 @@ pub struct StmtPass { /// See also [Break](https://docs.python.org/3/library/ast.html#ast.Break) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtBreak { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9181,6 +9222,7 @@ pub struct StmtBreak { /// See also [Continue](https://docs.python.org/3/library/ast.html#ast.Continue) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtContinue { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9241,6 +9283,7 @@ pub struct StmtContinue { /// [Escape kind]: crate::IpyEscapeKind /// #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StmtIpyEscapeCommand { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9250,6 +9293,7 @@ pub struct StmtIpyEscapeCommand { /// See also [BoolOp](https://docs.python.org/3/library/ast.html#ast.BoolOp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprBoolOp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9259,6 +9303,7 @@ pub struct ExprBoolOp { /// See also [NamedExpr](https://docs.python.org/3/library/ast.html#ast.NamedExpr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprNamed { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9268,6 +9313,7 @@ pub struct ExprNamed { /// See also [BinOp](https://docs.python.org/3/library/ast.html#ast.BinOp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprBinOp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9278,6 +9324,7 @@ pub struct ExprBinOp { /// See also [UnaryOp](https://docs.python.org/3/library/ast.html#ast.UnaryOp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprUnaryOp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9287,6 +9334,7 @@ pub struct ExprUnaryOp { /// See also [Lambda](https://docs.python.org/3/library/ast.html#ast.Lambda) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprLambda { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9296,6 +9344,7 @@ pub struct ExprLambda { /// See also [IfExp](https://docs.python.org/3/library/ast.html#ast.IfExp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprIf { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9306,6 +9355,7 @@ pub struct ExprIf { /// See also [Dict](https://docs.python.org/3/library/ast.html#ast.Dict) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprDict { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9314,6 +9364,7 @@ pub struct ExprDict { /// See also [Set](https://docs.python.org/3/library/ast.html#ast.Set) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprSet { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9322,6 +9373,7 @@ pub struct ExprSet { /// See also [ListComp](https://docs.python.org/3/library/ast.html#ast.ListComp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprListComp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9331,6 +9383,7 @@ pub struct ExprListComp { /// See also [SetComp](https://docs.python.org/3/library/ast.html#ast.SetComp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprSetComp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9340,6 +9393,7 @@ pub struct ExprSetComp { /// See also [DictComp](https://docs.python.org/3/library/ast.html#ast.DictComp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprDictComp { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9350,6 +9404,7 @@ pub struct ExprDictComp { /// See also [GeneratorExp](https://docs.python.org/3/library/ast.html#ast.GeneratorExp) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprGenerator { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9360,6 +9415,7 @@ pub struct ExprGenerator { /// See also [Await](https://docs.python.org/3/library/ast.html#ast.Await) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprAwait { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9368,6 +9424,7 @@ pub struct ExprAwait { /// See also [Yield](https://docs.python.org/3/library/ast.html#ast.Yield) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprYield { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9376,6 +9433,7 @@ pub struct ExprYield { /// See also [YieldFrom](https://docs.python.org/3/library/ast.html#ast.YieldFrom) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprYieldFrom { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9384,6 +9442,7 @@ pub struct ExprYieldFrom { /// See also [Compare](https://docs.python.org/3/library/ast.html#ast.Compare) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprCompare { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9394,6 +9453,7 @@ pub struct ExprCompare { /// See also [Call](https://docs.python.org/3/library/ast.html#ast.Call) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprCall { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9410,6 +9470,7 @@ pub struct ExprCall { /// /// See also [JoinedStr](https://docs.python.org/3/library/ast.html#ast.JoinedStr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprFString { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9425,6 +9486,7 @@ pub struct ExprFString { /// /// See also [TemplateStr](https://docs.python.org/3/library/ast.html#ast.TemplateStr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprTString { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9434,6 +9496,7 @@ pub struct ExprTString { /// An AST node that represents either a single-part string literal /// or an implicitly concatenated string literal. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprStringLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9443,6 +9506,7 @@ pub struct ExprStringLiteral { /// An AST node that represents either a single-part bytestring literal /// or an implicitly concatenated bytestring literal. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprBytesLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9450,6 +9514,7 @@ pub struct ExprBytesLiteral { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprNumberLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9457,6 +9522,7 @@ pub struct ExprNumberLiteral { } #[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprBooleanLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9464,12 +9530,14 @@ pub struct ExprBooleanLiteral { } #[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprNoneLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, } #[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprEllipsisLiteral { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9477,6 +9545,7 @@ pub struct ExprEllipsisLiteral { /// See also [Attribute](https://docs.python.org/3/library/ast.html#ast.Attribute) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprAttribute { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9487,6 +9556,7 @@ pub struct ExprAttribute { /// See also [Subscript](https://docs.python.org/3/library/ast.html#ast.Subscript) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprSubscript { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9497,6 +9567,7 @@ pub struct ExprSubscript { /// See also [Starred](https://docs.python.org/3/library/ast.html#ast.Starred) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprStarred { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9506,6 +9577,7 @@ pub struct ExprStarred { /// See also [Name](https://docs.python.org/3/library/ast.html#ast.Name) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprName { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9515,6 +9587,7 @@ pub struct ExprName { /// See also [List](https://docs.python.org/3/library/ast.html#ast.List) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprList { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9524,6 +9597,7 @@ pub struct ExprList { /// See also [Tuple](https://docs.python.org/3/library/ast.html#ast.Tuple) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprTuple { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9534,6 +9608,7 @@ pub struct ExprTuple { /// See also [Slice](https://docs.python.org/3/library/ast.html#ast.Slice) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprSlice { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, @@ -9554,6 +9629,7 @@ pub struct ExprSlice { /// For more information related to terminology and syntax of escape commands, /// see [`StmtIpyEscapeCommand`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExprIpyEscapeCommand { pub node_index: crate::AtomicNodeIndex, pub range: ruff_text_size::TextRange, diff --git a/crates/ruff_python_ast/src/int.rs b/crates/ruff_python_ast/src/int.rs index 4d918f5574..eacfd8b54a 100644 --- a/crates/ruff_python_ast/src/int.rs +++ b/crates/ruff_python_ast/src/int.rs @@ -3,6 +3,7 @@ use std::str::FromStr; /// A Python integer literal. Represents both small (fits in an `i64`) and large integers. #[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Int(Number); impl FromStr for Int { @@ -216,6 +217,7 @@ impl From for Int { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum Number { /// A "small" number that can be represented as an `u64`. Small(u64), diff --git a/crates/ruff_python_ast/src/name.rs b/crates/ruff_python_ast/src/name.rs index 725576a249..598257529b 100644 --- a/crates/ruff_python_ast/src/name.rs +++ b/crates/ruff_python_ast/src/name.rs @@ -10,6 +10,7 @@ use crate::generated::ExprName; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "cache", derive(ruff_macros::CacheKey))] #[cfg_attr(feature = "salsa", derive(salsa::Update))] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Name(compact_str::CompactString); impl Name { diff --git a/crates/ruff_python_ast/src/node_index.rs b/crates/ruff_python_ast/src/node_index.rs index d7d4e8614c..794a332b4e 100644 --- a/crates/ruff_python_ast/src/node_index.rs +++ b/crates/ruff_python_ast/src/node_index.rs @@ -19,7 +19,7 @@ where /// /// This type is interiorly mutable to allow assigning node indices /// on-demand after parsing. -#[derive(Default)] +#[derive(Default, get_size2::GetSize)] pub struct AtomicNodeIndex(AtomicU32); impl AtomicNodeIndex { @@ -41,6 +41,7 @@ impl AtomicNodeIndex { /// A unique index for a node within an AST. #[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Clone, Copy, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct NodeIndex(u32); impl NodeIndex { diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index 6217b27720..a3ad37faed 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -47,6 +47,7 @@ impl StmtClassDef { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ElifElseClause { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -133,6 +134,7 @@ impl ExprRef<'_> { /// /// [1]: https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct DictItem { pub key: Option, pub value: Expr, @@ -316,6 +318,7 @@ impl<'a> IntoIterator for &'a ExprSet { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct InterpolatedStringFormatSpec { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -324,6 +327,7 @@ pub struct InterpolatedStringFormatSpec { /// See also [FormattedValue](https://docs.python.org/3/library/ast.html#ast.FormattedValue) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct InterpolatedElement { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -335,6 +339,7 @@ pub struct InterpolatedElement { /// An `FStringLiteralElement` with an empty `value` is an invalid f-string element. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct InterpolatedStringLiteralElement { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -357,6 +362,7 @@ impl Deref for InterpolatedStringLiteralElement { /// Transforms a value prior to formatting it. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] #[repr(i8)] #[expect(clippy::cast_possible_wrap)] pub enum ConversionFlag { @@ -383,6 +389,7 @@ impl ConversionFlag { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct DebugText { /// The text between the `{` and the expression node. pub leading: String, @@ -403,6 +410,7 @@ impl ExprFString { /// The value representing an [`ExprFString`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct FStringValue { inner: FStringValueInner, } @@ -539,6 +547,7 @@ impl<'a> IntoIterator for &'a mut FStringValue { /// An internal representation of [`FStringValue`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum FStringValueInner { /// A single f-string i.e., `f"foo"`. /// @@ -552,6 +561,7 @@ enum FStringValueInner { /// An f-string part which is either a string literal or an f-string. #[derive(Clone, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum FStringPart { Literal(StringLiteral), FString(FString), @@ -595,6 +605,7 @@ impl ExprTString { /// The value representing an [`ExprTString`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TStringValue { inner: TStringValueInner, } @@ -717,6 +728,7 @@ impl<'a> IntoIterator for &'a mut TStringValue { /// An internal representation of [`TStringValue`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum TStringValueInner { /// A single t-string i.e., `t"foo"`. /// @@ -731,6 +743,7 @@ enum TStringValueInner { /// An t-string part which is either a string literal, an f-string, /// or a t-string. #[derive(Clone, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum TStringPart { Literal(StringLiteral), FString(FString), @@ -862,6 +875,8 @@ bitflags! { } } +impl get_size2::GetSize for InterpolatedStringFlagsInner {} + /// Flags that can be queried to obtain information /// regarding the prefixes and quotes used for an f-string. /// @@ -879,6 +894,7 @@ bitflags! { /// will properly handle nested f-strings. For usage that doesn't fit into one of these categories, /// the public constructor [`FStringFlags::empty`] can be used. #[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct FStringFlags(InterpolatedStringFlagsInner); impl FStringFlags { @@ -975,6 +991,7 @@ impl FStringFlags { /// will properly handle nested t-strings. For usage that doesn't fit into one of these categories, /// the public constructor [`TStringFlags::empty`] can be used. #[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TStringFlags(InterpolatedStringFlagsInner); impl TStringFlags { @@ -1129,6 +1146,7 @@ impl fmt::Debug for TStringFlags { /// An AST node that represents a single f-string which is part of an [`ExprFString`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct FString { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -1149,6 +1167,7 @@ impl From for Expr { /// A newtype wrapper around a list of [`InterpolatedStringElement`]. #[derive(Clone, Default, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct InterpolatedStringElements(Vec); impl InterpolatedStringElements { @@ -1209,6 +1228,7 @@ impl fmt::Debug for InterpolatedStringElements { /// An AST node that represents a single t-string which is part of an [`ExprTString`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TString { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -1240,6 +1260,7 @@ impl ExprStringLiteral { /// The value representing a [`ExprStringLiteral`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StringLiteralValue { inner: StringLiteralValueInner, } @@ -1397,6 +1418,7 @@ impl fmt::Display for StringLiteralValue { /// An internal representation of [`StringLiteralValue`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum StringLiteralValueInner { /// A single string literal i.e., `"foo"`. Single(StringLiteral), @@ -1440,6 +1462,8 @@ bitflags! { } } +impl get_size2::GetSize for StringLiteralFlagsInner {} + /// Flags that can be queried to obtain information /// regarding the prefixes and quotes used for a string literal. /// @@ -1453,6 +1477,7 @@ bitflags! { /// handle surrounding f-strings. For usage that doesn't fit into one of these categories, the /// public constructor [`StringLiteralFlags::empty`] can be used. #[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StringLiteralFlags(StringLiteralFlagsInner); impl StringLiteralFlags { @@ -1581,6 +1606,7 @@ impl fmt::Debug for StringLiteralFlags { /// An AST node that represents a single string literal which is part of an /// [`ExprStringLiteral`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct StringLiteral { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -1637,6 +1663,7 @@ impl From for Expr { /// An internal representation of [`StringLiteral`] that represents an /// implicitly concatenated string. #[derive(Clone)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] struct ConcatenatedStringLiteral { /// The individual [`StringLiteral`] parts that make up the concatenated string. strings: Vec, @@ -1689,6 +1716,7 @@ impl ExprBytesLiteral { /// The value representing a [`ExprBytesLiteral`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct BytesLiteralValue { inner: BytesLiteralValueInner, } @@ -1817,6 +1845,7 @@ impl<'a> From<&'a BytesLiteralValue> for Cow<'a, [u8]> { /// An internal representation of [`BytesLiteralValue`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum BytesLiteralValueInner { /// A single-part bytestring literal i.e., `b"foo"`. Single(BytesLiteral), @@ -1851,6 +1880,8 @@ bitflags! { } } +impl get_size2::GetSize for BytesLiteralFlagsInner {} + /// Flags that can be queried to obtain information /// regarding the prefixes and quotes used for a bytes literal. /// @@ -1863,6 +1894,7 @@ bitflags! { /// will properly handle surrounding f-strings. For usage that doesn't fit into one of these /// categories, the public constructor [`BytesLiteralFlags::empty`] can be used. #[derive(Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct BytesLiteralFlags(BytesLiteralFlagsInner); impl BytesLiteralFlags { @@ -1972,6 +2004,7 @@ impl fmt::Debug for BytesLiteralFlags { /// An AST node that represents a single bytes literal which is part of an /// [`ExprBytesLiteral`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct BytesLiteral { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2328,6 +2361,7 @@ impl From for AnyStringFlags { } #[derive(Clone, Debug, PartialEq, is_macro::Is)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Number { Int(int::Int), Float(f64), @@ -2395,6 +2429,7 @@ impl<'a> IntoIterator for &'a ExprTuple { /// See also [expr_context](https://docs.python.org/3/library/ast.html#ast.expr_context) #[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum ExprContext { Load, Store, @@ -2404,6 +2439,7 @@ pub enum ExprContext { /// See also [boolop](https://docs.python.org/3/library/ast.html#ast.BoolOp) #[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum BoolOp { And, Or, @@ -2426,6 +2462,7 @@ impl fmt::Display for BoolOp { /// See also [operator](https://docs.python.org/3/library/ast.html#ast.operator) #[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Operator { Add, Sub, @@ -2527,6 +2564,7 @@ impl fmt::Display for Operator { /// See also [unaryop](https://docs.python.org/3/library/ast.html#ast.unaryop) #[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum UnaryOp { Invert, Not, @@ -2553,6 +2591,7 @@ impl fmt::Display for UnaryOp { /// See also [cmpop](https://docs.python.org/3/library/ast.html#ast.cmpop) #[derive(Clone, Debug, PartialEq, is_macro::Is, Copy, Hash, Eq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum CmpOp { Eq, NotEq, @@ -2607,6 +2646,7 @@ impl fmt::Display for CmpOp { /// See also [comprehension](https://docs.python.org/3/library/ast.html#ast.comprehension) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Comprehension { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2618,6 +2658,7 @@ pub struct Comprehension { /// See also [ExceptHandler](https://docs.python.org/3/library/ast.html#ast.ExceptHandler) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ExceptHandlerExceptHandler { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2628,6 +2669,7 @@ pub struct ExceptHandlerExceptHandler { /// See also [arg](https://docs.python.org/3/library/ast.html#ast.arg) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Parameter { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2647,6 +2689,7 @@ impl Parameter { /// See also [keyword](https://docs.python.org/3/library/ast.html#ast.keyword) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Keyword { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2656,6 +2699,7 @@ pub struct Keyword { /// See also [alias](https://docs.python.org/3/library/ast.html#ast.alias) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Alias { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2665,6 +2709,7 @@ pub struct Alias { /// See also [withitem](https://docs.python.org/3/library/ast.html#ast.withitem) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct WithItem { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2674,6 +2719,7 @@ pub struct WithItem { /// See also [match_case](https://docs.python.org/3/library/ast.html#ast.match_case) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct MatchCase { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2751,6 +2797,7 @@ pub struct IrrefutablePattern { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum IrrefutablePatternKind { Name(Name), Wildcard, @@ -2758,6 +2805,7 @@ pub enum IrrefutablePatternKind { /// See also [MatchValue](https://docs.python.org/3/library/ast.html#ast.MatchValue) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchValue { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2766,6 +2814,7 @@ pub struct PatternMatchValue { /// See also [MatchSingleton](https://docs.python.org/3/library/ast.html#ast.MatchSingleton) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchSingleton { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2774,6 +2823,7 @@ pub struct PatternMatchSingleton { /// See also [MatchSequence](https://docs.python.org/3/library/ast.html#ast.MatchSequence) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchSequence { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2782,6 +2832,7 @@ pub struct PatternMatchSequence { /// See also [MatchMapping](https://docs.python.org/3/library/ast.html#ast.MatchMapping) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchMapping { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2792,6 +2843,7 @@ pub struct PatternMatchMapping { /// See also [MatchClass](https://docs.python.org/3/library/ast.html#ast.MatchClass) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchClass { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2804,6 +2856,7 @@ pub struct PatternMatchClass { /// /// Like [`Arguments`], but for [`PatternMatchClass`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternArguments { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2816,6 +2869,7 @@ pub struct PatternArguments { /// /// Like [`Keyword`], but for [`PatternMatchClass`]. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternKeyword { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2825,6 +2879,7 @@ pub struct PatternKeyword { /// See also [MatchStar](https://docs.python.org/3/library/ast.html#ast.MatchStar) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchStar { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2833,6 +2888,7 @@ pub struct PatternMatchStar { /// See also [MatchAs](https://docs.python.org/3/library/ast.html#ast.MatchAs) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchAs { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2842,6 +2898,7 @@ pub struct PatternMatchAs { /// See also [MatchOr](https://docs.python.org/3/library/ast.html#ast.MatchOr) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PatternMatchOr { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2868,6 +2925,7 @@ impl TypeParam { /// See also [TypeVar](https://docs.python.org/3/library/ast.html#ast.TypeVar) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TypeParamTypeVar { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2878,6 +2936,7 @@ pub struct TypeParamTypeVar { /// See also [ParamSpec](https://docs.python.org/3/library/ast.html#ast.ParamSpec) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TypeParamParamSpec { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2887,6 +2946,7 @@ pub struct TypeParamParamSpec { /// See also [TypeVarTuple](https://docs.python.org/3/library/ast.html#ast.TypeVarTuple) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TypeParamTypeVarTuple { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2896,6 +2956,7 @@ pub struct TypeParamTypeVarTuple { /// See also [decorator](https://docs.python.org/3/library/ast.html#ast.decorator) #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Decorator { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -2970,6 +3031,7 @@ impl Ranged for AnyParameterRef<'_> { /// NOTE: This type differs from the original Python AST. See: [arguments](https://docs.python.org/3/library/ast.html#ast.arguments). #[derive(Clone, Debug, PartialEq, Default)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Parameters { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -3189,6 +3251,7 @@ impl<'a> IntoIterator for &'a Box { /// NOTE: This type is different from original Python AST. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct ParameterWithDefault { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -3233,6 +3296,7 @@ impl ParameterWithDefault { /// typically used for `metaclass`, with any additional arguments being passed to the `metaclass`. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Arguments { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -3383,6 +3447,7 @@ impl Arguments { /// the `T`, `U`, and `V` type parameters in the order they appear in the source code. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TypeParams { pub range: TextRange, pub node_index: AtomicNodeIndex, @@ -3406,6 +3471,7 @@ pub type Suite = Vec; /// /// [IPython Syntax]: https://github.com/ipython/ipython/blob/635815e8f1ded5b764d66cacc80bbe25e9e2587f/IPython/core/inputtransformer2.py#L335-L343 #[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum IpyEscapeKind { /// Send line to underlying system shell (`!`). Shell, @@ -3497,6 +3563,7 @@ impl IpyEscapeKind { /// ... /// ``` #[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct Identifier { pub id: Name, pub range: TextRange, @@ -3572,6 +3639,7 @@ impl From for Name { } #[derive(Clone, Copy, Debug, Hash, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub enum Singleton { None, True, diff --git a/crates/ruff_python_ast/src/python_version.rs b/crates/ruff_python_ast/src/python_version.rs index 2a3b79594b..7eab154ad1 100644 --- a/crates/ruff_python_ast/src/python_version.rs +++ b/crates/ruff_python_ast/src/python_version.rs @@ -5,6 +5,7 @@ use std::{fmt, str::FromStr}; /// N.B. This does not necessarily represent a Python version that we actually support. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "cache", derive(ruff_macros::CacheKey))] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct PythonVersion { pub major: u8, pub minor: u8, diff --git a/crates/ruff_python_parser/Cargo.toml b/crates/ruff_python_parser/Cargo.toml index e6e96335bc..ae45871866 100644 --- a/crates/ruff_python_parser/Cargo.toml +++ b/crates/ruff_python_parser/Cargo.toml @@ -13,13 +13,14 @@ license = { workspace = true } [lib] [dependencies] -ruff_python_ast = { workspace = true } +ruff_python_ast = { workspace = true, features = ["get-size"] } ruff_python_trivia = { workspace = true } -ruff_text_size = { workspace = true } +ruff_text_size = { workspace = true, features = ["get-size"] } bitflags = { workspace = true } bstr = { workspace = true } compact_str = { workspace = true } +get-size2 = { workspace = true } memchr = { workspace = true } rustc-hash = { workspace = true } static_assertions = { workspace = true } diff --git a/crates/ruff_python_parser/src/error.rs b/crates/ruff_python_parser/src/error.rs index 03dd0132c2..8b3fc463e1 100644 --- a/crates/ruff_python_parser/src/error.rs +++ b/crates/ruff_python_parser/src/error.rs @@ -7,7 +7,7 @@ use crate::{TokenKind, string::InterpolatedStringKind}; /// Represents represent errors that occur during parsing and are /// returned by the `parse_*` functions. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] pub struct ParseError { pub error: ParseErrorType, pub location: TextRange, @@ -49,7 +49,7 @@ impl ParseError { } /// Represents the different types of errors that can occur during parsing of an f-string or t-string. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub enum InterpolatedStringErrorType { /// Expected a right brace after an opened left brace. UnclosedLbrace, @@ -95,7 +95,7 @@ impl std::fmt::Display for InterpolatedStringErrorType { } /// Represents the different types of errors that can occur during parsing. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)] pub enum ParseErrorType { /// An unexpected error occurred. OtherError(String), @@ -384,7 +384,7 @@ impl std::fmt::Display for LexicalError { } /// Represents the different types of errors that can occur during lexing. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub enum LexicalErrorType { // TODO: Can probably be removed, the places it is used seem to be able // to use the `UnicodeError` variant instead. @@ -468,7 +468,7 @@ impl std::fmt::Display for LexicalErrorType { /// /// An example of a version-related error is the use of a `match` statement before Python 3.10, when /// it was first introduced. See [`UnsupportedSyntaxErrorKind`] for other kinds of errors. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, get_size2::GetSize)] pub struct UnsupportedSyntaxError { pub kind: UnsupportedSyntaxErrorKind, pub range: TextRange, @@ -483,28 +483,28 @@ impl Ranged for UnsupportedSyntaxError { } /// The type of tuple unpacking for [`UnsupportedSyntaxErrorKind::StarTuple`]. -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, get_size2::GetSize)] pub enum StarTupleKind { Return, Yield, } /// The type of PEP 701 f-string error for [`UnsupportedSyntaxErrorKind::Pep701FString`]. -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, get_size2::GetSize)] pub enum FStringKind { Backslash, Comment, NestedQuote, } -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, get_size2::GetSize)] pub enum UnparenthesizedNamedExprKind { SequenceIndex, SetLiteral, SetComprehension, } -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, get_size2::GetSize)] pub enum UnsupportedSyntaxErrorKind { Match, Walrus, @@ -988,7 +988,7 @@ impl Display for UnsupportedSyntaxError { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum RelaxedDecoratorError { CallExpression, Other(&'static str), diff --git a/crates/ruff_python_parser/src/lib.rs b/crates/ruff_python_parser/src/lib.rs index 4825c2adce..54061c58e8 100644 --- a/crates/ruff_python_parser/src/lib.rs +++ b/crates/ruff_python_parser/src/lib.rs @@ -304,7 +304,7 @@ pub fn parse_unchecked_source(source: &str, source_type: PySourceType) -> Parsed } /// Represents the parsed source code. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, get_size2::GetSize)] pub struct Parsed { syntax: T, tokens: Tokens, @@ -474,7 +474,7 @@ impl Parsed { } /// Tokens represents a vector of lexed [`Token`]. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub struct Tokens { raw: Vec, } diff --git a/crates/ruff_python_parser/src/semantic_errors.rs b/crates/ruff_python_parser/src/semantic_errors.rs index c8d7fb458f..73368fce77 100644 --- a/crates/ruff_python_parser/src/semantic_errors.rs +++ b/crates/ruff_python_parser/src/semantic_errors.rs @@ -890,7 +890,7 @@ impl SemanticSyntaxChecker { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub struct SemanticSyntaxError { pub kind: SemanticSyntaxErrorKind, pub range: TextRange, @@ -981,7 +981,7 @@ impl Display for SemanticSyntaxError { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum SemanticSyntaxErrorKind { /// Represents the use of a `__future__` import after the beginning of a file. /// @@ -1303,7 +1303,7 @@ pub enum SemanticSyntaxErrorKind { NonlocalDeclarationAtModuleLevel, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum AwaitOutsideAsyncFunctionKind { Await, AsyncFor, @@ -1322,7 +1322,7 @@ impl Display for AwaitOutsideAsyncFunctionKind { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum YieldOutsideFunctionKind { Yield, YieldFrom, @@ -1345,7 +1345,7 @@ impl Display for YieldOutsideFunctionKind { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum InvalidExpressionPosition { TypeVarBound, TypeVarDefault, @@ -1370,7 +1370,7 @@ impl Display for InvalidExpressionPosition { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum InvalidExpressionKind { Yield, NamedExpr, @@ -1387,7 +1387,7 @@ impl Display for InvalidExpressionKind { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub enum WriteToDebugKind { Store, Delete(PythonVersion), diff --git a/crates/ruff_python_parser/src/token.rs b/crates/ruff_python_parser/src/token.rs index 59e4c0581c..240e015a3b 100644 --- a/crates/ruff_python_parser/src/token.rs +++ b/crates/ruff_python_parser/src/token.rs @@ -17,7 +17,7 @@ use ruff_python_ast::str_prefix::{ use ruff_python_ast::{AnyStringFlags, BoolOp, Int, IpyEscapeKind, Operator, StringFlags, UnaryOp}; use ruff_text_size::{Ranged, TextRange}; -#[derive(Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq, get_size2::GetSize)] pub struct Token { /// The kind of the token. kind: TokenKind, @@ -124,7 +124,7 @@ impl fmt::Debug for Token { } /// A kind of a token. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, get_size2::GetSize)] pub enum TokenKind { /// Token kind for a name, commonly known as an identifier. Name, @@ -754,6 +754,8 @@ bitflags! { } } +impl get_size2::GetSize for TokenFlags {} + impl StringFlags for TokenFlags { fn quote_style(self) -> Quote { if self.intersects(TokenFlags::DOUBLE_QUOTES) { diff --git a/crates/ruff_source_file/Cargo.toml b/crates/ruff_source_file/Cargo.toml index 0f82f37169..ffc41ca462 100644 --- a/crates/ruff_source_file/Cargo.toml +++ b/crates/ruff_source_file/Cargo.toml @@ -15,12 +15,14 @@ license = { workspace = true } [dependencies] ruff_text_size = { workspace = true } +get-size2 = { workspace = true, optional = true } memchr = { workspace = true } serde = { workspace = true, optional = true } [dev-dependencies] [features] +get-size = ["dep:get-size2"] serde = ["dep:serde", "ruff_text_size/serde"] [lints] diff --git a/crates/ruff_source_file/src/lib.rs b/crates/ruff_source_file/src/lib.rs index 4df0b2a20a..c7c652bc64 100644 --- a/crates/ruff_source_file/src/lib.rs +++ b/crates/ruff_source_file/src/lib.rs @@ -163,6 +163,7 @@ impl SourceFileBuilder { /// /// Cloning a [`SourceFile`] is cheap, because it only requires bumping a reference count. #[derive(Clone, Eq, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct SourceFile { inner: Arc, } @@ -225,6 +226,7 @@ impl Ord for SourceFile { } } +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] struct SourceFileInner { name: Box, code: Box, diff --git a/crates/ruff_source_file/src/line_index.rs b/crates/ruff_source_file/src/line_index.rs index 54d82b1ab9..8da47e0dba 100644 --- a/crates/ruff_source_file/src/line_index.rs +++ b/crates/ruff_source_file/src/line_index.rs @@ -14,11 +14,13 @@ use serde::{Deserialize, Serialize}; /// /// Cloning a [`LineIndex`] is cheap because it only requires bumping a reference count. #[derive(Clone, Eq, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct LineIndex { inner: Arc, } #[derive(Eq, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] struct LineIndexInner { line_starts: Vec, kind: IndexKind, @@ -534,6 +536,7 @@ impl Debug for LineIndex { } #[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] enum IndexKind { /// Optimized index for an ASCII only document Ascii, diff --git a/crates/ruff_text_size/Cargo.toml b/crates/ruff_text_size/Cargo.toml index 19de486464..d658fe4db3 100644 --- a/crates/ruff_text_size/Cargo.toml +++ b/crates/ruff_text_size/Cargo.toml @@ -12,6 +12,7 @@ license = { workspace = true } [dependencies] serde = { workspace = true, optional = true } +get-size2 = { workspace = true, optional = true } schemars = { workspace = true, optional = true } [dev-dependencies] @@ -20,6 +21,7 @@ static_assertions = { workspace = true } [features] serde = ["dep:serde"] +get-size = ["dep:get-size2"] [lints] workspace = true diff --git a/crates/ruff_text_size/src/range.rs b/crates/ruff_text_size/src/range.rs index f2767c3d75..895cfea530 100644 --- a/crates/ruff_text_size/src/range.rs +++ b/crates/ruff_text_size/src/range.rs @@ -12,6 +12,7 @@ use { /// /// It is a logic error for `start` to be greater than `end`. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TextRange { // Invariant: start <= end start: TextSize, diff --git a/crates/ruff_text_size/src/size.rs b/crates/ruff_text_size/src/size.rs index 1b597698d3..dda5ded61d 100644 --- a/crates/ruff_text_size/src/size.rs +++ b/crates/ruff_text_size/src/size.rs @@ -21,6 +21,7 @@ use { /// These escape hatches are primarily required for unit testing and when /// converting from UTF-8 size to another coordinate space, such as UTF-16. #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))] pub struct TextSize { pub(crate) raw: u32, } diff --git a/crates/ty/src/lib.rs b/crates/ty/src/lib.rs index aa00d2e374..4b3709d6d0 100644 --- a/crates/ty/src/lib.rs +++ b/crates/ty/src/lib.rs @@ -143,6 +143,13 @@ fn run_check(args: CheckCommand) -> anyhow::Result { main_loop.run(&mut db)? }; + let mut stdout = stdout().lock(); + match std::env::var("TY_MEMORY_REPORT").as_deref() { + Ok("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?, + Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?, + _ => {} + } + tracing::trace!("Counts for entire CLI run:\n{}", countme::get_all()); std::mem::forget(db); diff --git a/crates/ty_project/Cargo.toml b/crates/ty_project/Cargo.toml index d0648255fd..dcef49bd4a 100644 --- a/crates/ty_project/Cargo.toml +++ b/crates/ty_project/Cargo.toml @@ -27,6 +27,7 @@ anyhow = { workspace = true } camino = { workspace = true } colored = { workspace = true } crossbeam = { workspace = true } +get-size2 = { workspace = true } globset = { workspace = true } notify = { workspace = true } pep440_rs = { workspace = true, features = ["version-ranges"] } diff --git a/crates/ty_project/src/db.rs b/crates/ty_project/src/db.rs index dc9f282109..e614f3e73b 100644 --- a/crates/ty_project/src/db.rs +++ b/crates/ty_project/src/db.rs @@ -1,5 +1,6 @@ use std::panic::{AssertUnwindSafe, RefUnwindSafe}; use std::sync::Arc; +use std::{cmp, fmt}; use crate::metadata::settings::file_settings; use crate::{DEFAULT_LINT_REGISTRY, DummyReporter}; @@ -108,6 +109,171 @@ impl ProjectDatabase { Arc::get_mut(&mut self.system) .expect("ref count should be 1 because `zalsa_mut` drops all other DB references.") } + + /// Returns a [`SalsaMemoryDump`] that can be use to dump Salsa memory usage information + /// to the CLI after a typechecker run. + pub fn salsa_memory_dump(&self) -> SalsaMemoryDump { + let salsa_db = self as &dyn salsa::Database; + + let mut ingredients = salsa_db.structs_info(); + let mut memos = salsa_db.queries_info().into_iter().collect::>(); + + ingredients.sort_by_key(|ingredient| cmp::Reverse(ingredient.size_of_fields())); + memos.sort_by_key(|(_, memo)| cmp::Reverse(memo.size_of_fields())); + + SalsaMemoryDump { ingredients, memos } + } +} + +/// Stores memory usage information. +pub struct SalsaMemoryDump { + ingredients: Vec, + memos: Vec<(&'static str, salsa::IngredientInfo)>, +} + +#[allow(clippy::cast_precision_loss)] +impl SalsaMemoryDump { + /// Returns a short report that provides total memory usage information. + pub fn display_short(&self) -> impl fmt::Display + '_ { + struct DisplayShort<'a>(&'a SalsaMemoryDump); + + impl fmt::Display for DisplayShort<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut total_fields = 0; + let mut total_metadata = 0; + for ingredient in &self.0.ingredients { + total_metadata += ingredient.size_of_metadata(); + total_fields += ingredient.size_of_fields(); + } + + let mut total_memo_fields = 0; + let mut total_memo_metadata = 0; + for (_, memo) in &self.0.memos { + total_memo_fields += memo.size_of_fields(); + total_memo_metadata += memo.size_of_metadata(); + } + + writeln!(f, "=======SALSA SUMMARY=======")?; + + writeln!( + f, + "TOTAL MEMORY USAGE: {:.2}MB", + (total_metadata + total_fields + total_memo_fields + total_memo_metadata) + as f64 + / 1_000_000., + )?; + + writeln!( + f, + " struct metadata = {:.2}MB", + total_metadata as f64 / 1_000_000., + )?; + writeln!( + f, + " struct fields = {:.2}MB", + total_fields as f64 / 1_000_000., + )?; + writeln!( + f, + " memo metadata = {:.2}MB", + total_memo_metadata as f64 / 1_000_000., + )?; + writeln!( + f, + " memo fields = {:.2}MB", + total_memo_fields as f64 / 1_000_000. + )?; + + writeln!(f, "QUERY COUNT: {}", self.0.memos.len())?; + writeln!(f, "STRUCT COUNT: {}", self.0.ingredients.len())?; + + Ok(()) + } + } + + DisplayShort(self) + } + + /// Returns a short report that provides fine-grained memory usage information per + /// Salsa ingredient. + pub fn display_full(&self) -> impl fmt::Display + '_ { + struct DisplayFull<'a>(&'a SalsaMemoryDump); + + impl fmt::Display for DisplayFull<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "=======SALSA STRUCTS=======")?; + + let mut total_fields = 0; + let mut total_metadata = 0; + for ingredient in &self.0.ingredients { + total_metadata += ingredient.size_of_metadata(); + total_fields += ingredient.size_of_fields(); + + writeln!( + f, + "{:<50} metadata={:<8} fields={:<8} count={}", + format!("`{}`", ingredient.debug_name()), + format!("{:.2}MB", ingredient.size_of_metadata() as f64 / 1_000_000.), + format!("{:.2}MB", ingredient.size_of_fields() as f64 / 1_000_000.), + ingredient.count() + )?; + } + + writeln!(f, "=======SALSA QUERIES=======")?; + + let mut total_memo_fields = 0; + let mut total_memo_metadata = 0; + for (query_fn, memo) in &self.0.memos { + total_memo_fields += memo.size_of_fields(); + total_memo_metadata += memo.size_of_metadata(); + + writeln!(f, "`{query_fn} -> {}`", memo.debug_name())?; + + writeln!( + f, + " metadata={:<8} fields={:<8} count={}", + format!("{:.2}MB", memo.size_of_metadata() as f64 / 1_000_000.), + format!("{:.2}MB", memo.size_of_fields() as f64 / 1_000_000.), + memo.count() + )?; + } + + writeln!(f, "=======SALSA SUMMARY=======")?; + writeln!( + f, + "TOTAL MEMORY USAGE: {:.2}MB", + (total_metadata + total_fields + total_memo_fields + total_memo_metadata) + as f64 + / 1_000_000., + )?; + + writeln!( + f, + " struct metadata = {:.2}MB", + total_metadata as f64 / 1_000_000., + )?; + writeln!( + f, + " struct fields = {:.2}MB", + total_fields as f64 / 1_000_000., + )?; + writeln!( + f, + " memo metadata = {:.2}MB", + total_memo_metadata as f64 / 1_000_000., + )?; + writeln!( + f, + " memo fields = {:.2}MB", + total_memo_fields as f64 / 1_000_000. + )?; + + Ok(()) + } + } + + DisplayFull(self) + } } impl Upcast for ProjectDatabase { diff --git a/crates/ty_project/src/lib.rs b/crates/ty_project/src/lib.rs index 11834036ab..4dce06e90a 100644 --- a/crates/ty_project/src/lib.rs +++ b/crates/ty_project/src/lib.rs @@ -1,7 +1,7 @@ use crate::glob::{GlobFilterCheckMode, IncludeResult}; use crate::metadata::options::{OptionDiagnostic, ToSettingsError}; use crate::walk::{ProjectFilesFilter, ProjectFilesWalker}; -pub use db::{Db, ProjectDatabase}; +pub use db::{Db, ProjectDatabase, SalsaMemoryDump}; use files::{Index, Indexed, IndexedFiles}; use metadata::settings::Settings; pub use metadata::{ProjectMetadata, ProjectMetadataError}; @@ -159,7 +159,7 @@ impl Project { /// This is a salsa query to prevent re-computing queries if other, unrelated /// settings change. For example, we don't want that changing the terminal settings /// invalidates any type checking queries. - #[salsa::tracked(returns(deref))] + #[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] pub fn rules(self, db: &dyn Db) -> Arc { self.settings(db).to_rules() } diff --git a/crates/ty_project/src/metadata/settings.rs b/crates/ty_project/src/metadata/settings.rs index 1c8424fd9c..98cef0f74c 100644 --- a/crates/ty_project/src/metadata/settings.rs +++ b/crates/ty_project/src/metadata/settings.rs @@ -96,7 +96,7 @@ impl Override { } /// Resolves the settings for a given file. -#[salsa::tracked(returns(ref))] +#[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn file_settings(db: &dyn Db, file: File) -> FileSettings { let settings = db.project().settings(db); @@ -155,7 +155,7 @@ pub(crate) fn file_settings(db: &dyn Db, file: File) -> FileSettings { /// This is to make Salsa happy because it requires that queries with only a single argument /// take a salsa-struct as argument, which isn't the case here. The `()` enables salsa's /// automatic interning for the arguments. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] fn merge_overrides(db: &dyn Db, overrides: Vec>, _: ()) -> FileSettings { let mut overrides = overrides.into_iter().rev(); let mut merged = (*overrides.next().unwrap()).clone(); @@ -179,7 +179,7 @@ fn merge_overrides(db: &dyn Db, overrides: Vec>, _: () } /// The resolved settings for a file. -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, get_size2::GetSize)] pub enum FileSettings { /// The file uses the global settings. Global, @@ -197,7 +197,7 @@ impl FileSettings { } } -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, get_size2::GetSize)] pub struct OverrideSettings { pub(super) rules: RuleSelection, } diff --git a/crates/ty_python_semantic/Cargo.toml b/crates/ty_python_semantic/Cargo.toml index 7c5268299d..a4d5c6a6eb 100644 --- a/crates/ty_python_semantic/Cargo.toml +++ b/crates/ty_python_semantic/Cargo.toml @@ -30,6 +30,7 @@ colored = { workspace = true } compact_str = { workspace = true } countme = { workspace = true } drop_bomb = { workspace = true } +get-size2 = { workspace = true } indexmap = { workspace = true } itertools = { workspace = true } ordermap = { workspace = true } diff --git a/crates/ty_python_semantic/src/ast_node_ref.rs b/crates/ty_python_semantic/src/ast_node_ref.rs index 3c15435f92..c5f7f115cc 100644 --- a/crates/ty_python_semantic/src/ast_node_ref.rs +++ b/crates/ty_python_semantic/src/ast_node_ref.rs @@ -89,6 +89,8 @@ where } } +impl get_size2::GetSize for AstNodeRef {} + #[allow(clippy::missing_fields_in_debug)] impl Debug for AstNodeRef where diff --git a/crates/ty_python_semantic/src/dunder_all.rs b/crates/ty_python_semantic/src/dunder_all.rs index 78ceb1250d..4ed0aee93f 100644 --- a/crates/ty_python_semantic/src/dunder_all.rs +++ b/crates/ty_python_semantic/src/dunder_all.rs @@ -28,7 +28,7 @@ fn dunder_all_names_cycle_initial(_db: &dyn Db, _file: File) -> Option Option> { let _span = tracing::trace_span!("dunder_all_names", file=?file.path(db)).entered(); diff --git a/crates/ty_python_semantic/src/lint.rs b/crates/ty_python_semantic/src/lint.rs index 247bae7d78..0800ee5983 100644 --- a/crates/ty_python_semantic/src/lint.rs +++ b/crates/ty_python_semantic/src/lint.rs @@ -262,7 +262,7 @@ macro_rules! declare_lint { /// /// Implements `PartialEq`, `Eq`, and `Hash` based on the `LintMetadata` pointer /// for fast comparison and lookup. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, get_size2::GetSize)] pub struct LintId { definition: &'static LintMetadata, } @@ -415,7 +415,7 @@ impl LintRegistry { } } -#[derive(Error, Debug, Clone, PartialEq, Eq)] +#[derive(Error, Debug, Clone, PartialEq, Eq, get_size2::GetSize)] pub enum GetLintError { /// The name maps to this removed lint. #[error("lint `{0}` has been removed")] @@ -463,7 +463,7 @@ impl From<&'static LintMetadata> for LintEntry { } } -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, get_size2::GetSize)] pub struct RuleSelection { /// Map with the severity for each enabled lint rule. /// @@ -541,7 +541,7 @@ impl RuleSelection { } } -#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub enum LintSource { /// The user didn't enable the rule explicitly, instead it's enabled by default. #[default] diff --git a/crates/ty_python_semantic/src/list.rs b/crates/ty_python_semantic/src/list.rs index 38061787d0..093d6e6376 100644 --- a/crates/ty_python_semantic/src/list.rs +++ b/crates/ty_python_semantic/src/list.rs @@ -69,7 +69,7 @@ use ruff_index::{IndexVec, newtype_index}; /// A handle to an association list. Use [`ListStorage`] to access its elements, and /// [`ListBuilder`] to construct other lists based on this one. -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, get_size2::GetSize)] pub(crate) struct List { last: Option, _phantom: PhantomData<(K, V)>, @@ -95,12 +95,12 @@ impl Default for List { } #[newtype_index] -#[derive(PartialOrd, Ord)] +#[derive(PartialOrd, Ord, get_size2::GetSize)] struct ListCellId; /// Stores one or more association lists. This type provides read-only access to the lists. Use a /// [`ListBuilder`] to create lists. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, get_size2::GetSize)] pub(crate) struct ListStorage { cells: IndexVec>, } @@ -111,7 +111,7 @@ pub(crate) struct ListStorage { /// **Terminology**: The elements of a cons cell are usually called `head` and `tail` (assuming /// you're not in Lisp-land, where they're called `car` and `cdr`). The elements of a snoc cell /// are usually called `rest` and `last`. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, get_size2::GetSize)] struct ListCell { rest: Option, key: K, diff --git a/crates/ty_python_semantic/src/module_name.rs b/crates/ty_python_semantic/src/module_name.rs index a4a0251fcd..eb6ec828de 100644 --- a/crates/ty_python_semantic/src/module_name.rs +++ b/crates/ty_python_semantic/src/module_name.rs @@ -13,7 +13,7 @@ use crate::{db::Db, module_resolver::file_to_module}; /// A module name, e.g. `foo.bar`. /// /// Always normalized to the absolute form (never a relative module name, i.e., never `.foo`). -#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, get_size2::GetSize)] pub struct ModuleName(compact_str::CompactString); impl ModuleName { diff --git a/crates/ty_python_semantic/src/module_resolver/module.rs b/crates/ty_python_semantic/src/module_resolver/module.rs index 8ffd477622..38b59a3c3b 100644 --- a/crates/ty_python_semantic/src/module_resolver/module.rs +++ b/crates/ty_python_semantic/src/module_resolver/module.rs @@ -8,7 +8,7 @@ use super::path::SearchPath; use crate::module_name::ModuleName; /// Representation of a Python module. -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub struct Module { inner: Arc, } @@ -99,7 +99,7 @@ impl std::fmt::Debug for Module { } } -#[derive(PartialEq, Eq, Hash)] +#[derive(PartialEq, Eq, Hash, get_size2::GetSize)] enum ModuleInner { /// A module that resolves to a file (`lib.py` or `package/__init__.py`) FileModule { @@ -116,7 +116,7 @@ enum ModuleInner { NamespacePackage { name: ModuleName }, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)] pub enum ModuleKind { /// A single-file module (e.g. `foo.py` or `foo.pyi`) Module, @@ -135,7 +135,7 @@ impl ModuleKind { } /// Enumeration of various core stdlib modules in which important types are located -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::EnumString)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::EnumString, get_size2::GetSize)] #[cfg_attr(test, derive(strum_macros::EnumIter))] #[strum(serialize_all = "snake_case")] pub enum KnownModule { diff --git a/crates/ty_python_semantic/src/module_resolver/path.rs b/crates/ty_python_semantic/src/module_resolver/path.rs index c128a233f9..37eb8c7b8e 100644 --- a/crates/ty_python_semantic/src/module_resolver/path.rs +++ b/crates/ty_python_semantic/src/module_resolver/path.rs @@ -371,7 +371,7 @@ impl From for SearchPathValidationError { type SearchPathResult = Result; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] enum SearchPathInner { Extra(SystemPathBuf), FirstParty(SystemPathBuf), @@ -406,7 +406,7 @@ enum SearchPathInner { /// or the "Editable" category. For the "First-party", "Site-packages" /// and "Standard-library" categories, however, there will always be exactly /// one search path from that category in any given list of search paths. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub(crate) struct SearchPath(Arc); impl SearchPath { diff --git a/crates/ty_python_semantic/src/module_resolver/resolver.rs b/crates/ty_python_semantic/src/module_resolver/resolver.rs index b0c7dc2349..239a9f64c5 100644 --- a/crates/ty_python_semantic/src/module_resolver/resolver.rs +++ b/crates/ty_python_semantic/src/module_resolver/resolver.rs @@ -30,7 +30,7 @@ pub fn resolve_module(db: &dyn Db, module_name: &ModuleName) -> Option { /// /// This query should not be called directly. Instead, use [`resolve_module`]. It only exists /// because Salsa requires the module name to be an ingredient. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn resolve_module_query<'db>( db: &'db dyn Db, module_name: ModuleNameIngredient<'db>, @@ -95,7 +95,7 @@ impl std::fmt::Display for SystemOrVendoredPathRef<'_> { /// Resolves the module for the file with the given id. /// /// Returns `None` if the file is not a module locatable via any of the known search paths. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option { let _span = tracing::trace_span!("file_to_module", ?file).entered(); @@ -297,7 +297,7 @@ impl SearchPaths { /// The editable-install search paths for the first `site-packages` directory /// should come between the two `site-packages` directories when it comes to /// module-resolution priority. -#[salsa::tracked(returns(deref))] +#[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn dynamic_resolution_paths(db: &dyn Db) -> Vec { tracing::debug!("Resolving dynamic module resolution paths"); diff --git a/crates/ty_python_semantic/src/node_key.rs b/crates/ty_python_semantic/src/node_key.rs index 470ef35df2..18edfe1a04 100644 --- a/crates/ty_python_semantic/src/node_key.rs +++ b/crates/ty_python_semantic/src/node_key.rs @@ -1,7 +1,7 @@ use ruff_python_ast::{HasNodeIndex, NodeIndex}; /// Compact key for a node for use in a hash map. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)] pub(super) struct NodeKey(NodeIndex); impl NodeKey { diff --git a/crates/ty_python_semantic/src/place.rs b/crates/ty_python_semantic/src/place.rs index 64c5f80271..4b6c194e28 100644 --- a/crates/ty_python_semantic/src/place.rs +++ b/crates/ty_python_semantic/src/place.rs @@ -18,7 +18,7 @@ pub(crate) use implicit_globals::{ module_type_implicit_global_declaration, module_type_implicit_global_symbol, }; -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, get_size2::GetSize)] pub(crate) enum Boundness { Bound, PossiblyUnbound, @@ -50,7 +50,7 @@ impl Boundness { /// possibly_unbound: Place::Type(Type::IntLiteral(2), Boundness::PossiblyUnbound), /// non_existent: Place::Unbound, /// ``` -#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) enum Place<'db> { Type(Type<'db>, Boundness), Unbound, @@ -497,7 +497,7 @@ pub(crate) type PlaceFromDeclarationsResult<'db> = /// that this comes with a [`CLASS_VAR`] type qualifier. /// /// [`CLASS_VAR`]: crate::types::TypeQualifiers::CLASS_VAR -#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) struct PlaceAndQualifiers<'db> { pub(crate) place: Place<'db>, pub(crate) qualifiers: TypeQualifiers, @@ -625,7 +625,7 @@ fn place_cycle_initial<'db>( Place::bound(Type::Never).into() } -#[salsa::tracked(cycle_fn=place_cycle_recover, cycle_initial=place_cycle_initial)] +#[salsa::tracked(cycle_fn=place_cycle_recover, cycle_initial=place_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] fn place_by_id<'db>( db: &'db dyn Db, scope: ScopeId<'db>, @@ -1312,7 +1312,7 @@ mod implicit_globals { /// Conceptually this function could be a `Set` rather than a list, /// but the number of symbols declared in this scope is likely to be very small, /// so the cost of hashing the names is likely to be more expensive than it's worth. - #[salsa::tracked(returns(deref))] + #[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] fn module_type_symbols<'db>(db: &'db dyn Db) -> smallvec::SmallVec<[ast::name::Name; 8]> { let Some(module_type) = KnownClass::ModuleType .to_class_literal(db) diff --git a/crates/ty_python_semantic/src/semantic_index.rs b/crates/ty_python_semantic/src/semantic_index.rs index 009674ba26..0bee998f57 100644 --- a/crates/ty_python_semantic/src/semantic_index.rs +++ b/crates/ty_python_semantic/src/semantic_index.rs @@ -24,6 +24,7 @@ use crate::semantic_index::place::{ ScopeKind, ScopedPlaceId, }; use crate::semantic_index::use_def::{EagerSnapshotKey, ScopedEagerSnapshotId, UseDefMap}; +use crate::util::get_size::untracked_arc_size; pub mod ast_ids; mod builder; @@ -46,7 +47,7 @@ type PlaceSet = hashbrown::HashTable; /// Returns the semantic index for `file`. /// /// Prefer using [`symbol_table`] when working with symbols from a single scope. -#[salsa::tracked(returns(ref), no_eq)] +#[salsa::tracked(returns(ref), no_eq, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn semantic_index(db: &dyn Db, file: File) -> SemanticIndex<'_> { let _span = tracing::trace_span!("semantic_index", ?file).entered(); @@ -60,7 +61,7 @@ pub(crate) fn semantic_index(db: &dyn Db, file: File) -> SemanticIndex<'_> { /// Using [`place_table`] over [`semantic_index`] has the advantage that /// Salsa can avoid invalidating dependent queries if this scope's place table /// is unchanged. -#[salsa::tracked(returns(deref))] +#[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn place_table<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc { let file = scope.file(db); let _span = tracing::trace_span!("place_table", scope=?scope.as_id(), ?file).entered(); @@ -80,7 +81,7 @@ pub(crate) fn place_table<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc(db: &'db dyn Db, file: File) -> Arc> { semantic_index(db, file).imported_modules.clone() } @@ -90,8 +91,8 @@ pub(crate) fn imported_modules<'db>(db: &'db dyn Db, file: File) -> Arc(db: &'db dyn Db, scope: ScopeId<'db>) -> Arc> { +#[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] +pub(crate) fn use_def_map<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> ArcUseDefMap<'db> { let file = scope.file(db); let _span = tracing::trace_span!("use_def_map", scope=?scope.as_id(), ?file).entered(); let index = semantic_index(db, file); @@ -116,7 +117,10 @@ pub(crate) fn attribute_assignments<'db, 's>( let place_table = index.place_table(function_scope_id); let place = place_table.place_id_by_instance_attribute_name(name)?; let use_def = &index.use_def_maps[function_scope_id]; - Some((use_def.end_of_scope_bindings(place), function_scope_id)) + Some(( + use_def.inner.end_of_scope_bindings(place), + function_scope_id, + )) }) } @@ -151,7 +155,7 @@ pub(crate) fn attribute_scopes<'db, 's>( } /// Returns the module global scope of `file`. -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn global_scope(db: &dyn Db, file: File) -> ScopeId<'_> { let _span = tracing::trace_span!("global_scope", ?file).entered(); @@ -166,7 +170,7 @@ pub(crate) enum EagerSnapshotResult<'map, 'db> { } /// The place tables and use-def maps for all scopes in a file. -#[derive(Debug, Update)] +#[derive(Debug, Update, get_size2::GetSize)] pub(crate) struct SemanticIndex<'db> { /// List of all place tables in this file, indexed by scope. place_tables: IndexVec>, @@ -193,7 +197,7 @@ pub(crate) struct SemanticIndex<'db> { globals_by_scope: FxHashMap>, /// Use-def map for each scope in this file. - use_def_maps: IndexVec>>, + use_def_maps: IndexVec>, /// Lookup table to map between node ids and ast nodes. /// @@ -232,7 +236,7 @@ impl<'db> SemanticIndex<'db> { /// Use the Salsa cached [`use_def_map()`] query if you only need the /// use-def map for a single scope. #[track_caller] - pub(super) fn use_def_map(&self, scope_id: FileScopeId) -> Arc { + pub(super) fn use_def_map(&self, scope_id: FileScopeId) -> ArcUseDefMap<'_> { self.use_def_maps[scope_id].clone() } @@ -457,7 +461,7 @@ impl<'db> SemanticIndex<'db> { let Some(id) = self.eager_snapshots.get(&key) else { return EagerSnapshotResult::NotFound; }; - self.use_def_maps[enclosing_scope].eager_snapshot(*id) + self.use_def_maps[enclosing_scope].inner.eager_snapshot(*id) } pub(crate) fn semantic_syntax_errors(&self) -> &[SemanticSyntaxError] { @@ -465,6 +469,28 @@ impl<'db> SemanticIndex<'db> { } } +#[derive(Debug, PartialEq, Eq, Clone, salsa::Update, get_size2::GetSize)] +pub(crate) struct ArcUseDefMap<'db> { + #[get_size(size_fn = untracked_arc_size)] + inner: Arc>, +} + +impl<'db> std::ops::Deref for ArcUseDefMap<'db> { + type Target = UseDefMap<'db>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'db> ArcUseDefMap<'db> { + pub(crate) fn new(inner: UseDefMap<'db>) -> Self { + Self { + inner: Arc::new(inner), + } + } +} + pub struct AncestorsIter<'a> { scopes: &'a IndexSlice, next_id: Option, diff --git a/crates/ty_python_semantic/src/semantic_index/ast_ids.rs b/crates/ty_python_semantic/src/semantic_index/ast_ids.rs index 191be73c23..829c62c877 100644 --- a/crates/ty_python_semantic/src/semantic_index/ast_ids.rs +++ b/crates/ty_python_semantic/src/semantic_index/ast_ids.rs @@ -24,7 +24,7 @@ use crate::semantic_index::semantic_index; /// /// x = foo() /// ``` -#[derive(Debug, salsa::Update)] +#[derive(Debug, salsa::Update, get_size2::GetSize)] pub(crate) struct AstIds { /// Maps expressions to their expression id. expressions_map: FxHashMap, @@ -51,6 +51,7 @@ fn ast_ids<'db>(db: &'db dyn Db, scope: ScopeId) -> &'db AstIds { /// Uniquely identifies a use of a name in a [`crate::semantic_index::place::FileScopeId`]. #[newtype_index] +#[derive(get_size2::GetSize)] pub struct ScopedUseId; pub trait HasScopedUseId { @@ -95,7 +96,7 @@ impl HasScopedUseId for ast::ExprRef<'_> { /// Uniquely identifies an [`ast::Expr`] in a [`crate::semantic_index::place::FileScopeId`]. #[newtype_index] -#[derive(salsa::Update)] +#[derive(salsa::Update, get_size2::GetSize)] pub struct ScopedExpressionId; pub trait HasScopedExpressionId { @@ -203,7 +204,7 @@ pub(crate) mod node_key { use crate::node_key::NodeKey; - #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)] pub(crate) struct ExpressionNodeKey(NodeKey); impl From> for ExpressionNodeKey { diff --git a/crates/ty_python_semantic/src/semantic_index/builder.rs b/crates/ty_python_semantic/src/semantic_index/builder.rs index dcaff07143..3b4384c750 100644 --- a/crates/ty_python_semantic/src/semantic_index/builder.rs +++ b/crates/ty_python_semantic/src/semantic_index/builder.rs @@ -20,7 +20,6 @@ use crate::ast_node_ref::AstNodeRef; use crate::module_name::ModuleName; use crate::module_resolver::resolve_module; use crate::node_key::NodeKey; -use crate::semantic_index::SemanticIndex; use crate::semantic_index::ast_ids::AstIdsBuilder; use crate::semantic_index::ast_ids::node_key::ExpressionNodeKey; use crate::semantic_index::definition::{ @@ -46,6 +45,7 @@ use crate::semantic_index::reachability_constraints::{ use crate::semantic_index::use_def::{ EagerSnapshotKey, FlowSnapshot, ScopedEagerSnapshotId, UseDefMapBuilder, }; +use crate::semantic_index::{ArcUseDefMap, SemanticIndex}; use crate::unpack::{Unpack, UnpackKind, UnpackPosition, UnpackValue}; use crate::{Db, Program}; @@ -998,7 +998,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> { let mut use_def_maps: IndexVec<_, _> = self .use_def_maps .into_iter() - .map(|builder| Arc::new(builder.finish())) + .map(|builder| ArcUseDefMap::new(builder.finish())) .collect(); let mut ast_ids: IndexVec<_, _> = self diff --git a/crates/ty_python_semantic/src/semantic_index/definition.rs b/crates/ty_python_semantic/src/semantic_index/definition.rs index 869944eba5..e070dce353 100644 --- a/crates/ty_python_semantic/src/semantic_index/definition.rs +++ b/crates/ty_python_semantic/src/semantic_index/definition.rs @@ -44,6 +44,9 @@ pub struct Definition<'db> { count: countme::Count>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for Definition<'_> {} + impl<'db> Definition<'db> { pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> { self.file_scope(db).to_scope_id(db, self.file(db)) @@ -59,16 +62,20 @@ impl<'db> Definition<'db> { } /// One or more [`Definition`]s. -#[derive(Debug, Default, PartialEq, Eq, salsa::Update)] -pub struct Definitions<'db>(smallvec::SmallVec<[Definition<'db>; 1]>); +#[derive(Debug, Default, PartialEq, Eq, salsa::Update, get_size2::GetSize)] +pub struct Definitions<'db> { + definitions: smallvec::SmallVec<[Definition<'db>; 1]>, +} impl<'db> Definitions<'db> { pub(crate) fn single(definition: Definition<'db>) -> Self { - Self(smallvec::smallvec![definition]) + Self { + definitions: smallvec::smallvec![definition], + } } pub(crate) fn push(&mut self, definition: Definition<'db>) { - self.0.push(definition); + self.definitions.push(definition); } } @@ -76,7 +83,7 @@ impl<'db> Deref for Definitions<'db> { type Target = [Definition<'db>]; fn deref(&self) -> &Self::Target { - &self.0 + &self.definitions } } @@ -85,11 +92,11 @@ impl<'a, 'db> IntoIterator for &'a Definitions<'db> { type IntoIter = std::slice::Iter<'a, Definition<'db>>; fn into_iter(self) -> Self::IntoIter { - self.0.iter() + self.definitions.iter() } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) enum DefinitionState<'db> { Defined(Definition<'db>), /// Represents the implicit "unbound"/"undeclared" definition of every place. @@ -999,7 +1006,7 @@ impl ExceptHandlerDefinitionKind { } } -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update)] +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, salsa::Update, get_size2::GetSize)] pub(crate) struct DefinitionNodeKey(NodeKey); impl From<&ast::Alias> for DefinitionNodeKey { diff --git a/crates/ty_python_semantic/src/semantic_index/expression.rs b/crates/ty_python_semantic/src/semantic_index/expression.rs index 18a64b54e7..476255d6d5 100644 --- a/crates/ty_python_semantic/src/semantic_index/expression.rs +++ b/crates/ty_python_semantic/src/semantic_index/expression.rs @@ -62,6 +62,9 @@ pub(crate) struct Expression<'db> { count: countme::Count>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for Expression<'_> {} + impl<'db> Expression<'db> { pub(crate) fn node_ref<'ast>( self, diff --git a/crates/ty_python_semantic/src/semantic_index/narrowing_constraints.rs b/crates/ty_python_semantic/src/semantic_index/narrowing_constraints.rs index 48297e1da6..54155a6ff3 100644 --- a/crates/ty_python_semantic/src/semantic_index/narrowing_constraints.rs +++ b/crates/ty_python_semantic/src/semantic_index/narrowing_constraints.rs @@ -55,7 +55,7 @@ pub(crate) enum ConstraintKey { /// [`ScopedPredicateId`] to refer to the underlying predicate. /// /// [`Predicate`]: crate::semantic_index::predicate::Predicate -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, get_size2::GetSize)] pub(crate) struct ScopedNarrowingConstraintPredicate(ScopedPredicateId); impl ScopedNarrowingConstraintPredicate { @@ -72,7 +72,7 @@ impl From for ScopedNarrowingConstraintPredicate { } /// A collection of narrowing constraints for a given scope. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, get_size2::GetSize)] pub(crate) struct NarrowingConstraints { lists: ListStorage, } diff --git a/crates/ty_python_semantic/src/semantic_index/place.rs b/crates/ty_python_semantic/src/semantic_index/place.rs index 6b26cd4b7f..9dec0488ce 100644 --- a/crates/ty_python_semantic/src/semantic_index/place.rs +++ b/crates/ty_python_semantic/src/semantic_index/place.rs @@ -18,7 +18,7 @@ use crate::node_key::NodeKey; use crate::semantic_index::reachability_constraints::ScopedReachabilityConstraintId; use crate::semantic_index::{PlaceSet, SemanticIndex, semantic_index}; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub(crate) enum PlaceExprSubSegment { /// A member access, e.g. `.y` in `x.y` Member(ast::name::Name), @@ -38,7 +38,7 @@ impl PlaceExprSubSegment { } /// An expression that can be the target of a `Definition`. -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, get_size2::GetSize)] pub struct PlaceExpr { root_name: Name, sub_segments: SmallVec<[PlaceExprSubSegment; 1]>, @@ -217,7 +217,7 @@ impl PlaceExpr { } /// A [`PlaceExpr`] with flags, e.g. whether it is used, bound, an instance attribute, etc. -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, get_size2::GetSize)] pub struct PlaceExprWithFlags { pub(crate) expr: PlaceExpr, flags: PlaceFlags, @@ -405,6 +405,8 @@ bitflags! { } } +impl get_size2::GetSize for PlaceFlags {} + /// ID that uniquely identifies a place in a file. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct FilePlaceId { @@ -430,7 +432,7 @@ impl From for ScopedPlaceId { /// ID that uniquely identifies a place inside a [`Scope`]. #[newtype_index] -#[derive(salsa::Update)] +#[derive(salsa::Update, get_size2::GetSize)] pub struct ScopedPlaceId; /// A cross-module identifier of a scope that can be used as a salsa query parameter. @@ -443,6 +445,9 @@ pub struct ScopeId<'db> { count: countme::Count>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for ScopeId<'_> {} + impl<'db> ScopeId<'db> { pub(crate) fn is_function_like(self, db: &'db dyn Db) -> bool { self.node(db).scope_kind().is_function_like() @@ -489,7 +494,7 @@ impl<'db> ScopeId<'db> { /// ID that uniquely identifies a scope inside of a module. #[newtype_index] -#[derive(salsa::Update)] +#[derive(salsa::Update, get_size2::GetSize)] pub struct FileScopeId; impl FileScopeId { @@ -512,7 +517,7 @@ impl FileScopeId { } } -#[derive(Debug, salsa::Update)] +#[derive(Debug, salsa::Update, get_size2::GetSize)] pub struct Scope { parent: Option, node: NodeWithScopeKind, @@ -609,7 +614,7 @@ impl ScopeKind { } /// [`PlaceExpr`] table for a specific [`Scope`]. -#[derive(Default)] +#[derive(Default, get_size2::GetSize)] pub struct PlaceTable { /// The place expressions in this scope. places: IndexVec, @@ -932,7 +937,7 @@ impl NodeWithScopeRef<'_> { } /// Node that introduces a new scope. -#[derive(Clone, Debug, salsa::Update)] +#[derive(Clone, Debug, salsa::Update, get_size2::GetSize)] pub enum NodeWithScopeKind { Module, Class(AstNodeRef), @@ -1011,7 +1016,7 @@ impl NodeWithScopeKind { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)] pub(crate) enum NodeWithScopeKey { Module, Class(NodeKey), diff --git a/crates/ty_python_semantic/src/semantic_index/predicate.rs b/crates/ty_python_semantic/src/semantic_index/predicate.rs index 9259ab72e4..6009914ac4 100644 --- a/crates/ty_python_semantic/src/semantic_index/predicate.rs +++ b/crates/ty_python_semantic/src/semantic_index/predicate.rs @@ -18,7 +18,7 @@ use crate::semantic_index::place::{FileScopeId, ScopeId, ScopedPlaceId}; // A scoped identifier for each `Predicate` in a scope. #[newtype_index] -#[derive(Ord, PartialOrd)] +#[derive(Ord, PartialOrd, get_size2::GetSize)] pub(crate) struct ScopedPredicateId; // A collection of predicates for a given scope. @@ -43,7 +43,7 @@ impl<'db> PredicatesBuilder<'db> { } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) struct Predicate<'db> { pub(crate) node: PredicateNode<'db>, pub(crate) is_positive: bool, @@ -58,7 +58,7 @@ impl Predicate<'_> { } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) enum PredicateNode<'db> { Expression(Expression<'db>), Pattern(PatternPredicate<'db>), @@ -91,6 +91,9 @@ pub(crate) struct PatternPredicate<'db> { count: countme::Count>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for PatternPredicate<'_> {} + impl<'db> PatternPredicate<'db> { pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> { self.file_scope(db).to_scope_id(db, self.file(db)) @@ -155,6 +158,9 @@ pub(crate) struct StarImportPlaceholderPredicate<'db> { pub(crate) referenced_file: File, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for StarImportPlaceholderPredicate<'_> {} + impl<'db> StarImportPlaceholderPredicate<'db> { pub(crate) fn scope(self, db: &'db dyn Db) -> ScopeId<'db> { // See doc-comment above [`StarImportPlaceholderPredicate::symbol_id`]: diff --git a/crates/ty_python_semantic/src/semantic_index/re_exports.rs b/crates/ty_python_semantic/src/semantic_index/re_exports.rs index 275e13d7a7..733ec5d262 100644 --- a/crates/ty_python_semantic/src/semantic_index/re_exports.rs +++ b/crates/ty_python_semantic/src/semantic_index/re_exports.rs @@ -43,7 +43,7 @@ fn exports_cycle_initial(_db: &dyn Db, _file: File) -> Box<[Name]> { Box::default() } -#[salsa::tracked(returns(deref), cycle_fn=exports_cycle_recover, cycle_initial=exports_cycle_initial)] +#[salsa::tracked(returns(deref), cycle_fn=exports_cycle_recover, cycle_initial=exports_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(super) fn exported_names(db: &dyn Db, file: File) -> Box<[Name]> { let module = parsed_module(db.upcast(), file).load(db.upcast()); let mut finder = ExportFinder::new(db, file); diff --git a/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs b/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs index 2cef92945b..e7fd1d6914 100644 --- a/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs +++ b/crates/ty_python_semantic/src/semantic_index/reachability_constraints.rs @@ -226,7 +226,7 @@ use crate::types::{Truthiness, Type, infer_expression_type}; /// /// reachability constraints are normalized, so equivalent constraints are guaranteed to have equal /// IDs. -#[derive(Clone, Copy, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Eq, Hash, PartialEq, get_size2::GetSize)] pub(crate) struct ScopedReachabilityConstraintId(u32); impl std::fmt::Debug for ScopedReachabilityConstraintId { @@ -255,7 +255,7 @@ impl std::fmt::Debug for ScopedReachabilityConstraintId { // _Interior nodes_ provide the TDD structure for the formula. Interior nodes are stored in an // arena Vec, with the constraint ID providing an index into the arena. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] struct InteriorNode { /// A "variable" that is evaluated as part of a TDD ternary function. For reachability /// constraints, this is a `Predicate` that represents some runtime property of the Python @@ -306,7 +306,7 @@ const ALWAYS_FALSE: ScopedReachabilityConstraintId = ScopedReachabilityConstrain const SMALLEST_TERMINAL: ScopedReachabilityConstraintId = ALWAYS_FALSE; /// A collection of reachability constraints for a given scope. -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) struct ReachabilityConstraints { interiors: IndexVec, } diff --git a/crates/ty_python_semantic/src/semantic_index/use_def.rs b/crates/ty_python_semantic/src/semantic_index/use_def.rs index e216d51e60..3e20b8dbf3 100644 --- a/crates/ty_python_semantic/src/semantic_index/use_def.rs +++ b/crates/ty_python_semantic/src/semantic_index/use_def.rs @@ -259,7 +259,7 @@ use crate::types::{IntersectionBuilder, Truthiness, Type, infer_narrowing_constr mod place_state; /// Applicable definitions and constraints for every use of a name. -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) struct UseDefMap<'db> { /// Array of [`Definition`] in this scope. Only the first entry should be [`DefinitionState::Undefined`]; /// this represents the implicit "unbound"/"undeclared" definition of every place. @@ -549,9 +549,10 @@ impl<'db> UseDefMap<'db> { /// /// There is a unique ID for each distinct [`EagerSnapshotKey`] in the file. #[newtype_index] +#[derive(get_size2::GetSize)] pub(crate) struct ScopedEagerSnapshotId; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub(crate) struct EagerSnapshotKey { /// The enclosing scope containing the bindings pub(crate) enclosing_scope: FileScopeId, @@ -680,7 +681,7 @@ impl<'db> Iterator for DeclarationsIterator<'_, 'db> { impl std::iter::FusedIterator for DeclarationsIterator<'_, '_> {} -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] struct ReachableDefinitions { bindings: Bindings, declarations: Declarations, diff --git a/crates/ty_python_semantic/src/semantic_index/use_def/place_state.rs b/crates/ty_python_semantic/src/semantic_index/use_def/place_state.rs index dc10dc7ef2..b3f34577b9 100644 --- a/crates/ty_python_semantic/src/semantic_index/use_def/place_state.rs +++ b/crates/ty_python_semantic/src/semantic_index/use_def/place_state.rs @@ -55,7 +55,7 @@ use crate::semantic_index::reachability_constraints::{ /// A newtype-index for a definition in a particular scope. #[newtype_index] -#[derive(Ord, PartialOrd)] +#[derive(Ord, PartialOrd, get_size2::GetSize)] pub(super) struct ScopedDefinitionId; impl ScopedDefinitionId { @@ -77,14 +77,14 @@ const INLINE_DEFINITIONS_PER_PLACE: usize = 4; /// Live declarations for a single place at some point in control flow, with their /// corresponding reachability constraints. -#[derive(Clone, Debug, Default, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Debug, Default, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct Declarations { /// A list of live declarations for this place, sorted by their `ScopedDefinitionId` live_declarations: SmallVec<[LiveDeclaration; INLINE_DEFINITIONS_PER_PLACE]>, } /// One of the live declarations for a single place at some point in control flow. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub(super) struct LiveDeclaration { pub(super) declaration: ScopedDefinitionId, pub(super) reachability_constraint: ScopedReachabilityConstraintId, @@ -183,7 +183,7 @@ impl Declarations { /// Even if it's a class scope (class variables are not visible to nested scopes) or there are no /// bindings, the current narrowing constraint is necessary for narrowing, so it's stored in /// `Constraint`. -#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) enum EagerSnapshot { Constraint(ScopedNarrowingConstraint), Bindings(Bindings), @@ -191,7 +191,7 @@ pub(super) enum EagerSnapshot { /// Live bindings for a single place at some point in control flow. Each live binding comes /// with a set of narrowing constraints and a reachability constraint. -#[derive(Clone, Debug, Default, PartialEq, Eq, salsa::Update)] +#[derive(Clone, Debug, Default, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct Bindings { /// The narrowing constraint applicable to the "unbound" binding, if we need access to it even /// when it's not visible. This happens in class scopes, where local name bindings are not visible @@ -210,7 +210,7 @@ impl Bindings { } /// One of the live bindings for a single place at some point in control flow. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub(super) struct LiveBinding { pub(super) binding: ScopedDefinitionId, pub(super) narrowing_constraint: ScopedNarrowingConstraint, @@ -338,7 +338,7 @@ impl Bindings { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)] pub(in crate::semantic_index) struct PlaceState { declarations: Declarations, bindings: Bindings, diff --git a/crates/ty_python_semantic/src/suppression.rs b/crates/ty_python_semantic/src/suppression.rs index b5103f6581..823cd1223b 100644 --- a/crates/ty_python_semantic/src/suppression.rs +++ b/crates/ty_python_semantic/src/suppression.rs @@ -86,7 +86,7 @@ declare_lint! { } } -#[salsa::tracked(returns(ref))] +#[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn suppressions(db: &dyn Db, file: File) -> Suppressions { let parsed = parsed_module(db.upcast(), file).load(db.upcast()); let source = source_text(db.upcast(), file); @@ -331,7 +331,7 @@ impl<'a> CheckSuppressionsContext<'a> { } /// The suppressions of a single file. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, get_size2::GetSize)] pub(crate) struct Suppressions { /// Suppressions that apply to the entire file. /// @@ -424,7 +424,7 @@ impl<'a> IntoIterator for &'a Suppressions { /// Suppression comments that suppress multiple codes /// create multiple suppressions: one for every code. /// They all share the same `comment_range`. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)] pub(crate) struct Suppression { target: SuppressionTarget, kind: SuppressionKind, @@ -466,10 +466,10 @@ impl Suppression { /// The wrapped `TextRange` is the suppression's range. /// This is unique enough because it is its exact /// location in the source. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)] pub(crate) struct FileSuppressionId(TextRange); -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)] enum SuppressionTarget { /// Suppress all lints All, @@ -628,7 +628,7 @@ impl<'a> SuppressionsBuilder<'a> { } /// Suppression for an unknown lint rule. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, get_size2::GetSize)] struct UnknownSuppression { /// The range of the code. range: TextRange, @@ -639,7 +639,7 @@ struct UnknownSuppression { reason: GetLintError, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, get_size2::GetSize)] struct InvalidSuppression { kind: SuppressionKind, error: ParseError, @@ -843,7 +843,7 @@ struct SuppressionComment { codes: Option>, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)] enum SuppressionKind { TypeIgnore, Ty, @@ -871,7 +871,7 @@ impl fmt::Display for SuppressionKind { } } -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug, Eq, PartialEq, Clone, get_size2::GetSize)] struct ParseError { kind: ParseErrorKind, @@ -893,7 +893,7 @@ impl fmt::Display for ParseError { impl Error for ParseError {} -#[derive(Debug, Eq, PartialEq, Clone, Error)] +#[derive(Debug, Eq, PartialEq, Clone, Error, get_size2::GetSize)] enum ParseErrorKind { /// The comment isn't a suppression comment. #[error("not a suppression comment")] diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 94c2852790..3c2c475d23 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -85,7 +85,7 @@ mod definition; #[cfg(test)] mod property_tests; -#[salsa::tracked(returns(ref))] +#[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)] pub fn check_types(db: &dyn Db, file: File) -> TypeCheckDiagnostics { let _span = tracing::trace_span!("check_types", ?file).entered(); @@ -160,7 +160,7 @@ fn definition_expression_type<'db>( /// define a `__get__` method, while data descriptors additionally define a `__set__` /// method or a `__delete__` method. This enum is used to categorize attributes into two /// groups: (1) data descriptors and (2) normal attributes or non-data descriptors. -#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub(crate) enum AttributeKind { DataDescriptor, NormalOrNonDataDescriptor, @@ -284,7 +284,7 @@ fn class_lookup_cycle_initial<'db>( /// Meta data for `Type::Todo`, which represents a known limitation in ty. #[cfg(debug_assertions)] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)] pub struct TodoType(pub &'static str); #[cfg(debug_assertions)] @@ -295,7 +295,7 @@ impl std::fmt::Display for TodoType { } #[cfg(not(debug_assertions))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, get_size2::GetSize)] pub struct TodoType; #[cfg(not(debug_assertions))] @@ -370,6 +370,9 @@ pub struct PropertyInstanceType<'db> { setter: Option>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for PropertyInstanceType<'_> {} + impl<'db> PropertyInstanceType<'db> { fn apply_type_mapping<'a>(self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self { let getter = self @@ -423,6 +426,8 @@ bitflags! { } } +impl get_size2::GetSize for DataclassParams {} + impl Default for DataclassParams { fn default() -> Self { Self::INIT | Self::REPR | Self::EQ | Self::MATCH_ARGS @@ -456,7 +461,7 @@ impl From for DataclassParams { /// Representation of a type: a set of possible values at runtime. /// -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub enum Type<'db> { /// The dynamic type: a statically unknown set of values Dynamic(DynamicType), @@ -2483,7 +2488,7 @@ impl<'db> Type<'db> { } } - #[salsa::tracked] + #[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] #[allow(unused_variables)] // If we choose name `_unit`, the macro will generate code that uses `_unit`, causing clippy to fail. fn lookup_dunder_new(self, db: &'db dyn Db, unit: ()) -> Option> { @@ -2504,7 +2509,7 @@ impl<'db> Type<'db> { self.class_member_with_policy(db, name, MemberLookupPolicy::default()) } - #[salsa::tracked(cycle_fn=class_lookup_cycle_recover, cycle_initial=class_lookup_cycle_initial)] + #[salsa::tracked(cycle_fn=class_lookup_cycle_recover, cycle_initial=class_lookup_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] fn class_member_with_policy( self, db: &'db dyn Db, @@ -2657,7 +2662,7 @@ impl<'db> Type<'db> { /// that `self` represents: (1) a data descriptor or (2) a non-data descriptor / normal attribute. /// /// If `__get__` is not defined on the meta-type, this method returns `None`. - #[salsa::tracked] + #[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn try_call_dunder_get( self, db: &'db dyn Db, @@ -2950,7 +2955,7 @@ impl<'db> Type<'db> { /// Similar to [`Type::member`], but allows the caller to specify what policy should be used /// when looking up attributes. See [`MemberLookupPolicy`] for more information. - #[salsa::tracked(cycle_fn=member_lookup_cycle_recover, cycle_initial=member_lookup_cycle_initial)] + #[salsa::tracked(cycle_fn=member_lookup_cycle_recover, cycle_initial=member_lookup_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] fn member_lookup_with_policy( self, db: &'db dyn Db, @@ -5209,7 +5214,7 @@ impl<'db> Type<'db> { /// Note that this does not specialize generic classes, functions, or type aliases! That is a /// different operation that is performed explicitly (via a subscript operation), or implicitly /// via a call to the generic object. - #[salsa::tracked] + #[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub fn apply_specialization( self, db: &'db dyn Db, @@ -5701,7 +5706,9 @@ impl<'db> TypeMapping<'_, 'db> { /// Ordering between variants is stable and should be the same between runs. /// Ordering within variants is based on the wrapped data's salsa-assigned id and not on its values. /// The id may change between runs, or when e.g. a `TypeVarInstance` was garbage-collected and recreated. -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, Ord, PartialOrd)] +#[derive( + Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, Ord, PartialOrd, get_size2::GetSize, +)] pub enum KnownInstanceType<'db> { /// The type of `Protocol[T]`, `Protocol[U, S]`, etc -- usually only found in a class's bases list. /// @@ -5790,7 +5797,7 @@ impl<'db> KnownInstanceType<'db> { } } -#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub enum DynamicType { /// An explicitly annotated `typing.Any` Any, @@ -5848,6 +5855,8 @@ bitflags! { } } +impl get_size2::GetSize for TypeQualifiers {} + /// When inferring the type of an annotation expression, we can also encounter type qualifiers /// such as `ClassVar` or `Final`. These do not affect the inferred type itself, but rather /// control how a particular place can be accessed or modified. This struct holds a type and @@ -5855,7 +5864,7 @@ bitflags! { /// /// Example: `Annotated[ClassVar[tuple[int]], "metadata"]` would have type `tuple[int]` and the /// qualifier `ClassVar`. -#[derive(Clone, Debug, Copy, Eq, PartialEq, salsa::Update)] +#[derive(Clone, Debug, Copy, Eq, PartialEq, salsa::Update, get_size2::GetSize)] pub(crate) struct TypeAndQualifiers<'db> { inner: Type<'db>, qualifiers: TypeQualifiers, @@ -6081,6 +6090,9 @@ pub struct TypeVarInstance<'db> { pub kind: TypeVarKind, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for TypeVarInstance<'_> {} + impl<'db> TypeVarInstance<'db> { pub(crate) fn is_legacy(self, db: &'db dyn Db) -> bool { matches!(self.kind(db), TypeVarKind::Legacy) @@ -7057,6 +7069,9 @@ pub struct BoundMethodType<'db> { self_instance: Type<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for BoundMethodType<'_> {} + impl<'db> BoundMethodType<'db> { pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> Type<'db> { Type::Callable(CallableType::new( @@ -7122,6 +7137,9 @@ pub struct CallableType<'db> { is_function_like: bool, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for CallableType<'_> {} + impl<'db> CallableType<'db> { /// Create a callable type with a single non-overloaded signature. pub(crate) fn single(db: &'db dyn Db, signature: Signature<'db>) -> Type<'db> { @@ -7232,7 +7250,9 @@ impl<'db> CallableType<'db> { } /// Represents a specific instance of `types.MethodWrapperType` -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update)] +#[derive( + Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update, get_size2::GetSize, +)] pub enum MethodWrapperKind<'db> { /// Method wrapper for `some_function.__get__` FunctionTypeDunderGet(FunctionType<'db>), @@ -7337,7 +7357,9 @@ impl<'db> MethodWrapperKind<'db> { } /// Represents a specific instance of `types.WrapperDescriptorType` -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update)] +#[derive( + Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, salsa::Update, get_size2::GetSize, +)] pub enum WrapperDescriptorKind { /// `FunctionType.__get__` FunctionTypeDunderGet, @@ -7363,6 +7385,9 @@ pub struct ModuleLiteralType<'db> { pub module: Module, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for ModuleLiteralType<'_> {} + impl<'db> ModuleLiteralType<'db> { fn static_member(self, db: &'db dyn Db, name: &str) -> Place<'db> { // `__dict__` is a very special member that is never overridden by module globals; @@ -7416,6 +7441,9 @@ pub struct PEP695TypeAliasType<'db> { rhs_scope: ScopeId<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for PEP695TypeAliasType<'_> {} + #[salsa::tracked] impl<'db> PEP695TypeAliasType<'db> { pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> { @@ -7426,7 +7454,7 @@ impl<'db> PEP695TypeAliasType<'db> { semantic_index(db, scope.file(db)).expect_single_definition(type_alias_stmt_node) } - #[salsa::tracked] + #[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn value_type(self, db: &'db dyn Db) -> Type<'db> { let scope = self.rhs_scope(db); let module = parsed_module(db.upcast(), scope.file(db)).load(db.upcast()); @@ -7452,6 +7480,9 @@ pub struct BareTypeAliasType<'db> { pub value: Type<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for BareTypeAliasType<'_> {} + impl<'db> BareTypeAliasType<'db> { fn normalized(self, db: &'db dyn Db) -> Self { Self::new( @@ -7463,7 +7494,9 @@ impl<'db> BareTypeAliasType<'db> { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update, get_size2::GetSize, +)] pub enum TypeAliasType<'db> { PEP695(PEP695TypeAliasType<'db>), Bare(BareTypeAliasType<'db>), @@ -7500,7 +7533,7 @@ impl<'db> TypeAliasType<'db> { } /// Either the explicit `metaclass=` keyword of the class, or the inferred metaclass of one of its base classes. -#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct MetaclassCandidate<'db> { metaclass: ClassType<'db>, explicit_metaclass_of: ClassLiteral<'db>, @@ -7513,6 +7546,9 @@ pub struct UnionType<'db> { pub elements: Box<[Type<'db>]>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for UnionType<'_> {} + impl<'db> UnionType<'db> { /// Create a union from a list of elements /// (which may be eagerly simplified into a different variant of [`Type`] altogether). @@ -7726,6 +7762,9 @@ pub struct IntersectionType<'db> { negative: FxOrderSet>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for IntersectionType<'_> {} + impl<'db> IntersectionType<'db> { /// Return a new `IntersectionType` instance with the positive and negative types sorted /// according to a canonical ordering, and other normalizations applied to each element as applicable. @@ -7895,6 +7934,9 @@ pub struct StringLiteralType<'db> { value: Box, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for StringLiteralType<'_> {} + impl<'db> StringLiteralType<'db> { /// The length of the string, as would be returned by Python's `len()`. pub(crate) fn python_len(self, db: &'db dyn Db) -> usize { @@ -7920,6 +7962,9 @@ pub struct BytesLiteralType<'db> { value: Box<[u8]>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for BytesLiteralType<'_> {} + impl<'db> BytesLiteralType<'db> { pub(crate) fn python_len(self, db: &'db dyn Db) -> usize { self.value(db).len() @@ -8061,6 +8106,9 @@ pub struct BoundSuperType<'db> { pub owner: SuperOwnerKind<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for BoundSuperType<'_> {} + impl<'db> BoundSuperType<'db> { /// Attempts to build a `Type::BoundSuper` based on the given `pivot_class` and `owner`. /// @@ -8239,6 +8287,9 @@ pub struct TypeIsType<'db> { place_info: Option<(ScopeId<'db>, ScopedPlaceId)>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for TypeIsType<'_> {} + impl<'db> TypeIsType<'db> { pub fn place_name(self, db: &'db dyn Db) -> Option { let (scope, place) = self.place_info(db)?; diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index e479bd020b..d5ef1b9fea 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -178,6 +178,9 @@ pub struct GenericAlias<'db> { pub(crate) specialization: Specialization<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for GenericAlias<'_> {} + impl<'db> GenericAlias<'db> { pub(super) fn normalized(self, db: &'db dyn Db) -> Self { Self::new(db, self.origin(db), self.specialization(db).normalized(db)) @@ -227,7 +230,17 @@ impl<'db> From> for Type<'db> { /// Represents a class type, which might be a non-generic class, or a specialization of a generic /// class. #[derive( - Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, salsa::Supertype, salsa::Update, + Clone, + Copy, + Debug, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + salsa::Supertype, + salsa::Update, + get_size2::GetSize, )] pub enum ClassType<'db> { NonGeneric(ClassLiteral<'db>), @@ -749,6 +762,9 @@ pub struct ClassLiteral<'db> { pub(crate) dataclass_transformer_params: Option, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for ClassLiteral<'_> {} + #[expect(clippy::trivially_copy_pass_by_ref, clippy::ref_option)] fn pep695_generic_context_cycle_recover<'db>( _db: &'db dyn Db, @@ -795,7 +811,7 @@ impl<'db> ClassLiteral<'db> { self.pep695_generic_context(db).is_some() } - #[salsa::tracked(cycle_fn=pep695_generic_context_cycle_recover, cycle_initial=pep695_generic_context_cycle_initial)] + #[salsa::tracked(cycle_fn=pep695_generic_context_cycle_recover, cycle_initial=pep695_generic_context_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn pep695_generic_context(self, db: &'db dyn Db) -> Option> { let scope = self.body_scope(db); let parsed = parsed_module(db.upcast(), scope.file(db)).load(db.upcast()); @@ -905,7 +921,7 @@ impl<'db> ClassLiteral<'db> { /// /// Were this not a salsa query, then the calling query /// would depend on the class's AST and rerun for every change in that file. - #[salsa::tracked(returns(deref), cycle_fn=explicit_bases_cycle_recover, cycle_initial=explicit_bases_cycle_initial)] + #[salsa::tracked(returns(deref), cycle_fn=explicit_bases_cycle_recover, cycle_initial=explicit_bases_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(super) fn explicit_bases(self, db: &'db dyn Db) -> Box<[Type<'db>]> { tracing::trace!("ClassLiteral::explicit_bases_query: {}", self.name(db)); @@ -981,7 +997,7 @@ impl<'db> ClassLiteral<'db> { } /// Return the types of the decorators on this class - #[salsa::tracked(returns(deref))] + #[salsa::tracked(returns(deref), heap_size=get_size2::GetSize::get_heap_size)] fn decorators(self, db: &'db dyn Db) -> Box<[Type<'db>]> { tracing::trace!("ClassLiteral::decorators: {}", self.name(db)); @@ -1029,7 +1045,7 @@ impl<'db> ClassLiteral<'db> { /// attribute on a class at runtime. /// /// [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order - #[salsa::tracked(returns(as_ref), cycle_fn=try_mro_cycle_recover, cycle_initial=try_mro_cycle_initial)] + #[salsa::tracked(returns(as_ref), cycle_fn=try_mro_cycle_recover, cycle_initial=try_mro_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(super) fn try_mro( self, db: &'db dyn Db, @@ -1109,6 +1125,7 @@ impl<'db> ClassLiteral<'db> { #[salsa::tracked( cycle_fn=try_metaclass_cycle_recover, cycle_initial=try_metaclass_cycle_initial, + heap_size=get_size2::GetSize::get_heap_size, )] pub(super) fn try_metaclass( self, @@ -2097,7 +2114,7 @@ impl<'db> ClassLiteral<'db> { /// /// A class definition like this will fail at runtime, /// but we must be resilient to it or we could panic. - #[salsa::tracked(cycle_fn=inheritance_cycle_recover, cycle_initial=inheritance_cycle_initial)] + #[salsa::tracked(cycle_fn=inheritance_cycle_recover, cycle_initial=inheritance_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(super) fn inheritance_cycle(self, db: &'db dyn Db) -> Option { /// Return `true` if the class is cyclically defined. /// @@ -2181,7 +2198,7 @@ impl<'db> From> for Type<'db> { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, get_size2::GetSize)] pub(super) enum InheritanceCycle { /// The class is cyclically defined and is a participant in the cycle. /// i.e., it inherits either directly or indirectly from itself. @@ -3554,7 +3571,7 @@ impl<'db> Type<'db> { } } -#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct MetaclassError<'db> { kind: MetaclassErrorKind<'db>, } @@ -3566,7 +3583,7 @@ impl<'db> MetaclassError<'db> { } } -#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) enum MetaclassErrorKind<'db> { /// The class has incompatible metaclasses in its inheritance hierarchy. /// diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index 8a9d685693..d738edfaf6 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -13,7 +13,7 @@ use crate::types::{ /// Note that a non-specialized generic class _cannot_ be a class base. When we see a /// non-specialized generic class in any type expression (including the list of base classes), we /// automatically construct the default specialization for that class. -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub enum ClassBase<'db> { Dynamic(DynamicType), Class(ClassType<'db>), diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 683b20f9a0..5e0ba48b79 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -1561,7 +1561,7 @@ declare_lint! { } /// A collection of type check diagnostics. -#[derive(Default, Eq, PartialEq)] +#[derive(Default, Eq, PartialEq, get_size2::GetSize)] pub struct TypeCheckDiagnostics { diagnostics: Vec, used_suppressions: FxHashSet, diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index 504f84dbd9..523a09123b 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -131,6 +131,8 @@ bitflags! { } } +impl get_size2::GetSize for DataclassTransformerParams {} + impl Default for DataclassTransformerParams { fn default() -> Self { Self::EQ_DEFAULT @@ -168,6 +170,9 @@ pub struct OverloadLiteral<'db> { pub(crate) dataclass_transformer_params: Option, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for OverloadLiteral<'_> {} + #[salsa::tracked] impl<'db> OverloadLiteral<'db> { fn with_dataclass_transformer_params( @@ -432,7 +437,7 @@ impl<'db> FunctionLiteral<'db> { self.last_definition(db).spans(db) } - #[salsa::tracked(returns(ref))] + #[salsa::tracked(returns(ref), heap_size=get_size2::GetSize::get_heap_size)] fn overloads_and_implementation( self, db: &'db dyn Db, @@ -530,6 +535,9 @@ pub struct FunctionType<'db> { type_mappings: Box<[TypeMapping<'db, 'db>]>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for FunctionType<'_> {} + #[salsa::tracked] impl<'db> FunctionType<'db> { pub(crate) fn with_inherited_generic_context( @@ -699,7 +707,7 @@ impl<'db> FunctionType<'db> { /// /// Were this not a salsa query, then the calling query /// would depend on the function's AST and rerun for every change in that file. - #[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)] + #[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> { self.literal(db).signature(db, self.type_mappings(db)) } diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 32c844dc72..983b5caf7d 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -30,6 +30,9 @@ pub struct GenericContext<'db> { pub(crate) variables: FxOrderSet>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for GenericContext<'_> {} + impl<'db> GenericContext<'db> { /// Creates a generic context from a list of PEP-695 type parameters. pub(crate) fn from_type_params( diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 7eef066796..f464325d02 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -129,7 +129,7 @@ use super::{ClassBase, NominalInstanceType, add_inferred_python_version_hint_to_ /// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope. /// Use when checking a scope, or needing to provide a type for an arbitrary expression in the /// scope. -#[salsa::tracked(returns(ref), cycle_fn=scope_cycle_recover, cycle_initial=scope_cycle_initial)] +#[salsa::tracked(returns(ref), cycle_fn=scope_cycle_recover, cycle_initial=scope_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn infer_scope_types<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> TypeInference<'db> { let file = scope.file(db); let _span = tracing::trace_span!("infer_scope_types", scope=?scope.as_id(), ?file).entered(); @@ -158,7 +158,7 @@ fn scope_cycle_initial<'db>(_db: &'db dyn Db, scope: ScopeId<'db>) -> TypeInfere /// Infer all types for a [`Definition`] (including sub-expressions). /// Use when resolving a place use or public type of a place. -#[salsa::tracked(returns(ref), cycle_fn=definition_cycle_recover, cycle_initial=definition_cycle_initial)] +#[salsa::tracked(returns(ref), cycle_fn=definition_cycle_recover, cycle_initial=definition_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn infer_definition_types<'db>( db: &'db dyn Db, definition: Definition<'db>, @@ -197,7 +197,7 @@ fn definition_cycle_initial<'db>( /// /// Deferred expressions are type expressions (annotations, base classes, aliases...) in a stub /// file, or in a file with `from __future__ import annotations`, or stringified annotations. -#[salsa::tracked(returns(ref), cycle_fn=deferred_cycle_recover, cycle_initial=deferred_cycle_initial)] +#[salsa::tracked(returns(ref), cycle_fn=deferred_cycle_recover, cycle_initial=deferred_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn infer_deferred_types<'db>( db: &'db dyn Db, definition: Definition<'db>, @@ -234,7 +234,7 @@ fn deferred_cycle_initial<'db>(db: &'db dyn Db, definition: Definition<'db>) -> /// Use rarely; only for cases where we'd otherwise risk double-inferring an expression: RHS of an /// assignment, which might be unpacking/multi-target and thus part of multiple definitions, or a /// type narrowing guard expression (e.g. if statement test node). -#[salsa::tracked(returns(ref), cycle_fn=expression_cycle_recover, cycle_initial=expression_cycle_initial)] +#[salsa::tracked(returns(ref), cycle_fn=expression_cycle_recover, cycle_initial=expression_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn infer_expression_types<'db>( db: &'db dyn Db, expression: Expression<'db>, @@ -296,7 +296,7 @@ pub(super) fn infer_same_file_expression_type<'db>( /// /// Use [`infer_same_file_expression_type`] if it is guaranteed that `expression` is in the same /// to avoid unnecessary salsa ingredients. This is normally the case inside the `TypeInferenceBuilder`. -#[salsa::tracked(cycle_fn=single_expression_cycle_recover, cycle_initial=single_expression_cycle_initial)] +#[salsa::tracked(cycle_fn=single_expression_cycle_recover, cycle_initial=single_expression_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(crate) fn infer_expression_type<'db>( db: &'db dyn Db, expression: Expression<'db>, @@ -330,7 +330,7 @@ fn single_expression_cycle_initial<'db>( /// involved in an unpacking operation. It returns a result-like object that can be used to get the /// type of the variables involved in this unpacking along with any violations that are detected /// during this unpacking. -#[salsa::tracked(returns(ref), cycle_fn=unpack_cycle_recover, cycle_initial=unpack_cycle_initial)] +#[salsa::tracked(returns(ref), cycle_fn=unpack_cycle_recover, cycle_initial=unpack_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] pub(super) fn infer_unpack_types<'db>(db: &'db dyn Db, unpack: Unpack<'db>) -> UnpackResult<'db> { let file = unpack.file(db); let module = parsed_module(db.upcast(), file).load(db.upcast()); @@ -414,7 +414,7 @@ struct TypeAndRange<'db> { } /// The inferred types for a single region. -#[derive(Debug, Eq, PartialEq, salsa::Update)] +#[derive(Debug, Eq, PartialEq, salsa::Update, get_size2::GetSize)] pub(crate) struct TypeInference<'db> { /// The types of every expression in this region. expressions: FxHashMap>, diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index 2ad17b2949..957bb91387 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -64,7 +64,7 @@ impl<'db> Type<'db> { } /// A type representing the set of runtime objects which are instances of a certain nominal class. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, get_size2::GetSize)] pub struct NominalInstanceType<'db> { pub(super) class: ClassType<'db>, @@ -147,7 +147,9 @@ impl<'db> From> for Type<'db> { /// A `ProtocolInstanceType` represents the set of all possible runtime objects /// that conform to the interface described by a certain protocol. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord)] +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize, +)] pub struct ProtocolInstanceType<'db> { pub(super) inner: Protocol<'db>, @@ -324,7 +326,9 @@ impl<'db> ProtocolInstanceType<'db> { /// An enumeration of the two kinds of protocol types: those that originate from a class /// definition in source code, and those that are synthesized from a set of members. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord)] +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize, +)] pub(super) enum Protocol<'db> { FromClass(ClassType<'db>), Synthesized(SynthesizedProtocolType<'db>), @@ -359,7 +363,9 @@ mod synthesized_protocol { /// /// The constructor method of this type maintains the invariant that a synthesized protocol type /// is always constructed from a *normalized* protocol interface. - #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord)] + #[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize, + )] pub(in crate::types) struct SynthesizedProtocolType<'db>(ProtocolInterface<'db>); impl<'db> SynthesizedProtocolType<'db> { diff --git a/crates/ty_python_semantic/src/types/mro.rs b/crates/ty_python_semantic/src/types/mro.rs index 4b45888524..632a513b7b 100644 --- a/crates/ty_python_semantic/src/types/mro.rs +++ b/crates/ty_python_semantic/src/types/mro.rs @@ -28,7 +28,7 @@ use crate::types::{ClassLiteral, ClassType, KnownInstanceType, SpecialFormType, /// ``` /// /// See [`ClassType::iter_mro`] for more details. -#[derive(PartialEq, Eq, Clone, Debug, salsa::Update)] +#[derive(PartialEq, Eq, Clone, Debug, salsa::Update, get_size2::GetSize)] pub(super) struct Mro<'db>(Box<[ClassBase<'db>]>); impl<'db> Mro<'db> { @@ -413,7 +413,7 @@ impl<'db> Iterator for MroIterator<'db> { impl std::iter::FusedIterator for MroIterator<'_> {} -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct MroError<'db> { kind: MroErrorKind<'db>, fallback_mro: Mro<'db>, @@ -442,7 +442,7 @@ impl<'db> MroError<'db> { } /// Possible ways in which attempting to resolve the MRO of a class might fail. -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) enum MroErrorKind<'db> { /// The class inherits from one or more invalid bases. /// @@ -483,7 +483,7 @@ impl<'db> MroErrorKind<'db> { } /// Error recording the fact that a class definition was found to have duplicate bases. -#[derive(Debug, PartialEq, Eq, salsa::Update)] +#[derive(Debug, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(super) struct DuplicateBaseError<'db> { /// The base that is duplicated in the class's bases list. pub(super) duplicate_base: ClassBase<'db>, diff --git a/crates/ty_python_semantic/src/types/narrow.rs b/crates/ty_python_semantic/src/types/narrow.rs index ca03a38f41..dc3dbd68ac 100644 --- a/crates/ty_python_semantic/src/types/narrow.rs +++ b/crates/ty_python_semantic/src/types/narrow.rs @@ -69,7 +69,7 @@ pub(crate) fn infer_narrowing_constraint<'db>( } } -#[salsa::tracked(returns(as_ref))] +#[salsa::tracked(returns(as_ref), heap_size=get_size2::GetSize::get_heap_size)] fn all_narrowing_constraints_for_pattern<'db>( db: &'db dyn Db, pattern: PatternPredicate<'db>, @@ -82,6 +82,7 @@ fn all_narrowing_constraints_for_pattern<'db>( returns(as_ref), cycle_fn=constraints_for_expression_cycle_recover, cycle_initial=constraints_for_expression_cycle_initial, + heap_size=get_size2::GetSize::get_heap_size, )] fn all_narrowing_constraints_for_expression<'db>( db: &'db dyn Db, @@ -96,6 +97,7 @@ fn all_narrowing_constraints_for_expression<'db>( returns(as_ref), cycle_fn=negative_constraints_for_expression_cycle_recover, cycle_initial=negative_constraints_for_expression_cycle_initial, + heap_size=get_size2::GetSize::get_heap_size, )] fn all_negative_narrowing_constraints_for_expression<'db>( db: &'db dyn Db, @@ -106,7 +108,7 @@ fn all_negative_narrowing_constraints_for_expression<'db>( .finish() } -#[salsa::tracked(returns(as_ref))] +#[salsa::tracked(returns(as_ref), heap_size=get_size2::GetSize::get_heap_size)] fn all_negative_narrowing_constraints_for_pattern<'db>( db: &'db dyn Db, pattern: PatternPredicate<'db>, diff --git a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs index 6643ca884e..1f5a69f88d 100644 --- a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs +++ b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs @@ -106,7 +106,7 @@ enum ParamKind { KeywordVariadic, } -#[salsa::tracked] +#[salsa::tracked(heap_size=get_size2::GetSize::get_heap_size)] fn create_bound_method<'db>( db: &'db dyn Db, function: 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 db8d344d50..2c4b167072 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -70,8 +70,12 @@ pub(super) struct ProtocolInterfaceMembers<'db> { inner: BTreeMap>, } +impl get_size2::GetSize for ProtocolInterfaceMembers<'_> {} + /// The interface of a protocol: the members of that protocol, and the types of those members. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord)] +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, salsa::Update, PartialOrd, Ord, get_size2::GetSize, +)] pub(super) enum ProtocolInterface<'db> { Members(ProtocolInterfaceMembers<'db>), SelfReference, @@ -327,7 +331,7 @@ fn excluded_from_proto_members(member: &str) -> bool { } /// Inner Salsa query for [`ProtocolClassLiteral::interface`]. -#[salsa::tracked(cycle_fn=proto_interface_cycle_recover, cycle_initial=proto_interface_cycle_initial)] +#[salsa::tracked(cycle_fn=proto_interface_cycle_recover, cycle_initial=proto_interface_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)] fn cached_protocol_interface<'db>( db: &'db dyn Db, class: ClassLiteral<'db>, diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 473781f18d..ce8bda10c8 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -24,7 +24,7 @@ use ruff_python_ast::{self as ast, name::Name}; /// The signature of a single callable. If the callable is overloaded, there is a separate /// [`Signature`] for each overload. -#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub struct CallableSignature<'db> { /// The signatures of each overload of this callable. Will be empty if the type is not /// callable. @@ -220,7 +220,7 @@ impl<'a, 'db> IntoIterator for &'a CallableSignature<'db> { } /// The signature of one of the overloads of a callable. -#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub struct Signature<'db> { /// The generic context for this overload, if it is generic. pub(crate) generic_context: Option>, @@ -897,7 +897,7 @@ impl<'db> Signature<'db> { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub(crate) struct Parameters<'db> { // TODO: use SmallVec here once invariance bug is fixed value: Vec>, @@ -1196,7 +1196,7 @@ impl<'db> std::ops::Index for Parameters<'db> { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub(crate) struct Parameter<'db> { /// Annotated type of the parameter. annotated_type: Option>, @@ -1454,7 +1454,7 @@ impl<'db> Parameter<'db> { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub(crate) enum ParameterKind<'db> { /// Positional-only parameter, e.g. `def f(x, /): ...` PositionalOnly { @@ -1520,7 +1520,7 @@ impl<'db> ParameterKind<'db> { } /// Whether a parameter is used as a value or a type form. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub(crate) enum ParameterForm { Value, Type, diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index be8995018d..f960f3058f 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -24,6 +24,7 @@ use std::str::FromStr; PartialOrd, Ord, strum_macros::EnumString, + get_size2::GetSize, )] pub enum SpecialFormType { /// The symbol `typing.Annotated` (which can also be found as `typing_extensions.Annotated`) diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index 0351f27cd3..bb16be2a9d 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -10,7 +10,7 @@ use crate::{Db, FxOrderSet}; use super::{TypeVarBoundOrConstraints, TypeVarKind, TypeVarVariance}; /// A type that represents `type[C]`, i.e. the class object `C` and class objects that are subclasses of `C`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub struct SubclassOfType<'db> { // Keep this field private, so that the only way of constructing the struct is through the `from` method. subclass_of: SubclassOfInner<'db>, @@ -199,7 +199,7 @@ impl<'db> SubclassOfType<'db> { /// Note that this enum is similar to the [`super::ClassBase`] enum, /// but does not include the `ClassBase::Protocol` and `ClassBase::Generic` variants /// (`type[Protocol]` and `type[Generic]` are not valid types). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)] pub(crate) enum SubclassOfInner<'db> { Class(ClassType<'db>), Dynamic(DynamicType), diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index b26730334b..48d6a7d536 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -33,6 +33,9 @@ pub struct TupleType<'db> { pub(crate) tuple: TupleSpec<'db>, } +// The Salsa heap is tracked separately. +impl get_size2::GetSize for TupleType<'_> {} + impl<'db> Type<'db> { pub(crate) fn tuple(db: &'db dyn Db, tuple: TupleType<'db>) -> Self { // If a fixed-length (i.e., mandatory) element of the tuple is `Never`, then it's not diff --git a/crates/ty_python_semantic/src/types/unpacker.rs b/crates/ty_python_semantic/src/types/unpacker.rs index f963fb0255..3674e55647 100644 --- a/crates/ty_python_semantic/src/types/unpacker.rs +++ b/crates/ty_python_semantic/src/types/unpacker.rs @@ -343,7 +343,7 @@ impl<'db, 'ast> Unpacker<'db, 'ast> { } } -#[derive(Debug, Default, PartialEq, Eq, salsa::Update)] +#[derive(Debug, Default, PartialEq, Eq, salsa::Update, get_size2::GetSize)] pub(crate) struct UnpackResult<'db> { targets: FxHashMap>, diagnostics: TypeCheckDiagnostics, diff --git a/crates/ty_python_semantic/src/util/get_size.rs b/crates/ty_python_semantic/src/util/get_size.rs new file mode 100644 index 0000000000..d77cf5c14c --- /dev/null +++ b/crates/ty_python_semantic/src/util/get_size.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use get_size2::GetSize; + +/// By default, `Arc: GetSize` requires `T: 'static` to enable tracking references +/// of the `Arc` and avoid double-counting. This method opts out of that behavior and +/// removes the `'static` requirement. +/// +/// This method will just return the heap-size of the inner `T`. +pub(crate) fn untracked_arc_size(arc: &Arc) -> usize +where + T: GetSize, +{ + T::get_heap_size(&**arc) +} diff --git a/crates/ty_python_semantic/src/util/mod.rs b/crates/ty_python_semantic/src/util/mod.rs index 54555cd617..5ba24ddaac 100644 --- a/crates/ty_python_semantic/src/util/mod.rs +++ b/crates/ty_python_semantic/src/util/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod diagnostics; +pub(crate) mod get_size; pub(crate) mod subscript; diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index e5b2572349..0df27e82b9 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -30,7 +30,7 @@ ty_python_semantic = { path = "../crates/ty_python_semantic" } ty_vendored = { path = "../crates/ty_vendored" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer", default-features = false } -salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "09627e450566f894956710a3fd923dc80462ae6d" } +salsa = { git = "https://github.com/salsa-rs/salsa", rev = "0666e2018bc35376b1ac4f98906f2d04d11e5fe4" } similar = { version = "2.5.0" } tracing = { version = "0.1.40" }