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

Commit 62b4c59

Browse files
author
Hendrik van Antwerpen
committed
Generalize ForwardPartialPathStitcher
1 parent cb18a6a commit 62b4c59

File tree

6 files changed

+111
-75
lines changed

6 files changed

+111
-75
lines changed

stack-graphs/src/assert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ impl Assertion {
178178
let mut actual_paths = Vec::new();
179179
for reference in &references {
180180
let mut reference_paths = Vec::new();
181-
ForwardPartialPathStitcher::find_all_complete_partial_paths(
181+
ForwardPartialPathStitcher::<Handle<PartialPath>>::find_all_complete_partial_paths(
182182
graph,
183183
partials,
184184
db,

stack-graphs/src/c.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,12 +1404,12 @@ struct InternalForwardPartialPathStitcher {
14041404
previous_phase_partial_paths: *const PartialPath,
14051405
previous_phase_partial_paths_length: usize,
14061406
is_complete: bool,
1407-
stitcher: ForwardPartialPathStitcher,
1407+
stitcher: ForwardPartialPathStitcher<Handle<PartialPath>>,
14081408
}
14091409

14101410
impl InternalForwardPartialPathStitcher {
14111411
fn new(
1412-
stitcher: ForwardPartialPathStitcher,
1412+
stitcher: ForwardPartialPathStitcher<Handle<PartialPath>>,
14131413
partials: &mut PartialPaths,
14141414
) -> InternalForwardPartialPathStitcher {
14151415
let mut this = InternalForwardPartialPathStitcher {

stack-graphs/src/cycles.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
3232
use enumset::EnumSet;
3333
use smallvec::SmallVec;
34-
use std::borrow::Cow;
3534
use std::collections::HashMap;
3635

3736
use crate::arena::Handle;
@@ -129,22 +128,22 @@ where
129128
// ----------------------------------------------------------------------------
130129
// Cycle detector
131130

132-
pub trait Index<'a, H, A>
131+
pub trait Index<H, A>
133132
where
134-
A: Appendable + Clone + 'a,
133+
A: Appendable + Clone,
135134
{
136-
fn get(&'a self, handle: &H) -> Cow<'a, A>;
135+
fn get<'a>(&'a self, handle: &'a H) -> &'a A;
137136
}
138137

139-
impl<'a> Index<'a, Handle<PartialPath>, PartialPath> for Database {
140-
fn get(&'a self, handle: &Handle<PartialPath>) -> Cow<'a, PartialPath> {
141-
Cow::Borrowed(&self[*handle])
138+
impl Index<Handle<PartialPath>, PartialPath> for Database {
139+
fn get<'a>(&'a self, handle: &'a Handle<PartialPath>) -> &'a PartialPath {
140+
&self[*handle]
142141
}
143142
}
144143

145-
impl<'a, A: Appendable + Clone + 'a> Index<'a, A, A> for () {
146-
fn get(&self, value: &A) -> Cow<'a, A> {
147-
Cow::Owned(value.clone())
144+
impl<A: Appendable + Clone> Index<A, A> for () {
145+
fn get<'a>(&'a self, value: &'a A) -> &'a A {
146+
value
148147
}
149148
}
150149

@@ -175,7 +174,7 @@ where
175174
) -> Result<(), PathResolutionError>
176175
where
177176
A: Appendable + Clone + 'a,
178-
Db: Index<'a, H, A>,
177+
Db: Index<H, A>,
179178
{
180179
match self {
181180
Self::Path(other) => other.append_to(graph, partials, path),
@@ -186,7 +185,7 @@ where
186185
fn start_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
187186
where
188187
A: Appendable + Clone + 'a,
189-
Db: Index<'a, H, A>,
188+
Db: Index<H, A>,
190189
{
191190
match self {
192191
Self::Path(path) => path.start_node,
@@ -197,7 +196,7 @@ where
197196
fn end_node<'a, A, Db>(&self, db: &'a Db) -> Handle<Node>
198197
where
199198
A: Appendable + Clone + 'a,
200-
Db: Index<'a, H, A>,
199+
Db: Index<H, A>,
201200
{
202201
match self {
203202
Self::Path(path) => path.end_node,
@@ -247,7 +246,7 @@ where
247246
) -> Result<EnumSet<Cyclicity>, PathResolutionError>
248247
where
249248
A: Appendable + Clone + 'a,
250-
Db: Index<'a, H, A>,
249+
Db: Index<H, A>,
251250
{
252251
let mut cycles = EnumSet::new();
253252

stack-graphs/src/stitching.rs

Lines changed: 90 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,34 @@ impl Appendable for PartialPath {
153153
}
154154
}
155155

156+
//-------------------------------------------------------------------------------------------------
157+
// Candidates
158+
159+
pub trait Candidates<H> {
160+
fn find_candidates<R>(
161+
&mut self,
162+
graph: &StackGraph,
163+
partials: &mut PartialPaths,
164+
path: &PartialPath,
165+
result: &mut R,
166+
) where
167+
R: std::iter::Extend<H>;
168+
}
169+
170+
impl Candidates<Edge> for () {
171+
fn find_candidates<R>(
172+
&mut self,
173+
graph: &StackGraph,
174+
_partials: &mut PartialPaths,
175+
path: &PartialPath,
176+
result: &mut R,
177+
) where
178+
R: std::iter::Extend<Edge>,
179+
{
180+
result.extend(graph.outgoing_edges(path.end_node));
181+
}
182+
}
183+
156184
//-------------------------------------------------------------------------------------------------
157185
// Databases
158186

@@ -420,6 +448,31 @@ impl Index<Handle<PartialPath>> for Database {
420448
}
421449
}
422450

451+
impl Candidates<Handle<PartialPath>> for Database {
452+
fn find_candidates<R>(
453+
&mut self,
454+
graph: &StackGraph,
455+
partials: &mut PartialPaths,
456+
path: &PartialPath,
457+
result: &mut R,
458+
) where
459+
R: std::iter::Extend<Handle<PartialPath>>,
460+
{
461+
if graph[path.end_node].is_root() {
462+
// The join node is root, so there's no need to use half-open symbol stacks here, as we
463+
// do for [`PartialPath::concatenate`][].
464+
let key = SymbolStackKey::from_partial_symbol_stack(
465+
partials,
466+
self,
467+
path.symbol_stack_postcondition,
468+
);
469+
self.find_candidate_partial_paths_from_root(graph, partials, Some(key), result);
470+
} else {
471+
self.find_candidate_partial_paths_from_node(graph, partials, path.end_node, result);
472+
}
473+
}
474+
}
475+
423476
/// The key type that we use to find partial paths that start from the root node and have a
424477
/// particular symbol stack as their precondition.
425478
#[derive(Clone, Copy)]
@@ -553,30 +606,23 @@ impl<'a> Display for DisplaySymbolStackKey<'a> {
553606
/// completion, using the [`find_all_complete_partial_paths`][] method.
554607
///
555608
/// [`find_all_complete_partial_paths`]: #method.find_all_complete_partial_paths
556-
pub struct ForwardPartialPathStitcher {
557-
candidate_partial_paths: Vec<Handle<PartialPath>>,
558-
queue: VecDeque<(PartialPath, AppendingCycleDetector<Handle<PartialPath>>)>,
609+
pub struct ForwardPartialPathStitcher<H> {
610+
candidates: Vec<H>,
611+
queue: VecDeque<(PartialPath, AppendingCycleDetector<H>)>,
559612
// next_iteration is a tuple of queues instead of an queue of tuples so that the path queue
560613
// can be cheaply exposed through the C API as a continuous memory block
561-
next_iteration: (
562-
VecDeque<PartialPath>,
563-
VecDeque<AppendingCycleDetector<Handle<PartialPath>>>,
564-
),
565-
appended_paths: Appendables<Handle<PartialPath>>,
614+
next_iteration: (VecDeque<PartialPath>, VecDeque<AppendingCycleDetector<H>>),
615+
appended_paths: Appendables<H>,
566616
similar_path_detector: Option<SimilarPathDetector<PartialPath>>,
567617
max_work_per_phase: usize,
568618
#[cfg(feature = "copious-debugging")]
569619
phase_number: usize,
570620
}
571621

572-
impl ForwardPartialPathStitcher {
622+
impl<H> ForwardPartialPathStitcher<H> {
573623
/// Creates a new forward partial path stitcher that is "seeded" with a set of starting stack
574624
/// graph nodes.
575-
pub fn from_nodes<I>(
576-
graph: &StackGraph,
577-
partials: &mut PartialPaths,
578-
starting_nodes: I,
579-
) -> ForwardPartialPathStitcher
625+
pub fn from_nodes<I>(graph: &StackGraph, partials: &mut PartialPaths, starting_nodes: I) -> Self
580626
where
581627
I: IntoIterator<Item = Handle<Node>>,
582628
{
@@ -590,8 +636,8 @@ impl ForwardPartialPathStitcher {
590636
(p, c)
591637
})
592638
.unzip();
593-
ForwardPartialPathStitcher {
594-
candidate_partial_paths: Vec::new(),
639+
Self {
640+
candidates: Vec::new(),
595641
queue: VecDeque::new(),
596642
next_iteration,
597643
appended_paths,
@@ -609,7 +655,7 @@ impl ForwardPartialPathStitcher {
609655
_graph: &StackGraph,
610656
partials: &mut PartialPaths,
611657
initial_partial_paths: Vec<PartialPath>,
612-
) -> ForwardPartialPathStitcher {
658+
) -> Self {
613659
let mut appended_paths = Appendables::new();
614660
let next_iteration = initial_partial_paths
615661
.into_iter()
@@ -619,8 +665,8 @@ impl ForwardPartialPathStitcher {
619665
(p, c)
620666
})
621667
.unzip();
622-
ForwardPartialPathStitcher {
623-
candidate_partial_paths: Vec::new(),
668+
Self {
669+
candidates: Vec::new(),
624670
queue: VecDeque::new(),
625671
next_iteration,
626672
appended_paths,
@@ -631,7 +677,9 @@ impl ForwardPartialPathStitcher {
631677
phase_number: 1,
632678
}
633679
}
680+
}
634681

682+
impl<H: Clone> ForwardPartialPathStitcher<H> {
635683
/// Returns an iterator of all of the (possibly incomplete) partial paths that were encountered
636684
/// during the most recent phase of the algorithm.
637685
pub fn previous_phase_partial_paths(&self) -> impl Iterator<Item = &PartialPath> + '_ {
@@ -676,60 +724,41 @@ impl ForwardPartialPathStitcher {
676724
/// Attempts to extend one partial path as part of the algorithm. When calling this function,
677725
/// you are responsible for ensuring that `db` already contains all of the possible partial
678726
/// paths that we might want to extend `partial_path` with.
679-
fn stitch_partial_path(
727+
fn stitch_partial_path<'a, A, Db>(
680728
&mut self,
681729
graph: &StackGraph,
682730
partials: &mut PartialPaths,
683-
db: &mut Database,
731+
db: &mut Db,
684732
partial_path: &PartialPath,
685-
cycle_detector: AppendingCycleDetector<Handle<PartialPath>>,
686-
) -> usize {
687-
self.candidate_partial_paths.clear();
688-
if graph[partial_path.end_node].is_root() {
689-
// The join node is root, so there's no need to use half-open symbol stacks here, as we
690-
// do for [`PartialPath::concatenate`][].
691-
let key = SymbolStackKey::from_partial_symbol_stack(
692-
partials,
693-
db,
694-
partial_path.symbol_stack_postcondition,
695-
);
696-
db.find_candidate_partial_paths_from_root(
697-
graph,
698-
partials,
699-
Some(key),
700-
&mut self.candidate_partial_paths,
701-
);
702-
} else {
703-
db.find_candidate_partial_paths_from_node(
704-
graph,
705-
partials,
706-
partial_path.end_node,
707-
&mut self.candidate_partial_paths,
708-
);
709-
}
733+
cycle_detector: AppendingCycleDetector<H>,
734+
) -> usize
735+
where
736+
A: Appendable + Clone + 'a,
737+
Db: Candidates<H> + crate::cycles::Index<H, A>,
738+
{
739+
self.candidates.clear();
740+
db.find_candidates(graph, partials, partial_path, &mut self.candidates);
710741

711-
let extension_count = self.candidate_partial_paths.len();
742+
let extension_count = self.candidates.len();
712743
self.next_iteration.0.reserve(extension_count);
713744
self.next_iteration.1.reserve(extension_count);
714-
for extension in &self.candidate_partial_paths {
715-
let mut extension_path = db[*extension].clone();
745+
for extension in &self.candidates {
746+
let extension_path = db.get(extension);
716747
copious_debugging!(" Extend {}", partial_path.display(graph, partials));
717748
copious_debugging!(" with {}", extension_path.display(graph, partials));
718-
extension_path.ensure_no_overlapping_variables(partials, partial_path);
719-
copious_debugging!(" -> {}", extension_path.display(graph, partials));
720749

721750
let mut new_partial_path = partial_path.clone();
722751
let mut new_cycle_detector = cycle_detector.clone();
723752
// If there are errors concatenating these partial paths, or resolving the resulting
724753
// partial path, just skip the extension — it's not a fatal error.
725754
#[cfg_attr(not(feature = "copious-debugging"), allow(unused_variables))]
726755
{
727-
if let Err(err) = new_partial_path.concatenate(graph, partials, &extension_path) {
756+
if let Err(err) = extension_path.append_to(graph, partials, &mut new_partial_path) {
728757
copious_debugging!(" is invalid: {:?}", err);
729758
continue;
730759
}
731760
copious_debugging!(" is {}", new_partial_path.display(graph, partials));
732-
new_cycle_detector.append(&mut self.appended_paths, *extension);
761+
new_cycle_detector.append(&mut self.appended_paths, extension.clone());
733762
let cycles = new_cycle_detector
734763
.is_cyclic(graph, partials, db, &mut self.appended_paths)
735764
.expect("cyclic test failed when stitching partial paths");
@@ -773,12 +802,15 @@ impl ForwardPartialPathStitcher {
773802
/// list of the (possibly incomplete) partial paths that were encountered during this phase.
774803
///
775804
/// [`previous_phase_partial_paths`]: #method.previous_phase_partial_paths
776-
pub fn process_next_phase(
805+
pub fn process_next_phase<'a, A, Db>(
777806
&mut self,
778807
graph: &StackGraph,
779808
partials: &mut PartialPaths,
780-
db: &mut Database,
781-
) {
809+
db: &mut Db,
810+
) where
811+
A: Appendable + Clone + 'a,
812+
Db: Candidates<H> + crate::cycles::Index<H, A>,
813+
{
782814
copious_debugging!("==> Start phase {}", self.phase_number);
783815
self.queue.extend(
784816
self.next_iteration
@@ -811,7 +843,9 @@ impl ForwardPartialPathStitcher {
811843
self.phase_number += 1;
812844
}
813845
}
846+
}
814847

848+
impl<H> ForwardPartialPathStitcher<H> {
815849
/// Returns all of the complete partial paths that are reachable from a set of starting nodes,
816850
/// building them up by stitching together partial paths from this database.
817851
///

stack-graphs/tests/it/can_jump_to_definition_with_forward_partial_path_stitching.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
use std::collections::BTreeSet;
99

1010
use pretty_assertions::assert_eq;
11+
use stack_graphs::arena::Handle;
1112
use stack_graphs::graph::StackGraph;
13+
use stack_graphs::partial::PartialPath;
1214
use stack_graphs::partial::PartialPaths;
1315
use stack_graphs::stitching::Database;
1416
use stack_graphs::stitching::ForwardPartialPathStitcher;
@@ -38,7 +40,7 @@ fn check_jump_to_definition(graph: &StackGraph, expected_partial_paths: &[&str])
3840
.iter_nodes()
3941
.filter(|handle| graph[*handle].is_reference());
4042
let mut complete_partial_paths = Vec::new();
41-
ForwardPartialPathStitcher::find_all_complete_partial_paths(
43+
ForwardPartialPathStitcher::<Handle<PartialPath>>::find_all_complete_partial_paths(
4244
graph,
4345
&mut partials,
4446
&mut db,

tree-sitter-stack-graphs/src/cli/test.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use itertools::Itertools;
1313
use stack_graphs::arena::Handle;
1414
use stack_graphs::graph::File;
1515
use stack_graphs::graph::StackGraph;
16+
use stack_graphs::partial::PartialPath;
1617
use stack_graphs::partial::PartialPaths;
1718
use stack_graphs::serde::Filter;
1819
use stack_graphs::stitching::Database;
@@ -446,7 +447,7 @@ impl TestArgs {
446447
.filter(|n| filter.include_node(graph, n))
447448
.collect::<Vec<_>>();
448449
let mut paths = Vec::new();
449-
ForwardPartialPathStitcher::find_all_complete_partial_paths(
450+
ForwardPartialPathStitcher::<Handle<PartialPath>>::find_all_complete_partial_paths(
450451
graph,
451452
partials,
452453
db,

0 commit comments

Comments
 (0)