[ty] Track heap usage of salsa structs (#19790)

Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
Ibraheem Ahmed 2025-08-12 07:28:44 -04:00 committed by GitHub
parent 6a05d46ef6
commit f34b65b7a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 269 additions and 124 deletions

1
Cargo.lock generated
View file

@ -3073,6 +3073,7 @@ name = "ruff_memory_usage"
version = "0.0.0"
dependencies = [
"get-size2",
"ordermap",
]
[[package]]

View file

@ -9,7 +9,7 @@ use crate::system::file_time_now;
/// * The last modification time of the file.
/// * The hash of the file's content.
/// * The revision as it comes from an external system, for example the LSP.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
pub struct FileRevision(u128);
impl FileRevision {

View file

@ -289,7 +289,7 @@ impl std::panic::RefUnwindSafe for Files {}
/// # Ordering
/// Ordering is based on the file's salsa-assigned id and not on its values.
/// The id may change between runs.
#[salsa::input]
#[salsa::input(heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct File {
/// The path of the file (immutable).
@ -521,7 +521,7 @@ impl VirtualFile {
// The types in here need to be public because they're salsa ingredients but we
// don't want them to be publicly accessible. That's why we put them into a private module.
mod private {
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
pub enum FileStatus {
/// The file exists.
#[default]

View file

@ -16,7 +16,7 @@ use crate::system::{SystemPath, SystemPathBuf};
/// The main usage of file roots is to determine a file's durability. But it can also be used
/// to make a salsa query dependent on whether a file in a root has changed without writing any
/// manual invalidation logic.
#[salsa::input(debug)]
#[salsa::input(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct FileRoot {
/// The path of a root is guaranteed to never change.
#[returns(deref)]
@ -37,7 +37,7 @@ impl FileRoot {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
pub enum FileRootKind {
/// The root of a project.
Project,

View file

@ -11,7 +11,7 @@ use std::fmt::{Display, Formatter};
/// * a file stored on the [host system](crate::system::System).
/// * a virtual file stored on the [host system](crate::system::System).
/// * a vendored file stored in the [vendored file system](crate::vendored::VendoredFileSystem).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
pub enum FilePath {
/// Path to a file on the [host system](crate::system::System).
System(SystemPathBuf),

View file

@ -762,7 +762,7 @@ impl SystemVirtualPath {
}
/// An owned, virtual path on [`System`](`super::System`) (akin to [`String`]).
#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord)]
#[derive(Eq, PartialEq, Clone, Hash, PartialOrd, Ord, get_size2::GetSize)]
pub struct SystemVirtualPathBuf(String);
impl SystemVirtualPathBuf {

View file

@ -12,6 +12,7 @@ license = { workspace = true }
[dependencies]
get-size2 = { workspace = true }
ordermap = { workspace = true }
[lints]
workspace = true

View file

@ -1,6 +1,7 @@
use std::sync::{LazyLock, Mutex};
use get_size2::{GetSize, StandardTracker};
use ordermap::OrderSet;
/// Returns the memory usage of the provided object, using a global tracker to avoid
/// double-counting shared objects.
@ -12,3 +13,8 @@ pub fn heap_size<T: GetSize>(value: &T) -> usize {
.get_heap_size_with_tracker(&mut *TRACKER.lock().unwrap())
.0
}
/// An implementation of [`GetSize::get_heap_size`] for [`OrderSet`].
pub fn order_set_heap_size<T: GetSize, S>(set: &OrderSet<T, S>) -> usize {
(set.capacity() * T::get_stack_size()) + set.iter().map(heap_size).sum::<usize>()
}

View file

@ -128,18 +128,49 @@ impl ProjectDatabase {
/// to the CLI after a typechecker run.
pub fn salsa_memory_dump(&self) -> SalsaMemoryDump {
let memory_usage = <dyn salsa::Database>::memory_usage(self);
let mut ingredients = memory_usage.structs;
let mut memos = memory_usage.queries.into_iter().collect::<Vec<_>>();
let mut ingredients = memory_usage
.structs
.into_iter()
.filter(|ingredient| ingredient.count() > 0)
.collect::<Vec<_>>();
let mut memos = memory_usage
.queries
.into_iter()
.filter(|(_, memos)| memos.count() > 0)
.collect::<Vec<_>>();
ingredients.sort_by_key(|ingredient| cmp::Reverse(ingredient.size_of_fields()));
memos.sort_by_key(|(_, memo)| cmp::Reverse(memo.size_of_fields()));
ingredients.sort_by_key(|ingredient| {
let heap_size = ingredient.heap_size_of_fields().unwrap_or_else(|| {
// Salsa currently does not expose a way to track the heap size of interned
// query arguments.
if !ingredient.debug_name().contains("interned_arguments") {
tracing::warn!(
"expected `heap_size` to be provided by Salsa struct `{}`",
ingredient.debug_name()
);
}
0
});
cmp::Reverse(ingredient.size_of_fields() + heap_size)
});
memos.sort_by_key(|(query, memo)| {
let heap_size = memo.heap_size_of_fields().unwrap_or_else(|| {
tracing::warn!("expected `heap_size` to be provided by Salsa query `{query}`");
0
});
cmp::Reverse(memo.size_of_fields() + heap_size)
});
let mut total_fields = 0;
let mut total_metadata = 0;
for ingredient in &ingredients {
total_metadata += ingredient.size_of_metadata();
total_fields += ingredient.size_of_fields();
total_fields += ingredient.heap_size_of_fields().unwrap_or(0);
total_metadata += ingredient.size_of_metadata();
}
let mut total_memo_fields = 0;
@ -171,7 +202,7 @@ impl std::fmt::Debug for ProjectDatabase {
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, get_size2::GetSize)]
#[cfg_attr(test, derive(serde::Serialize))]
pub enum CheckMode {
/// Checks the open files in the project.
@ -281,12 +312,15 @@ impl SalsaMemoryDump {
writeln!(f, "=======SALSA STRUCTS=======")?;
for ingredient in ingredients {
let size_of_fields =
ingredient.size_of_fields() + ingredient.heap_size_of_fields().unwrap_or(0);
writeln!(
f,
"{:<50} metadata={:<8} fields={:<8} count={}",
format!("`{}`", ingredient.debug_name()),
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_metadata())),
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_fields())),
format!("{:.2}MB", bytes_to_mb(size_of_fields)),
ingredient.count()
)?;
}
@ -294,13 +328,16 @@ impl SalsaMemoryDump {
writeln!(f, "=======SALSA QUERIES=======")?;
for (query_fn, memo) in memos {
let size_of_fields =
memo.size_of_fields() + memo.heap_size_of_fields().unwrap_or(0);
writeln!(f, "`{query_fn} -> {}`", memo.debug_name())?;
writeln!(
f,
" metadata={:<8} fields={:<8} count={}",
format!("{:.2}MB", bytes_to_mb(memo.size_of_metadata())),
format!("{:.2}MB", bytes_to_mb(memo.size_of_fields())),
format!("{:.2}MB", bytes_to_mb(size_of_fields)),
memo.count()
)?;
}

View file

@ -20,7 +20,7 @@ use crate::{IOErrorDiagnostic, Project};
/// so no query can be depending on the contents of the indexed files before that. All subsequent mutations to
/// the indexed files must go through `IndexedMut`, which uses the Salsa setter `project.set_file_set` to
/// ensure that Salsa always knows when the set of indexed files have changed.
#[derive(Debug)]
#[derive(Debug, get_size2::GetSize)]
pub struct IndexedFiles {
state: std::sync::Mutex<State>,
}
@ -102,7 +102,7 @@ impl Default for IndexedFiles {
}
}
#[derive(Debug)]
#[derive(Debug, get_size2::GetSize)]
enum State {
/// The files of a package haven't been indexed yet.
Lazy,
@ -150,7 +150,7 @@ pub struct Indexed<'db> {
_lifetime: PhantomData<&'db ()>,
}
#[derive(Debug)]
#[derive(Debug, get_size2::GetSize)]
struct IndexedInner {
files: FxHashSet<File>,
diagnostics: Vec<IOErrorDiagnostic>,

View file

@ -13,7 +13,7 @@ mod portable;
/// Path filtering based on an an exclude and include glob pattern set.
///
/// Exclude patterns take precedence over includes.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
pub struct IncludeExcludeFilter {
include: IncludeFilter,
exclude: ExcludeFilter,

View file

@ -23,7 +23,7 @@ use crate::glob::portable::AbsolutePortableGlobPattern;
/// Two filters are equal if they're constructed from the same patterns (including order).
/// Two filters that exclude the exact same files but were constructed from different patterns aren't considered
/// equal.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
pub(crate) struct ExcludeFilter {
ignore: Gitignore,
}
@ -118,10 +118,12 @@ impl ExcludeFilterBuilder {
/// Two ignore matches are only equal if they're constructed from the same patterns (including order).
/// Two matchers that were constructed from different patterns but result in
/// including the same files don't compare equal.
#[derive(Clone)]
#[derive(Clone, get_size2::GetSize)]
struct Gitignore {
#[get_size(ignore)]
set: GlobSet,
globs: Vec<IgnoreGlob>,
#[get_size(ignore)]
matches: Option<Arc<Pool<Vec<usize>>>>,
}
@ -185,7 +187,7 @@ enum Match {
Allow,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
struct IgnoreGlob {
/// The pattern that was originally parsed.
original: String,

View file

@ -29,13 +29,20 @@ const DFA_SIZE_LIMIT: usize = 1_000_000;
///
/// Because of that, two filters that include the exact same files but were
/// constructed from different patterns (or even just order) compare unequal.
#[derive(Clone)]
#[derive(Clone, get_size2::GetSize)]
pub(crate) struct IncludeFilter {
#[get_size(ignore)]
glob_set: GlobSet,
original_patterns: Box<[String]>,
#[get_size(size_fn = dfa_memory_usage)]
dfa: Option<dfa::dense::DFA<Vec<u32>>>,
}
#[allow(clippy::ref_option)]
fn dfa_memory_usage(dfa: &Option<dfa::dense::DFA<Vec<u32>>>) -> usize {
dfa.as_ref().map(dfa::dense::DFA::memory_usage).unwrap_or(0)
}
impl IncludeFilter {
/// Whether the file matches any of the globs.
pub(crate) fn match_file(&self, path: impl AsRef<SystemPath>) -> bool {

View file

@ -55,7 +55,7 @@ pub fn default_lints_registry() -> LintRegistry {
/// 2. Running `ruff check` with different target versions results in different programs (settings) but
/// it remains the same project. That's why program is a narrowed view of the project only
/// holding on to the most fundamental settings required for checking.
#[salsa::input]
#[salsa::input(heap_size=ruff_memory_usage::heap_size)]
#[derive(Debug)]
pub struct Project {
/// The files that are open in the project, [`None`] if there are no open files.
@ -636,7 +636,7 @@ impl Iterator for ProjectFilesIter<'_> {
impl FusedIterator for ProjectFilesIter<'_> {}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, get_size2::GetSize)]
pub struct IOErrorDiagnostic {
file: Option<File>,
error: IOErrorKind,
@ -652,7 +652,7 @@ impl IOErrorDiagnostic {
}
}
#[derive(Error, Debug, Clone)]
#[derive(Error, Debug, Clone, get_size2::GetSize)]
enum IOErrorKind {
#[error(transparent)]
Walk(#[from] walk::WalkError),

View file

@ -19,7 +19,7 @@ pub mod pyproject;
pub mod settings;
pub mod value;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, get_size2::GetSize)]
#[cfg_attr(test, derive(serde::Serialize))]
pub struct ProjectMetadata {
pub(super) name: Name,

View file

@ -36,7 +36,16 @@ use ty_python_semantic::{
};
#[derive(
Debug, Default, Clone, PartialEq, Eq, Combine, Serialize, Deserialize, OptionsMetadata,
Debug,
Default,
Clone,
PartialEq,
Eq,
Combine,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -395,7 +404,16 @@ impl Options {
}
#[derive(
Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata,
Debug,
Default,
Clone,
Eq,
PartialEq,
Combine,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -516,7 +534,16 @@ pub struct EnvironmentOptions {
}
#[derive(
Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata,
Debug,
Default,
Clone,
Eq,
PartialEq,
Combine,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -679,11 +706,14 @@ impl SrcOptions {
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, Hash)]
#[derive(
Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, Hash, get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", transparent)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Rules {
#[cfg_attr(feature = "schemars", schemars(with = "schema::Rules"))]
#[get_size(ignore)] // TODO: Add `GetSize` support for `OrderMap`.
inner: OrderMap<RangedValue<String>, RangedValue<Level>, BuildHasherDefault<FxHasher>>,
}
@ -1002,7 +1032,9 @@ impl GlobFilterContext {
}
/// The diagnostic output format.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[derive(
Debug, Default, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum OutputFormat {
@ -1035,7 +1067,16 @@ impl From<OutputFormat> for DiagnosticFormat {
}
#[derive(
Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata,
Debug,
Default,
Clone,
Eq,
PartialEq,
Combine,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -1095,7 +1136,18 @@ pub struct TerminalOptions {
/// [tool.ty.overrides.rules]
/// possibly-unresolved-reference = "ignore"
/// ```
#[derive(Debug, Default, Clone, PartialEq, Eq, Combine, Serialize, Deserialize, RustDoc)]
#[derive(
Debug,
Default,
Clone,
PartialEq,
Eq,
Combine,
Serialize,
Deserialize,
RustDoc,
get_size2::GetSize,
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(transparent)]
pub struct OverridesOptions(Vec<RangedValue<OverrideOptions>>);
@ -1119,7 +1171,16 @@ impl Deref for OverridesOptions {
}
#[derive(
Debug, Default, Clone, Eq, PartialEq, Combine, Serialize, Deserialize, OptionsMetadata,
Debug,
Default,
Clone,
Eq,
PartialEq,
Combine,
Serialize,
Deserialize,
OptionsMetadata,
get_size2::GetSize,
)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -1339,7 +1400,7 @@ impl RangedValue<OverrideOptions> {
}
/// The options for an override but without the include/exclude patterns.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Combine)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Combine, get_size2::GetSize)]
pub(super) struct InnerOverrideOptions {
/// Raw rule options as specified in the configuration.
/// Used when multiple overrides match a file and need to be merged.
@ -1463,7 +1524,7 @@ pub enum TyTomlError {
TomlSyntax(#[from] toml::de::Error),
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)]
pub struct OptionDiagnostic {
id: DiagnosticId,
message: String,

View file

@ -20,7 +20,7 @@ use crate::{Db, glob::IncludeExcludeFilter};
/// This can be achieved by adding a salsa query for the type checking specific settings.
///
/// Settings that are part of [`ty_python_semantic::ProgramSettings`] are not included here.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, PartialEq, get_size2::GetSize)]
pub struct Settings {
pub(super) rules: Arc<RuleSelection>,
pub(super) terminal: TerminalSettings,
@ -56,20 +56,20 @@ impl Settings {
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Default, get_size2::GetSize)]
pub struct TerminalSettings {
pub output_format: OutputFormat,
pub error_on_warning: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
pub struct SrcSettings {
pub respect_ignore_files: bool,
pub files: IncludeExcludeFilter,
}
/// A single configuration override that applies to files matching specific patterns.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
pub struct Override {
/// File pattern filter to determine which files this override applies to.
pub(super) files: IncludeExcludeFilter,

View file

@ -16,7 +16,7 @@ use std::sync::Arc;
use toml::Spanned;
use ty_combine::Combine;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub enum ValueSource {
/// Value loaded from a project's configuration file.
///
@ -84,7 +84,7 @@ impl Drop for ValueSourceGuard {
///
/// This ensures that two resolved configurations are identical even if the position of a value has changed
/// or if the values were loaded from different sources.
#[derive(Clone, serde::Serialize)]
#[derive(Clone, serde::Serialize, get_size2::GetSize)]
#[serde(transparent)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct RangedValue<T> {
@ -325,6 +325,7 @@ where
Ord,
Hash,
Combine,
get_size2::GetSize,
)]
#[serde(transparent)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@ -394,6 +395,7 @@ impl fmt::Display for RelativePathBuf {
Ord,
Hash,
Combine,
get_size2::GetSize,
)]
#[serde(transparent)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

View file

@ -310,7 +310,7 @@ impl<'a> ProjectFilesWalker<'a> {
}
}
#[derive(Error, Debug, Clone)]
#[derive(Error, Debug, Clone, get_size2::GetSize)]
pub(crate) enum WalkError {
#[error("`{path}`: {error}")]
IOPathError { path: SystemPathBuf, error: String },

View file

@ -211,7 +211,7 @@ fn all_submodule_names_for_package(db: &dyn Db, file: File) -> Option<Vec<Name>>
}
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct FileModule<'db> {
#[returns(ref)]
name: ModuleName,
@ -226,7 +226,7 @@ pub struct FileModule<'db> {
///
/// Namespace packages are special because there are
/// multiple possible paths and they have no corresponding code file.
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct NamespacePackage<'db> {
#[returns(ref)]
name: ModuleName,

View file

@ -45,7 +45,7 @@ pub fn resolve_real_module<'db>(db: &'db dyn Db, module_name: &ModuleName) -> Op
}
/// Which files should be visible when doing a module query
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, get_size2::GetSize)]
pub(crate) enum ModuleResolveMode {
/// Stubs are allowed to appear.
///
@ -60,7 +60,7 @@ pub(crate) enum ModuleResolveMode {
StubsNotAllowed,
}
#[salsa::interned]
#[salsa::interned(heap_size=ruff_memory_usage::heap_size)]
#[derive(Debug)]
pub(crate) struct ModuleResolveModeIngredient<'db> {
mode: ModuleResolveMode,
@ -172,7 +172,7 @@ pub(crate) fn search_paths(db: &dyn Db, resolve_mode: ModuleResolveMode) -> Sear
Program::get(db).search_paths(db).iter(db, resolve_mode)
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
pub struct SearchPaths {
/// Search paths that have been statically determined purely from reading
/// ty's configuration settings. These shouldn't ever change unless the
@ -643,7 +643,7 @@ impl<'db> Iterator for PthFileIterator<'db> {
/// A thin wrapper around `ModuleName` to make it a Salsa ingredient.
///
/// This is needed because Salsa requires that all query arguments are salsa ingredients.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
struct ModuleNameIngredient<'db> {
#[returns(ref)]
pub(super) name: ModuleName,

View file

@ -74,7 +74,7 @@ pub(crate) enum TypeshedVersionsParseErrorKind {
VersionParseError(#[from] PythonVersionDeserializationError),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, get_size2::GetSize)]
pub(crate) struct TypeshedVersions(FxHashMap<ModuleName, PyVersionRange>);
impl TypeshedVersions {
@ -230,7 +230,7 @@ impl fmt::Display for TypeshedVersions {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, get_size2::GetSize)]
pub(crate) enum PyVersionRange {
AvailableFrom(RangeFrom<PythonVersion>),
AvailableWithin(RangeInclusive<PythonVersion>),

View file

@ -13,7 +13,7 @@ use ruff_text_size::TextRange;
use salsa::Durability;
use salsa::Setter;
#[salsa::input(singleton)]
#[salsa::input(singleton, heap_size=ruff_memory_usage::heap_size)]
pub struct Program {
#[returns(ref)]
pub python_version_with_source: PythonVersionWithSource,
@ -93,7 +93,7 @@ pub struct ProgramSettings {
pub search_paths: SearchPaths,
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
#[derive(Clone, Debug, Eq, PartialEq, Default, get_size2::GetSize)]
pub enum PythonVersionSource {
/// Value loaded from a project's configuration file.
ConfigFile(PythonVersionFileSource),
@ -123,7 +123,7 @@ pub enum PythonVersionSource {
/// Information regarding the file and [`TextRange`] of the configuration
/// from which we inferred the Python version.
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, get_size2::GetSize)]
pub struct PythonVersionFileSource {
path: Arc<SystemPathBuf>,
range: Option<TextRange>,
@ -145,7 +145,7 @@ impl PythonVersionFileSource {
}
}
#[derive(Eq, PartialEq, Debug, Clone)]
#[derive(Eq, PartialEq, Debug, Clone, get_size2::GetSize)]
pub struct PythonVersionWithSource {
pub version: PythonVersion,
pub source: PythonVersionSource,

View file

@ -1,7 +1,7 @@
use std::fmt::{Display, Formatter};
/// The target platform to assume when resolving types.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, get_size2::GetSize)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize, ruff_macros::RustDoc),

View file

@ -22,7 +22,7 @@ use crate::unpack::{Unpack, UnpackPosition};
/// because a new scope gets inserted before the `Definition` or a new place is inserted
/// before this `Definition`. However, the ID can be considered stable and it is okay to use
/// `Definition` in cross-module` salsa queries or as a field on other salsa tracked structs.
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct Definition<'db> {
/// The file in which the definition occurs.
pub file: File,
@ -645,7 +645,7 @@ impl DefinitionCategory {
/// [`DefinitionKind`] fields in salsa tracked structs should be tracked (attributed with `#[tracked]`)
/// because the kind is a thin wrapper around [`AstNodeRef`]. See the [`AstNodeRef`] documentation
/// for an in-depth explanation of why this is necessary.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub enum DefinitionKind<'db> {
Import(ImportDefinitionKind),
ImportFrom(ImportFromDefinitionKind),
@ -834,7 +834,7 @@ impl DefinitionKind<'_> {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Hash)]
#[derive(Copy, Clone, Debug, PartialEq, Hash, get_size2::GetSize)]
pub(crate) enum TargetKind<'db> {
Sequence(UnpackPosition, Unpack<'db>),
/// Name, attribute, or subscript.
@ -850,7 +850,7 @@ impl<'db> From<Option<(UnpackPosition, Unpack<'db>)>> for TargetKind<'db> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct StarImportDefinitionKind {
node: AstNodeRef<ast::StmtImportFrom>,
symbol_id: ScopedSymbolId,
@ -880,7 +880,7 @@ impl StarImportDefinitionKind {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct MatchPatternDefinitionKind {
pattern: AstNodeRef<ast::Pattern>,
identifier: AstNodeRef<ast::Identifier>,
@ -902,7 +902,7 @@ impl MatchPatternDefinitionKind {
/// But if the target is an attribute or subscript, its definition is not in the comprehension's scope;
/// it is in the scope in which the root variable is bound.
/// TODO: currently we don't model this correctly and simply assume that it is in a scope outside the comprehension.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct ComprehensionDefinitionKind<'db> {
target_kind: TargetKind<'db>,
iterable: AstNodeRef<ast::Expr>,
@ -933,7 +933,7 @@ impl<'db> ComprehensionDefinitionKind<'db> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct ImportDefinitionKind {
node: AstNodeRef<ast::StmtImport>,
alias_index: usize,
@ -954,7 +954,7 @@ impl ImportDefinitionKind {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct ImportFromDefinitionKind {
node: AstNodeRef<ast::StmtImportFrom>,
alias_index: usize,
@ -975,7 +975,7 @@ impl ImportFromDefinitionKind {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct AssignmentDefinitionKind<'db> {
target_kind: TargetKind<'db>,
value: AstNodeRef<ast::Expr>,
@ -996,7 +996,7 @@ impl<'db> AssignmentDefinitionKind<'db> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct AnnotatedAssignmentDefinitionKind {
annotation: AstNodeRef<ast::Expr>,
value: Option<AstNodeRef<ast::Expr>>,
@ -1017,7 +1017,7 @@ impl AnnotatedAssignmentDefinitionKind {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct WithItemDefinitionKind<'db> {
target_kind: TargetKind<'db>,
context_expr: AstNodeRef<ast::Expr>,
@ -1043,7 +1043,7 @@ impl<'db> WithItemDefinitionKind<'db> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct ForStmtDefinitionKind<'db> {
target_kind: TargetKind<'db>,
iterable: AstNodeRef<ast::Expr>,
@ -1069,7 +1069,7 @@ impl<'db> ForStmtDefinitionKind<'db> {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, get_size2::GetSize)]
pub struct ExceptHandlerDefinitionKind {
handler: AstNodeRef<ast::ExceptHandlerExceptHandler>,
is_star: bool,

View file

@ -10,7 +10,7 @@ use salsa;
/// a type expression. For example, in `self.x: <annotation> = <value>`, the
/// `<annotation>` is inferred as a type expression, while `<value>` is inferred
/// as a normal expression.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
pub(crate) enum ExpressionKind {
Normal,
TypeExpression,
@ -31,7 +31,7 @@ pub(crate) enum ExpressionKind {
/// * a return type of a cross-module query
/// * a field of a type that is a return type of a cross-module query
/// * an argument of a cross-module query
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct Expression<'db> {
/// The file in which the expression occurs.
pub(crate) file: File,

View file

@ -117,7 +117,7 @@ pub(crate) enum PredicateNode<'db> {
StarImportPlaceholder(StarImportPlaceholderPredicate<'db>),
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, salsa::Update)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(crate) enum ClassPatternKind {
Irrefutable,
Refutable,
@ -130,7 +130,7 @@ impl ClassPatternKind {
}
/// Pattern kinds for which we support type narrowing and/or static reachability analysis.
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update)]
#[derive(Debug, Clone, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
pub(crate) enum PatternPredicateKind<'db> {
Singleton(Singleton),
Value(Expression<'db>),
@ -140,7 +140,7 @@ pub(crate) enum PatternPredicateKind<'db> {
Unsupported,
}
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct PatternPredicate<'db> {
pub(crate) file: File,
@ -206,7 +206,7 @@ impl<'db> PatternPredicate<'db> {
/// - If it resolves to a possibly bound symbol, then the predicate resolves to [`Truthiness::Ambiguous`]
///
/// [Truthiness]: [crate::types::Truthiness]
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct StarImportPlaceholderPredicate<'db> {
pub(crate) importing_file: File,

View file

@ -14,7 +14,7 @@ use crate::{
};
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct ScopeId<'db> {
pub file: File,

View file

@ -385,7 +385,7 @@ pub(crate) use todo_type;
/// # Ordering
/// Ordering is based on the property instance's salsa-assigned id and not on its values.
/// The id may change between runs, or when the property instance was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct PropertyInstanceType<'db> {
getter: Option<Type<'db>>,
@ -6079,7 +6079,7 @@ impl<'db> From<&Type<'db>> for Type<'db> {
/// This is represented as an enum (with some variants using `Cow`), and not an `FnMut` trait,
/// since we sometimes have to apply type mappings lazily (e.g., to the signature of a function
/// literal).
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub enum TypeMapping<'a, 'db> {
/// Applies a specialization to the type
Specialization(Specialization<'db>),
@ -6610,7 +6610,7 @@ impl<'db> InvalidTypeExpression<'db> {
}
/// Data regarding a `warnings.deprecated` or `typing_extensions.deprecated` decorator.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct DeprecatedInstance<'db> {
/// The message for the deprecation
@ -6622,7 +6622,7 @@ impl get_size2::GetSize for DeprecatedInstance<'_> {}
/// Contains information about instances of `dataclasses.Field`, typically created using
/// `dataclasses.field()`.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct FieldInstance<'db> {
/// The type of the default value for this field. This is derived from the `default` or
@ -6648,7 +6648,7 @@ impl<'db> FieldInstance<'db> {
/// Whether this typevar was created via the legacy `TypeVar` constructor, using PEP 695 syntax,
/// or an implicit typevar like `Self` was used.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub enum TypeVarKind {
/// `T = TypeVar("T")`
Legacy,
@ -6694,7 +6694,7 @@ pub enum TypeVarKind {
/// # Ordering
/// Ordering is based on the type var instance's salsa-assigned id and not on its values.
/// The id may change between runs, or when the type var instance was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct TypeVarInstance<'db> {
/// The name of this TypeVar (e.g. `T`)
@ -6789,7 +6789,7 @@ impl<'db> TypeVarInstance<'db> {
}
/// Where a type variable is bound and usable.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub enum BindingContext<'db> {
/// The definition of the generic class, function, or type alias that binds this typevar.
Definition(Definition<'db>),
@ -6809,7 +6809,7 @@ impl<'db> BindingContext<'db> {
/// A type variable that has been bound to a generic context, and which can be specialized to a
/// concrete type.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct BoundTypeVarInstance<'db> {
pub typevar: TypeVarInstance<'db>,
@ -6874,7 +6874,7 @@ impl<'db> BoundTypeVarInstance<'db> {
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub enum TypeVarVariance {
Invariant,
Covariant,
@ -6896,7 +6896,7 @@ impl TypeVarVariance {
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub enum TypeVarBoundOrConstraints<'db> {
UpperBound(Type<'db>),
Constraints(UnionType<'db>),
@ -7916,7 +7916,7 @@ impl From<bool> for Truthiness {
/// # Ordering
/// Ordering is based on the bounded method's salsa-assigned id and not on its values.
/// The id may change between runs, or when the bounded method was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct BoundMethodType<'db> {
/// The function that is being bound. Corresponds to the `__func__` attribute on a
@ -7992,7 +7992,7 @@ impl<'db> BoundMethodType<'db> {
/// # Ordering
/// Ordering is based on the callable type's salsa-assigned id and not on its values.
/// The id may change between runs, or when the callable type was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct CallableType<'db> {
#[returns(ref)]
@ -8270,7 +8270,7 @@ pub enum WrapperDescriptorKind {
/// # Ordering
/// Ordering is based on the module literal's salsa-assigned id and not on its values.
/// The id may change between runs, or when the module literal was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct ModuleLiteralType<'db> {
/// The imported module.
@ -8380,7 +8380,7 @@ impl<'db> ModuleLiteralType<'db> {
/// # Ordering
/// Ordering is based on the type alias's salsa-assigned id and not on its values.
/// The id may change between runs, or when the alias was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct PEP695TypeAliasType<'db> {
#[returns(ref)]
@ -8427,7 +8427,7 @@ impl<'db> PEP695TypeAliasType<'db> {
/// # Ordering
/// Ordering is based on the type alias's salsa-assigned id and not on its values.
/// The id may change between runs, or when the alias was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct BareTypeAliasType<'db> {
#[returns(ref)]
@ -8522,7 +8522,7 @@ pub(super) struct MetaclassCandidate<'db> {
explicit_metaclass_of: ClassLiteral<'db>,
}
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct UnionType<'db> {
/// The union type includes values in any of these types.
#[returns(deref)]
@ -8744,7 +8744,7 @@ impl<'db> UnionType<'db> {
}
}
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=IntersectionType::heap_size)]
pub struct IntersectionType<'db> {
/// The intersection type includes only values in all of these types.
#[returns(ref)]
@ -8938,12 +8938,17 @@ impl<'db> IntersectionType<'db> {
pub fn has_one_element(&self, db: &'db dyn Db) -> bool {
(self.positive(db).len() + self.negative(db).len()) == 1
}
fn heap_size((positive, negative): &(FxOrderSet<Type<'db>>, FxOrderSet<Type<'db>>)) -> usize {
ruff_memory_usage::order_set_heap_size(positive)
+ ruff_memory_usage::order_set_heap_size(negative)
}
}
/// # Ordering
/// Ordering is based on the string literal's salsa-assigned id and not on its value.
/// The id may change between runs, or when the string literal was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct StringLiteralType<'db> {
#[returns(deref)]
@ -8971,7 +8976,7 @@ impl<'db> StringLiteralType<'db> {
/// # Ordering
/// Ordering is based on the byte literal's salsa-assigned id and not on its value.
/// The id may change between runs, or when the byte literal was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct BytesLiteralType<'db> {
#[returns(deref)]
@ -8996,7 +9001,7 @@ impl<'db> BytesLiteralType<'db> {
/// NO = 0
/// YES = 1
/// ```
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct EnumLiteralType<'db> {
/// A reference to the enum class this literal belongs to
@ -9017,7 +9022,7 @@ impl<'db> EnumLiteralType<'db> {
/// Type that represents the set of all inhabitants (`dict` instances) that conform to
/// a given `TypedDict` schema.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct TypedDictType<'db> {
/// A reference to the class (inheriting from `typing.TypedDict`) that specifies the
@ -9104,7 +9109,7 @@ impl BoundSuperError<'_> {
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, get_size2::GetSize)]
pub enum SuperOwnerKind<'db> {
Dynamic(DynamicType),
Class(ClassType<'db>),
@ -9193,7 +9198,7 @@ impl<'db> From<SuperOwnerKind<'db>> for Type<'db> {
}
/// Represent a bound super object like `super(PivotClass, owner)`
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct BoundSuperType<'db> {
pub pivot_class: ClassBase<'db>,
pub owner: SuperOwnerKind<'db>,
@ -9381,7 +9386,7 @@ impl<'db> BoundSuperType<'db> {
}
}
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct TypeIsType<'db> {
return_type: Type<'db>,
/// The ID of the scope to which the place belongs

View file

@ -212,7 +212,7 @@ impl CodeGeneratorKind {
/// # Ordering
/// Ordering is based on the generic aliases's salsa-assigned id and not on its values.
/// The id may change between runs, or when the alias was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct GenericAlias<'db> {
pub(crate) origin: ClassLiteral<'db>,
@ -1147,7 +1147,7 @@ pub(crate) struct Field<'db> {
/// # Ordering
/// Ordering is based on the class's id assigned by salsa and not on the class literal's values.
/// The id may change between runs, or when the class literal was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct ClassLiteral<'db> {
/// Name of the class at definition
@ -2998,7 +2998,7 @@ pub(super) enum SolidBaseKind {
/// Feel free to expand this enum if you ever find yourself using the same class in multiple
/// places.
/// Note: good candidates are any classes in `[crate::module_resolver::module::KnownModule]`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
#[cfg_attr(test, derive(strum_macros::EnumIter))]
pub enum KnownClass {
// To figure out where an stdlib symbol is defined, you can go into `crates/ty_vendored`

View file

@ -120,6 +120,8 @@ bitflags! {
}
}
impl get_size2::GetSize for FunctionDecorators {}
impl FunctionDecorators {
pub(super) fn from_decorator_type(db: &dyn Db, decorator_type: Type) -> Self {
match decorator_type {
@ -184,7 +186,7 @@ impl Default for DataclassTransformerParams {
/// Ordering is based on the function's id assigned by salsa and not on the function literal's
/// values. The id may change between runs, or when the function literal was garbage collected and
/// recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct OverloadLiteral<'db> {
/// Name of the function at definition.
@ -406,7 +408,7 @@ impl<'db> OverloadLiteral<'db> {
/// Ordering is based on the function's id assigned by salsa and not on the function literal's
/// values. The id may change between runs, or when the function literal was garbage collected and
/// recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct FunctionLiteral<'db> {
pub(crate) last_definition: OverloadLiteral<'db>,
@ -433,6 +435,9 @@ pub struct FunctionLiteral<'db> {
inherited_generic_context: Option<GenericContext<'db>>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for FunctionLiteral<'_> {}
fn walk_function_literal<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
function: FunctionLiteral<'db>,
@ -609,7 +614,7 @@ impl<'db> FunctionLiteral<'db> {
/// Represents a function type, which might be a non-generic function, or a specialization of a
/// generic function.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct FunctionType<'db> {
pub(crate) literal: FunctionLiteral<'db>,
@ -1057,7 +1062,15 @@ fn last_definition_signature_cycle_initial<'db>(
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
/// have special behavior.
#[derive(
Debug, Copy, Clone, PartialEq, Eq, Hash, strum_macros::EnumString, strum_macros::IntoStaticStr,
Debug,
Copy,
Clone,
PartialEq,
Eq,
Hash,
strum_macros::EnumString,
strum_macros::IntoStaticStr,
get_size2::GetSize,
)]
#[strum(serialize_all = "snake_case")]
#[cfg_attr(test, derive(strum_macros::EnumIter))]

View file

@ -89,7 +89,7 @@ pub(crate) fn bind_typevar<'db>(
/// # Ordering
/// Ordering is based on the context's salsa-assigned id and not on its values.
/// The id may change between runs, or when the context was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=GenericContext::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct GenericContext<'db> {
#[returns(ref)]
@ -363,6 +363,10 @@ impl<'db> GenericContext<'db> {
.collect();
Self::new(db, variables)
}
fn heap_size((variables,): &(FxOrderSet<BoundTypeVarInstance<'db>>,)) -> usize {
ruff_memory_usage::order_set_heap_size(variables)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -390,7 +394,7 @@ impl std::fmt::Display for LegacyGenericBase {
///
/// TODO: Handle nested specializations better, with actual parent links to the specialization of
/// the lexically containing context.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
pub struct Specialization<'db> {
pub(crate) generic_context: GenericContext<'db>,
#[returns(deref)]
@ -401,6 +405,9 @@ pub struct Specialization<'db> {
tuple_inner: Option<TupleType<'db>>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for Specialization<'_> {}
pub(super) fn walk_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
db: &'db dyn Db,
specialization: Specialization<'db>,
@ -655,7 +662,7 @@ impl<'db> Specialization<'db> {
///
/// You will usually use [`Specialization`] instead of this type. This type is used when we need to
/// substitute types for type variables before we have fully constructed a [`Specialization`].
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct PartialSpecialization<'a, 'db> {
generic_context: GenericContext<'db>,
types: Cow<'a, [Type<'db>]>,

View file

@ -70,7 +70,7 @@ impl<'db> Deref for ProtocolClassLiteral<'db> {
/// # Ordering
/// Ordering is based on the protocol interface member's salsa-assigned id and not on its members.
/// The id may change between runs, or when the protocol instance members was garbage collected and recreated.
#[salsa::interned(debug)]
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub(super) struct ProtocolInterface<'db> {
#[returns(ref)]
@ -241,7 +241,7 @@ impl<'db> ProtocolInterface<'db> {
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update)]
#[derive(Debug, PartialEq, Eq, Clone, Hash, salsa::Update, get_size2::GetSize)]
pub(super) struct ProtocolMemberData<'db> {
kind: ProtocolMemberKind<'db>,
qualifiers: TypeQualifiers,
@ -319,7 +319,7 @@ impl<'db> ProtocolMemberData<'db> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, salsa::Update, Hash, get_size2::GetSize)]
enum ProtocolMemberKind<'db> {
Method(CallableType<'db>),
Property(PropertyInstanceType<'db>),

View file

@ -125,7 +125,7 @@ impl TupleLength {
/// # Ordering
/// Ordering is based on the tuple's salsa-assigned id and not on its elements.
/// The id may change between runs, or when the tuple was garbage collected and recreated.
#[salsa::interned(debug, constructor=new_internal)]
#[salsa::interned(debug, constructor=new_internal, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct TupleType<'db> {
#[returns(ref)]
@ -223,7 +223,7 @@ impl<'db> TupleType<'db> {
// N.B. If this method is not Salsa-tracked, we take 10 minutes to check
// `static-frame` as part of a mypy_primer run! This is because it's called
// from `NominalInstanceType::class()`, which is a very hot method.
#[salsa::tracked]
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> {
let tuple_class = KnownClass::Tuple
.try_to_class_literal(db)
@ -302,7 +302,7 @@ pub(crate) type TupleSpec<'db> = Tuple<Type<'db>>;
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct FixedLengthTuple<T>(Vec<T>);
impl<T> FixedLengthTuple<T> {
@ -516,7 +516,7 @@ impl<'db> PySlice<'db> for FixedLengthTuple<Type<'db>> {
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct VariableLengthTuple<T> {
pub(crate) prefix: Vec<T>,
pub(crate) variable: T,
@ -956,7 +956,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub enum Tuple<T> {
Fixed(FixedLengthTuple<T>),
Variable(VariableLengthTuple<T>),

View file

@ -26,7 +26,7 @@ use crate::semantic_index::scope::{FileScopeId, ScopeId};
/// * a return type of a cross-module query
/// * a field of a type that is a return type of a cross-module query
/// * an argument of a cross-module query
#[salsa::tracked(debug)]
#[salsa::tracked(debug, heap_size=ruff_memory_usage::heap_size)]
pub(crate) struct Unpack<'db> {
pub(crate) file: File,
@ -46,6 +46,9 @@ pub(crate) struct Unpack<'db> {
pub(crate) value: UnpackValue<'db>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for Unpack<'_> {}
impl<'db> Unpack<'db> {
pub(crate) fn target<'ast>(
self,
@ -67,7 +70,7 @@ impl<'db> Unpack<'db> {
}
/// The expression that is being unpacked.
#[derive(Clone, Copy, Debug, Hash, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) struct UnpackValue<'db> {
/// The kind of unpack expression
kind: UnpackKind,
@ -99,7 +102,7 @@ impl<'db> UnpackValue<'db> {
}
}
#[derive(Clone, Copy, Debug, Hash, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) enum EvaluationMode {
Sync,
Async,
@ -119,7 +122,7 @@ impl EvaluationMode {
}
}
#[derive(Clone, Copy, Debug, Hash, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) enum UnpackKind {
/// An iterable expression like the one in a `for` loop or a comprehension.
Iterable { mode: EvaluationMode },
@ -130,7 +133,7 @@ pub(crate) enum UnpackKind {
}
/// The position of the target element in an unpacking.
#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
pub(crate) enum UnpackPosition {
/// The target element is in the first position of the unpacking.
First,