Skip to content

Commit e2e1e64

Browse files
authored
[ty] Remove excess capacity from more Salsa cached collections (#25411)
1 parent 1bd77e1 commit e2e1e64

20 files changed

Lines changed: 69 additions & 50 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,5 @@ When working on ty, PR titles should start with `[ty]` and be tagged with the `t
7676
- Run `cargo dev generate-all` after changing configuration options, CLI arguments, lint rules, or environment variable definitions, as these changes require regeneration of schemas, docs, and CLI references.
7777
- Don't prefix tests with `test_`.
7878
- Don't separate struct definitions from their `impl` blocks unless the `impl` is deliberately placed in a separate file, as for large structs.
79+
- The `db` parameter should always be the first, or second, if it's a method taking a `self` argument
80+
- For Salsa-cached values, avoid retaining excess collection capacity. Prefer boxed slices; otherwise shrink collections that may have spare capacity before returning them. In particular, inspect `HashMap` and `HashSet` values constructed via `extend`, `collect`, explicit reservation, or removal, since those operations can leave capacity that insert-only construction does not.

crates/ty_ide/src/symbols.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,9 +792,15 @@ impl<'db> SymbolVisitor<'db> {
792792
remap.push(Some(new_id));
793793
new.push(symbol);
794794
}
795+
796+
new.shrink_to_fit();
797+
795798
FlatSymbols {
796799
symbols: new,
797-
all_names: self.all_origin.map(|_| self.all_names),
800+
all_names: self.all_origin.map(|_| {
801+
self.all_names.shrink_to_fit();
802+
self.all_names
803+
}),
798804
}
799805
}
800806

crates/ty_module_resolver/src/list.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::resolve::{ModuleResolveMode, ResolverContext, resolve_file_module, se
1010

1111
/// List all available modules, including all sub-modules, sorted in lexicographic order.
1212
pub fn all_modules(db: &dyn Db) -> Vec<Module<'_>> {
13-
let mut modules = list_modules(db);
13+
let mut modules = list_modules(db).to_vec();
1414
let mut stack = modules.clone();
1515
while let Some(module) = stack.pop() {
1616
for &submodule in module.all_submodules(db) {
@@ -23,8 +23,8 @@ pub fn all_modules(db: &dyn Db) -> Vec<Module<'_>> {
2323
}
2424

2525
/// List all available top-level modules.
26-
#[salsa::tracked]
27-
pub fn list_modules(db: &dyn Db) -> Vec<Module<'_>> {
26+
#[salsa::tracked(returns(deref))]
27+
pub fn list_modules(db: &dyn Db) -> Box<[Module<'_>]> {
2828
let mut modules: BTreeMap<&ModuleName, ListedModule<'_>> = BTreeMap::new();
2929
for search_path in search_paths(db, ModuleResolveMode::StubsAllowed) {
3030
for new in list_modules_in(db, SearchPathIngredient::new(db, search_path.clone())) {
@@ -463,7 +463,7 @@ mod tests {
463463
}
464464

465465
fn sorted_list(db: &dyn Db) -> Vec<Module<'_>> {
466-
let mut modules = list_modules(db);
466+
let mut modules = list_modules(db).to_vec();
467467
modules.sort_by(|m1, m2| m1.name(db).cmp(m2.name(db)));
468468
modules
469469
}

crates/ty_module_resolver/src/module.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ impl<'db> Module<'db> {
9797
/// The names returned correspond to the "base" name of the module.
9898
/// That is, `{self.name}.{basename}` should give the full module name.
9999
pub fn all_submodules(self, db: &'db dyn Db) -> &'db [Module<'db>] {
100-
all_submodule_names_for_package(db, self)
101-
.as_deref()
102-
.unwrap_or_default()
100+
all_submodule_names_for_package(db, self).unwrap_or_default()
103101
}
104102
}
105103

@@ -118,12 +116,11 @@ impl std::fmt::Debug for Module<'_> {
118116
}
119117
}
120118

121-
#[allow(clippy::ref_option)]
122-
#[salsa::tracked(returns(ref), heap_size=ruff_memory_usage::heap_size)]
119+
#[salsa::tracked(returns(as_deref), heap_size=ruff_memory_usage::heap_size)]
123120
fn all_submodule_names_for_package<'db>(
124121
db: &'db dyn Db,
125122
module: Module<'db>,
126-
) -> Option<Vec<Module<'db>>> {
123+
) -> Option<Box<[Module<'db>]>> {
127124
fn is_submodule(
128125
is_dir: bool,
129126
is_file: bool,

crates/ty_module_resolver/src/resolve.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ pub fn search_paths(db: &dyn Db, resolve_mode: ModuleResolveMode) -> SearchPathI
370370
///
371371
/// We exclude `__init__.py(i)` dirs to avoid truncating packages.
372372
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size)]
373-
fn absolute_desperate_search_paths(db: &dyn Db, importing_file: File) -> Option<Vec<SearchPath>> {
373+
fn absolute_desperate_search_paths(db: &dyn Db, importing_file: File) -> Option<Box<[SearchPath]>> {
374374
let system = db.system();
375375
let importing_path = importing_file.path(db).as_system_path()?;
376376

@@ -425,7 +425,7 @@ fn absolute_desperate_search_paths(db: &dyn Db, importing_file: File) -> Option<
425425
if search_paths.is_empty() {
426426
None
427427
} else {
428-
Some(search_paths)
428+
Some(search_paths.into_boxed_slice())
429429
}
430430
}
431431

crates/ty_python_core/src/ast_ids.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,7 @@ impl AstIdsBuilder {
110110
self.uses_map.get(&key.into()).copied()
111111
}
112112

113-
pub(super) fn finish(mut self) -> AstIds {
114-
self.uses_map.shrink_to_fit();
115-
113+
pub(super) fn finish(self) -> AstIds {
116114
AstIds {
117115
uses_map: self.uses_map,
118116
}

crates/ty_python_core/src/builder.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,17 +2230,14 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
22302230
place_tables.shrink_to_fit();
22312231
use_def_maps.shrink_to_fit();
22322232
ast_ids.shrink_to_fit();
2233-
self.definitions_by_node.shrink_to_fit();
2234-
self.statements_by_node.shrink_to_fit();
22352233
self.enclosing_lambda_statements.shrink_to_fit();
2236-
self.collections_by_use.shrink_to_fit();
2237-
self.uses_by_collection.shrink_to_fit();
2238-
2234+
self.imported_modules.shrink_to_fit();
22392235
self.scope_ids_by_scope.shrink_to_fit();
2240-
self.scopes_by_node.shrink_to_fit();
2241-
self.generator_functions.shrink_to_fit();
22422236
self.enclosing_snapshots.shrink_to_fit();
22432237

2238+
let mut semantic_syntax_errors = self.semantic_syntax_errors.into_inner();
2239+
semantic_syntax_errors.shrink_to_fit();
2240+
22442241
SemanticIndex {
22452242
place_tables,
22462243
scopes: self.scopes,
@@ -2258,7 +2255,7 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
22582255
imported_modules: Arc::new(self.imported_modules),
22592256
has_future_annotations: self.has_future_annotations,
22602257
enclosing_snapshots: self.enclosing_snapshots,
2261-
semantic_syntax_errors: self.semantic_syntax_errors.into_inner(),
2258+
semantic_syntax_errors,
22622259
generator_functions: self.generator_functions,
22632260
narrowing_alias_predicates: self.alias_predicates,
22642261
}
@@ -3620,10 +3617,12 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
36203617
// context the lambda is being inferred with, and so any statement
36213618
// containing a lambda must be inferable as a standalone statement
36223619
// to avoid large scope-level cycles.
3623-
for lambda in current_statement.lambda_expressions {
3624-
self.enclosing_lambda_statements
3625-
.insert(lambda.into(), standalone_statement);
3626-
}
3620+
self.enclosing_lambda_statements.extend(
3621+
current_statement
3622+
.lambda_expressions
3623+
.into_iter()
3624+
.map(|lambda| (lambda.into(), standalone_statement)),
3625+
);
36273626

36283627
// The inferred element type of collection literal depends on uses of
36293628
// the collection in its containing scope, and so each use must be part

crates/ty_python_core/src/use_def.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,10 +1683,7 @@ impl<'db> UseDefMapBuilder<'db> {
16831683
self.reachable_symbol_definitions.shrink_to_fit();
16841684
self.reachable_member_definitions.shrink_to_fit();
16851685
self.bindings_by_use.shrink_to_fit();
1686-
self.multi_bindings_by_use.shrink_to_fit();
16871686
self.range_reachability.shrink_to_fit();
1688-
self.declarations_by_binding.shrink_to_fit();
1689-
self.bindings_by_definition.shrink_to_fit();
16901687
self.enclosing_snapshots.shrink_to_fit();
16911688

16921689
let mut interned_bindings = IndexVec::with_capacity(self.bindings_by_definition.len());
@@ -1808,7 +1805,6 @@ impl<'db> UseDefMapBuilder<'db> {
18081805
interned_ids_by_definition.insert(definition, interned_id);
18091806
}
18101807

1811-
interned_ids_by_definition.shrink_to_fit();
18121808
interned_ids_by_definition
18131809
}
18141810

@@ -1832,7 +1828,6 @@ impl<'db> UseDefMapBuilder<'db> {
18321828
interned_ids_by_binding.insert(binding, interned_id);
18331829
}
18341830

1835-
interned_ids_by_binding.shrink_to_fit();
18361831
interned_ids_by_binding
18371832
}
18381833

crates/ty_python_semantic/src/dunder_all.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use rustc_hash::FxHashSet;
2-
31
use ruff_db::files::File;
42
use ruff_db::parsed::parsed_module;
53
use ruff_python_ast::name::Name;
64
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
75
use ruff_python_ast::{self as ast};
6+
use rustc_hash::FxHashSet;
87
use ty_module_resolver::{ModuleName, resolve_module};
98

109
use crate::Db;
@@ -197,13 +196,14 @@ impl<'db> DunderAllNamesCollector<'db> {
197196
///
198197
/// Returns [`None`] if `__all__` is not defined in the current module or if it contains
199198
/// invalid elements.
200-
fn into_names(self) -> Option<FxHashSet<Name>> {
199+
fn into_names(mut self) -> Option<FxHashSet<Name>> {
201200
if self.origin.is_none() {
202201
None
203202
} else if self.invalid {
204203
tracing::debug!("Invalid `__all__` in `{}`", self.file.path(self.db));
205204
None
206205
} else {
206+
self.names.shrink_to_fit();
207207
Some(self.names)
208208
}
209209
}

crates/ty_python_semantic/src/semantic_model.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ impl<'db> SemanticModel<'db> {
119119
let is_typing_extensions_available = self.file.is_stub(self.db)
120120
|| resolve_real_shadowable_module(self.db, self.file, &typing_extensions).is_some();
121121
list_modules(self.db)
122-
.into_iter()
122+
.iter()
123+
.copied()
123124
.filter(|module| {
124125
is_typing_extensions_available || module.name(self.db) != &typing_extensions
125126
})

0 commit comments

Comments
 (0)