Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit 73905d2

Browse files
author
Hendrik van Antwerpen
authored
Merge pull request #184 from github/test-new-partial-paths
Change tests and C API to use minimal partial paths set
2 parents d5c133f + c5b3053 commit 73905d2

13 files changed

+194
-457
lines changed

languages/tree-sitter-stack-graphs-typescript/test/builtins/async-arrow-function.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/builtins/async-arrow-function.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-definition.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-definition.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-expression.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-expression.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-inferred-return-type-not-directly-accessible.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/builtins/async-function-inferred-return-type-not-directly-accessible.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/modules/default-exported-declaration-is-not-a-named-export.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/modules/default-exported-declaration-is-not-a-named-export.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/modules/split-namespace-can-only-see-exports.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/modules/split-namespace-can-only-see-exports.ts

File renamed without changes.

languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts.skip renamed to languages/tree-sitter-stack-graphs-typescript/test/projects/package-dependency-with-nested-source-root.ts

File renamed without changes.

stack-graphs/src/c.rs

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,45 +1576,17 @@ pub extern "C" fn sg_partial_path_arena_find_partial_paths_in_file(
15761576
let partial_path_list = unsafe { &mut *partial_path_list };
15771577
let cancellation_flag: Option<&AtomicUsize> =
15781578
unsafe { std::mem::transmute(cancellation_flag.as_ref()) };
1579-
sg_partial_path_arena_find_partial_paths_in_file_inner(
1580-
graph,
1581-
partials,
1582-
file,
1583-
partial_path_list,
1584-
&AtomicUsizeCancellationFlag(cancellation_flag),
1585-
)
1586-
.into()
1587-
}
1588-
1589-
fn sg_partial_path_arena_find_partial_paths_in_file_inner(
1590-
graph: &StackGraph,
1591-
partials: &mut PartialPaths,
1592-
file: Handle<File>,
1593-
partial_path_list: &mut sg_partial_path_list,
1594-
cancellation_flag: &dyn CancellationFlag,
1595-
) -> Result<(), CancellationError> {
1596-
let mut db = Database::new();
1597-
partials.find_minimal_partial_paths_set_in_file(
1598-
graph,
1599-
file,
1600-
cancellation_flag,
1601-
|graph, partials, path| {
1602-
db.add_partial_path(graph, partials, path);
1603-
},
1604-
)?;
1605-
#[allow(deprecated)]
1606-
ForwardPartialPathStitcher::find_locally_complete_partial_paths(
1607-
graph,
1608-
partials,
1609-
&mut db,
1610-
cancellation_flag,
1611-
|_graph, partials, path| {
1612-
let mut path = path.clone();
1613-
path.ensure_both_directions(partials);
1614-
partial_path_list.partial_paths.push(path);
1615-
},
1616-
)?;
1617-
Ok(())
1579+
partials
1580+
.find_minimal_partial_paths_set_in_file(
1581+
graph,
1582+
file,
1583+
&AtomicUsizeCancellationFlag(cancellation_flag),
1584+
|_graph, partials, mut path| {
1585+
path.ensure_both_directions(partials);
1586+
partial_path_list.partial_paths.push(path);
1587+
},
1588+
)
1589+
.into()
16181590
}
16191591

16201592
/// A handle to a partial path in a partial path database. A zero handle represents a missing

stack-graphs/src/stitching.rs

Lines changed: 15 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -725,11 +725,9 @@ pub struct ForwardPartialPathStitcher {
725725
max_work_per_phase: usize,
726726
#[cfg(feature = "copious-debugging")]
727727
phase_number: usize,
728-
should_extend: Box<ShouldExtend>,
728+
should_extend: fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool,
729729
}
730730

731-
type ShouldExtend = dyn Fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool;
732-
733731
impl ForwardPartialPathStitcher {
734732
/// Creates a new forward partial path stitcher that is "seeded" with a set of starting stack
735733
/// graph nodes.
@@ -787,7 +785,7 @@ impl ForwardPartialPathStitcher {
787785
max_work_per_phase: usize::MAX,
788786
#[cfg(feature = "copious-debugging")]
789787
phase_number: 1,
790-
should_extend: Box::new(|_, _, _| true),
788+
should_extend: |_, _, _| true,
791789
}
792790
}
793791

@@ -813,7 +811,7 @@ impl ForwardPartialPathStitcher {
813811
max_work_per_phase: usize::MAX,
814812
#[cfg(feature = "copious-debugging")]
815813
phase_number: 1,
816-
should_extend: Box::new(|_, _, _| true),
814+
should_extend: |_, _, _| true,
817815
}
818816
}
819817

@@ -846,6 +844,14 @@ impl ForwardPartialPathStitcher {
846844
self.max_work_per_phase = max_work_per_phase;
847845
}
848846

847+
/// Sets a condition that determines if a partial path is a candidate that should be extended.
848+
pub fn set_should_extend(
849+
&mut self,
850+
should_extend: fn(&StackGraph, &mut PartialPaths, &PartialPath) -> bool,
851+
) {
852+
self.should_extend = should_extend;
853+
}
854+
849855
/// Attempts to extend one partial path as part of the algorithm. When calling this function,
850856
/// you are responsible for ensuring that `db` already contains all of the possible partial
851857
/// paths that we might want to extend `partial_path` with.
@@ -896,15 +902,13 @@ impl ForwardPartialPathStitcher {
896902
copious_debugging!(" is invalid: {:?}", err);
897903
continue;
898904
}
899-
if new_partial_path.start_node != partial_path.start_node {
900-
copious_debugging!(" is invalid: slips off of starting node");
905+
if !new_partial_path.is_productive(partials) {
906+
copious_debugging!(" is invalid: not productive");
901907
continue;
902908
}
903909
}
904910
copious_debugging!(" is {}", new_partial_path.display(graph, partials));
905-
if new_partial_path.is_productive(partials) {
906-
self.next_iteration.push_back(new_partial_path);
907-
}
911+
self.next_iteration.push_back(new_partial_path);
908912
}
909913

910914
extension_count
@@ -988,6 +992,7 @@ impl ForwardPartialPathStitcher {
988992
{
989993
let mut stitcher =
990994
ForwardPartialPathStitcher::from_nodes(graph, partials, db, starting_nodes);
995+
stitcher.set_should_extend(|g, _, p| p.starts_at_reference(g));
991996
while !stitcher.is_complete() {
992997
cancellation_flag.check("finding complete partial paths")?;
993998
let complete_partial_paths = stitcher
@@ -1000,70 +1005,4 @@ impl ForwardPartialPathStitcher {
10001005
}
10011006
Ok(())
10021007
}
1003-
1004-
/// Finds a set of locally complete partial paths, calling the `visit` closure for each one.
1005-
///
1006-
/// This functions computes paths that
1007-
/// (a) start at references, exported scopes, or the root,
1008-
/// (b) end at definitions, exported scopes, jump-to-scope nodes, or the root, and
1009-
/// (c) do not go through the root node.
1010-
/// If the partial paths in de provided database are file-local, the resutling paths set will
1011-
/// contain all paths that are locally complete, or end at a file boundary. The set of paths
1012-
/// may contain paths that can be constructed by stitching other paths in the result set.
1013-
///
1014-
/// This function will not return until all reachable partial paths have been processed, so
1015-
/// your database must already contain all partial paths that might be needed. If you have a
1016-
/// very large stack graph stored in some other storage system, and want more control over
1017-
/// lazily loading only the necessary pieces, then you should code up your own loop that calls
1018-
/// [`process_next_phase`][] manually.
1019-
///
1020-
/// [`process_next_phase`]: #method.process_next_phase
1021-
#[deprecated = "This method replicates old PartialPaths::find_all_partial_paths_in_file behavior. It has poor performance because it computes more paths than necessary, and users should use PartialPaths::find_minimal_partial_paths_set_in_file instead."]
1022-
pub fn find_locally_complete_partial_paths<F>(
1023-
graph: &StackGraph,
1024-
partials: &mut PartialPaths,
1025-
db: &mut Database,
1026-
cancellation_flag: &dyn CancellationFlag,
1027-
mut visit: F,
1028-
) -> Result<(), CancellationError>
1029-
where
1030-
F: FnMut(&StackGraph, &mut PartialPaths, &PartialPath),
1031-
{
1032-
fn is_start_node(graph: &StackGraph, node: Handle<Node>) -> bool {
1033-
let node = &graph[node];
1034-
node.is_reference() || node.is_exported_scope() || node.is_root()
1035-
}
1036-
fn is_complete(graph: &StackGraph, path: &PartialPath) -> bool {
1037-
let start_node = &graph[path.start_node];
1038-
let end_node = &graph[path.end_node];
1039-
let start_ok = path.starts_at_reference(graph)
1040-
|| start_node.is_root()
1041-
|| start_node.is_exported_scope();
1042-
let end_ok = path.ends_at_definition(graph)
1043-
|| end_node.is_exported_scope()
1044-
|| end_node.is_root()
1045-
|| end_node.is_jump_to();
1046-
start_ok && end_ok
1047-
}
1048-
let starting_nodes = graph
1049-
.iter_nodes()
1050-
.filter(|node| is_start_node(graph, *node))
1051-
.collect::<Vec<_>>();
1052-
let mut stitcher =
1053-
ForwardPartialPathStitcher::from_nodes(graph, partials, db, starting_nodes);
1054-
stitcher.should_extend =
1055-
Box::new(|g, _ps, p| p.edges.is_empty() || !g[p.end_node].is_root());
1056-
while !stitcher.is_complete() {
1057-
cancellation_flag.check("finding complete partial paths")?;
1058-
let partial_paths = stitcher
1059-
.previous_phase_partial_paths()
1060-
.filter(|partial_path| is_complete(graph, partial_path))
1061-
.collect::<Vec<_>>();
1062-
for path in partial_paths {
1063-
visit(graph, partials, path);
1064-
}
1065-
stitcher.process_next_phase(graph, partials, db);
1066-
}
1067-
Ok(())
1068-
}
10691008
}

stack-graphs/tests/it/c/can_find_partial_paths_in_file.rs

Lines changed: 10 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -142,64 +142,17 @@ fn class_field_through_function_parameter() {
142142
check_partial_paths_in_file(
143143
&graph,
144144
"main.py",
145-
&[
146-
// definition of `__main__` module
147-
"<__main__,%1> ($1) [root] -> [main.py(0) definition __main__] <%1> ($1)",
148-
// reference to `a` in import statement
149-
"<%1> ($1) [main.py(17) reference a] -> [root] <a,%1> ($1)",
150-
// `from a import *` means we can rewrite any lookup of `__main__.*` → `a.*`
151-
"<__main__.,%2> ($1) [root] -> [root] <a.,%2> ($1)",
152-
// reference to `b` in import statement
153-
"<%1> ($1) [main.py(15) reference b] -> [root] <b,%1> ($1)",
154-
// `from b import *` means we can rewrite any lookup of `__main__.*` → `b.*`
155-
"<__main__.,%2> ($1) [root] -> [root] <b.,%2> ($1)",
156-
// we can look for every reference in either `a` or `b`
157-
"<%1> ($1) [main.py(9) reference A] -> [root] <a.A,%1> ($1)",
158-
"<%1> ($1) [main.py(9) reference A] -> [root] <b.A,%1> ($1)",
159-
"<%1> ($1) [main.py(10) reference bar] -> [root] <a.foo()/([main.py(7)],$1).bar,%1> ($1)",
160-
"<%1> ($1) [main.py(10) reference bar] -> [root] <b.foo()/([main.py(7)],$1).bar,%1> ($1)",
161-
"<%1> ($1) [main.py(13) reference foo] -> [root] <a.foo,%1> ($1)",
162-
"<%1> ($1) [main.py(13) reference foo] -> [root] <b.foo,%1> ($1)",
163-
// parameter 0 of function call is `A`, which we can look up in either `a` or `b`
164-
"<0,%1> ($1) [main.py(7) exported scope] -> [root] <a.A,%1> ($1)",
165-
"<0,%1> ($1) [main.py(7) exported scope] -> [root] <b.A,%1> ($1)",
166-
],
145+
crate::can_find_partial_paths_in_file::CLASS_FIELD_THROUGH_FUNCTION_PARAMETER_MAIN_PATHS,
167146
);
168147
check_partial_paths_in_file(
169148
&graph,
170149
"a.py",
171-
&[
172-
// definition of `a` module
173-
"<a,%1> ($1) [root] -> [a.py(0) definition a] <%1> ($1)",
174-
// definition of `foo` function
175-
"<a.foo,%2> ($1) [root] -> [a.py(5) definition foo] <%2> ($1)",
176-
// reference to `x` in function body can resolve to formal parameter, and may have passed in parameters...
177-
"<%1> ($1) [a.py(8) reference x] -> [a.py(14) definition x] <%1> ()",
178-
// ...which we can look up either the 0th actual positional parameter...
179-
"<%1> ($1) [a.py(8) reference x] -> [jump to scope] <0,%1> ($1)",
180-
// ...or the actual named parameter `x`
181-
"<%1> ($1) [a.py(8) reference x] -> [jump to scope] <x,%1> ($1)",
182-
// result of function is `x`, which is passed in as a formal parameter...
183-
"<a.foo()/($3),%3> ($1) [root] -> [a.py(14) definition x] <%3> ()",
184-
// ...which we can look up either the 0th actual positional parameter...
185-
"<a.foo()/($3),%3> ($1) [root] -> [jump to scope] <0,%3> ($3)",
186-
// ...or the actual named parameter `x`
187-
"<a.foo()/($3),%3> ($1) [root] -> [jump to scope] <x,%3> ($3)",
188-
],
150+
crate::can_find_partial_paths_in_file::CLASS_FIELD_THROUGH_FUNCTION_PARAMETER_A_PATHS,
189151
);
190152
check_partial_paths_in_file(
191153
&graph,
192154
"b.py",
193-
&[
194-
// definition of `b` module
195-
"<b,%1> ($1) [root] -> [b.py(0) definition b] <%1> ($1)",
196-
// definition of class `A`
197-
"<b.A,%2> ($1) [root] -> [b.py(5) definition A] <%2> ($1)",
198-
// definition of class member `A.bar`
199-
"<b.A.bar,%3> ($1) [root] -> [b.py(8) definition bar] <%3> ($1)",
200-
// `bar` can also be accessed as an instance member
201-
"<b.A()/($3).bar,%3> ($1) [root] -> [b.py(8) definition bar] <%3> ($3)",
202-
],
155+
crate::can_find_partial_paths_in_file::CLASS_FIELD_THROUGH_FUNCTION_PARAMETER_B_PATHS,
203156
);
204157
}
205158

@@ -209,42 +162,17 @@ fn cyclic_imports_python() {
209162
check_partial_paths_in_file(
210163
&graph,
211164
"main.py",
212-
&[
213-
// definition of `__main__` module
214-
"<__main__,%1> ($1) [root] -> [main.py(0) definition __main__] <%1> ($1)",
215-
// reference to `a` in import statement
216-
"<%1> ($1) [main.py(8) reference a] -> [root] <a,%1> ($1)",
217-
// `from a import *` means we can rewrite any lookup of `__main__.*` → `a.*`
218-
"<__main__.,%2> ($1) [root] -> [root] <a.,%2> ($1)",
219-
// reference to `foo` becomes `a.foo` because of import statement
220-
"<%1> ($1) [main.py(6) reference foo] -> [root] <a.foo,%1> ($1)",
221-
],
165+
crate::can_find_partial_paths_in_file::CYCLIC_IMPORTS_PYTHON_MAIN_PATHS,
222166
);
223167
check_partial_paths_in_file(
224168
&graph,
225169
"a.py",
226-
&[
227-
// definition of `a` module
228-
"<a,%1> ($1) [root] -> [a.py(0) definition a] <%1> ($1)",
229-
// reference to `b` in import statement
230-
"<%1> ($1) [a.py(6) reference b] -> [root] <b,%1> ($1)",
231-
// `from b import *` means we can rewrite any lookup of `a.*` → `b.*`
232-
"<a.,%2> ($1) [root] -> [root] <b.,%2> ($1)",
233-
],
170+
crate::can_find_partial_paths_in_file::CYCLIC_IMPORTS_PYTHON_A_PATHS,
234171
);
235172
check_partial_paths_in_file(
236173
&graph,
237174
"b.py",
238-
&[
239-
// definition of `b` module
240-
"<b,%1> ($1) [root] -> [b.py(0) definition b] <%1> ($1)",
241-
// reference to `a` in import statement
242-
"<%1> ($1) [b.py(8) reference a] -> [root] <a,%1> ($1)",
243-
// `from a import *` means we can rewrite any lookup of `b.*` → `a.*`
244-
"<b.,%2> ($1) [root] -> [root] <a.,%2> ($1)",
245-
// definition of `foo`
246-
"<b.foo,%2> ($1) [root] -> [b.py(6) definition foo] <%2> ($1)",
247-
],
175+
crate::can_find_partial_paths_in_file::CYCLIC_IMPORTS_PYTHON_B_PATHS,
248176
);
249177
}
250178

@@ -254,21 +182,7 @@ fn cyclic_imports_rust() {
254182
check_partial_paths_in_file(
255183
&graph,
256184
"test.rs",
257-
// NOTE: Because everything in this example is local to one file, there aren't any partial
258-
// paths involving the root node.
259-
&[
260-
// reference to `a` in `main` function
261-
"<%1> ($1) [test.rs(103) reference a] -> [test.rs(201) definition a] <%1> ($1)",
262-
// reference to `a` in `b` function
263-
"<%1> ($1) [test.rs(307) reference a] -> [test.rs(201) definition a] <%1> ($1)",
264-
// reference to `b` in `a` function
265-
"<%1> ($1) [test.rs(206) reference b] -> [test.rs(301) definition b] <%1> ($1)",
266-
// reference to `FOO` in `main` can resolve either to `a::BAR` or `b::FOO`
267-
"<%1> ($1) [test.rs(101) reference FOO] -> [test.rs(204) definition BAR] <%1> ($1)",
268-
"<%1> ($1) [test.rs(101) reference FOO] -> [test.rs(304) definition FOO] <%1> ($1)",
269-
// reference to `BAR` in `b` resolves _only_ to `a::BAR`
270-
"<%1> ($1) [test.rs(305) reference BAR] -> [test.rs(204) definition BAR] <%1> ($1)",
271-
],
185+
crate::can_find_partial_paths_in_file::CYCLIC_IMPORTS_RUST_PATHS,
272186
);
273187
}
274188

@@ -278,37 +192,16 @@ fn sequenced_import_star() {
278192
check_partial_paths_in_file(
279193
&graph,
280194
"main.py",
281-
&[
282-
// definition of `__main__` module
283-
"<__main__,%1> ($1) [root] -> [main.py(0) definition __main__] <%1> ($1)",
284-
// reference to `a` in import statement
285-
"<%1> ($1) [main.py(8) reference a] -> [root] <a,%1> ($1)",
286-
// `from a import *` means we can rewrite any lookup of `__main__.*` → `a.*`
287-
"<__main__.,%2> ($1) [root] -> [root] <a.,%2> ($1)",
288-
// reference to `foo` becomes `a.foo` because of import statement
289-
"<%1> ($1) [main.py(6) reference foo] -> [root] <a.foo,%1> ($1)",
290-
],
195+
crate::can_find_partial_paths_in_file::SEQUENCED_IMPORT_STAR_MAIN_PATHS,
291196
);
292197
check_partial_paths_in_file(
293198
&graph,
294199
"a.py",
295-
&[
296-
// definition of `a` module
297-
"<a,%1> ($1) [root] -> [a.py(0) definition a] <%1> ($1)",
298-
// reference to `b` in import statement
299-
"<%1> ($1) [a.py(6) reference b] -> [root] <b,%1> ($1)",
300-
// `from b import *` means we can rewrite any lookup of `a.*` → `b.*`
301-
"<a.,%2> ($1) [root] -> [root] <b.,%2> ($1)",
302-
],
200+
crate::can_find_partial_paths_in_file::SEQUENCED_IMPORT_STAR_A_PATHS,
303201
);
304202
check_partial_paths_in_file(
305203
&graph,
306204
"b.py",
307-
&[
308-
// definition of `b` module
309-
"<b,%1> ($1) [root] -> [b.py(0) definition b] <%1> ($1)",
310-
// definition of `foo` inside of `b` module
311-
"<b.foo,%2> ($1) [root] -> [b.py(5) definition foo] <%2> ($1)",
312-
],
205+
crate::can_find_partial_paths_in_file::SEQUENCED_IMPORT_STAR_B_PATHS,
313206
);
314207
}

0 commit comments

Comments
 (0)