stack_graphs/
graph.rs

1// -*- coding: utf-8 -*-
2// ------------------------------------------------------------------------------------------------
3// Copyright © 2021, stack-graphs authors.
4// Licensed under either of Apache License, Version 2.0, or MIT license, at your option.
5// Please see the LICENSE-APACHE or LICENSE-MIT files in this distribution for license details.
6// ------------------------------------------------------------------------------------------------
7
8//! Defines the structure of a stack graph.
9//!
10//! This module contains all of the types that you need to define the structure of a particular
11//! stack graph.
12//!
13//! The stack graph as a whole lives in an instance of [`StackGraph`][].  This type contains
14//! several [`Arena`s][`Arena`], which are used to manage the life cycle of the data instances that
15//! comprise the stack graph.  You cannot delete anything from the stack graph; all of its contents
16//! are dropped in a single operation when the graph itself is dropped.
17//!
18//! [`Arena`]: ../arena/struct.Arena.html
19//! [`StackGraph`]: struct.StackGraph.html
20//!
21//! There are several different kinds of node that can appear in a stack graph.  As we search for a
22//! path representing a name binding, each kind of node has different rules for how it interacts
23//! with the symbol and scope stacks:
24//!
25//!   - the singleton [_root node_][`RootNode`], which allows name binding paths to cross between
26//!     files
27//!   - [_scope_][`ScopeNode`] nodes, which define the name binding structure within a single file
28//!   - [_push symbol_][`PushSymbolNode`] and [_push scoped symbol_][`PushScopedSymbolNode`] nodes,
29//!     which push onto the symbol stack new things for us to look for
30//!   - [_pop symbol_][`PopSymbolNode`] and [_pop scoped symbol_][`PopScopedSymbolNode`] nodes,
31//!     which pop things off the symbol stack once they've been found
32//!   - [_drop scopes_][`DropScopesNode`] and [_jump to scope_][`JumpToNode`] nodes, which
33//!     manipulate the scope stack
34//!
35//! [`DropScopesNode`]: struct.DropScopesNode.html
36//! [`JumpToNode`]: struct.JumpToNode.html
37//! [`PushScopedSymbolNode`]: struct.PushScopedSymbolNode.html
38//! [`PushSymbolNode`]: struct.PushSymbolNode.html
39//! [`PopScopedSymbolNode`]: struct.PopScopedSymbolNode.html
40//! [`PopSymbolNode`]: struct.PopSymbolNode.html
41//! [`RootNode`]: struct.RootNode.html
42//! [`ScopeNode`]: struct.ScopeNode.html
43//!
44//! All nodes except for the singleton _root node_ and _jump to scope_ node belong to
45//! [files][`File`].
46//!
47//! Nodes are connected via [edges][`Edge`].
48//!
49//! [`Edge`]: struct.Edge.html
50//! [`File`]: struct.File.html
51
52use std::collections::HashMap;
53use std::fmt::Display;
54use std::num::NonZeroU32;
55use std::ops::Index;
56use std::ops::IndexMut;
57
58use controlled_option::ControlledOption;
59use either::Either;
60use fxhash::FxHashMap;
61use smallvec::SmallVec;
62
63use crate::arena::Arena;
64use crate::arena::Handle;
65use crate::arena::SupplementalArena;
66
67//-------------------------------------------------------------------------------------------------
68// String content
69
70#[repr(C)]
71struct InternedStringContent {
72    // See InternedStringArena below for how we fill in these fields safely.
73    start: *const u8,
74    len: usize,
75}
76
77const INITIAL_STRING_CAPACITY: usize = 512;
78
79/// The content of each interned string is stored in one of the buffers inside of a
80/// `InternedStringArena` instance, following the trick [described by Aleksey Kladov][interner].
81///
82/// The buffers stored in this type are preallocated, and are never allowed to grow.  That ensures
83/// that pointers into the buffer are stable, as long as the buffer has not been destroyed.
84/// (`InternedStringContent` instances are also stored in an arena, ensuring that the strings that
85/// we hand out don't outlive the buffers.)
86///
87/// [interner]: https://matklad.github.io/2020/03/22/fast-simple-rust-interner.html
88struct InternedStringArena {
89    current_buffer: Vec<u8>,
90    full_buffers: Vec<Vec<u8>>,
91}
92
93impl InternedStringArena {
94    fn new() -> InternedStringArena {
95        InternedStringArena {
96            current_buffer: Vec::with_capacity(INITIAL_STRING_CAPACITY),
97            full_buffers: Vec::new(),
98        }
99    }
100
101    // Adds a new string.  This does not check whether we've already stored a string with the same
102    // content; that is handled down below in `StackGraph::add_symbol` and `add_file`.
103    fn add(&mut self, value: &str) -> InternedStringContent {
104        // Is there enough room in current_buffer to hold this string?
105        let value = value.as_bytes();
106        let len = value.len();
107        let capacity = self.current_buffer.capacity();
108        let remaining_capacity = capacity - self.current_buffer.len();
109        if len > remaining_capacity {
110            // If not, move current_buffer over into full_buffers (so that we hang onto it until
111            // we're dropped) and allocate a new current_buffer that's at least big enough to hold
112            // this string.
113            let new_capacity = (capacity.max(len) + 1).next_power_of_two();
114            let new_buffer = Vec::with_capacity(new_capacity);
115            let old_buffer = std::mem::replace(&mut self.current_buffer, new_buffer);
116            self.full_buffers.push(old_buffer);
117        }
118
119        // Copy the string's content into current_buffer and return a pointer to it.  That pointer
120        // is stable since we never allow the current_buffer to be resized — once we run out of
121        // room, we allocate a _completely new buffer_ to replace it.
122        let start_index = self.current_buffer.len();
123        self.current_buffer.extend_from_slice(value);
124        let start = unsafe { self.current_buffer.as_ptr().add(start_index) };
125        InternedStringContent { start, len }
126    }
127}
128
129impl InternedStringContent {
130    /// Returns the content of this string as a `str`.  This is safe as long as the lifetime of the
131    /// InternedStringContent is outlived by the lifetime of the InternedStringArena that holds its
132    /// data.  That is guaranteed because we store the InternedStrings in an Arena alongside the
133    /// InternedStringArena, and only hand out references to them.
134    fn as_str(&self) -> &str {
135        unsafe {
136            let bytes = std::slice::from_raw_parts(self.start, self.len);
137            std::str::from_utf8_unchecked(bytes)
138        }
139    }
140
141    // Returns a supposedly 'static reference to the string's data.  The string data isn't really
142    // static, but we are careful only to use this as a key in the HashMap that StackGraph uses to
143    // track whether we've stored a particular symbol already.  That HashMap lives alongside the
144    // InternedStringArena that holds the data, so we can get away with a technically incorrect
145    // 'static lifetime here.  As an extra precaution, this method is is marked as unsafe so that
146    // we don't inadvertently call it from anywhere else in the crate.
147    unsafe fn as_hash_key(&self) -> &'static str {
148        let bytes = std::slice::from_raw_parts(self.start, self.len);
149        std::str::from_utf8_unchecked(bytes)
150    }
151}
152
153unsafe impl Send for InternedStringContent {}
154unsafe impl Sync for InternedStringContent {}
155
156//-------------------------------------------------------------------------------------------------
157// Symbols
158
159/// A name that we are trying to resolve using stack graphs.
160///
161/// This typically represents a portion of an identifier as it appears in the source language.  It
162/// can also represent some other "operation" that can occur in source code, and which needs to be
163/// modeled in a stack graph — for instance, many languages will use a "fake" symbol named `.` to
164/// represent member access.
165///
166/// We deduplicate `Symbol` instances in a `StackGraph` — that is, we ensure that there are never
167/// multiple `Symbol` instances with the same content.  That means that you can compare _handles_
168/// to symbols using simple equality, without having to dereference into the `StackGraph` arena.
169#[repr(C)]
170pub struct Symbol {
171    content: InternedStringContent,
172}
173
174impl Symbol {
175    pub fn as_str(&self) -> &str {
176        self.content.as_str()
177    }
178}
179
180impl PartialEq<&str> for Symbol {
181    fn eq(&self, other: &&str) -> bool {
182        self.as_str() == *other
183    }
184}
185
186impl StackGraph {
187    /// Adds a symbol to the stack graph, ensuring that there's only ever one copy of a particular
188    /// symbol stored in the graph.
189    pub fn add_symbol<S: AsRef<str> + ?Sized>(&mut self, symbol: &S) -> Handle<Symbol> {
190        let symbol = symbol.as_ref();
191        if let Some(handle) = self.symbol_handles.get(symbol) {
192            return *handle;
193        }
194
195        let interned = self.interned_strings.add(symbol);
196        let hash_key = unsafe { interned.as_hash_key() };
197        let handle = self.symbols.add(Symbol { content: interned });
198        self.symbol_handles.insert(hash_key, handle);
199        handle
200    }
201
202    /// Returns an iterator over all of the handles of all of the symbols in this stack graph.
203    /// (Note that because we're only returning _handles_, this iterator does not retain a
204    /// reference to the `StackGraph`.)
205    pub fn iter_symbols(&self) -> impl Iterator<Item = Handle<Symbol>> {
206        self.symbols.iter_handles()
207    }
208}
209
210impl Index<Handle<Symbol>> for StackGraph {
211    type Output = str;
212    #[inline(always)]
213    fn index(&self, handle: Handle<Symbol>) -> &str {
214        self.symbols.get(handle).as_str()
215    }
216}
217
218#[doc(hidden)]
219pub struct DisplaySymbol<'a> {
220    wrapped: Handle<Symbol>,
221    graph: &'a StackGraph,
222}
223
224impl<'a> Display for DisplaySymbol<'a> {
225    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
226        write!(f, "{}", &self.graph[self.wrapped])
227    }
228}
229
230impl Handle<Symbol> {
231    pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
232        DisplaySymbol {
233            wrapped: self,
234            graph,
235        }
236    }
237}
238
239//-------------------------------------------------------------------------------------------------
240// Interned strings
241
242/// Arbitrary string content associated with some part of a stack graph.
243#[repr(C)]
244pub struct InternedString {
245    content: InternedStringContent,
246}
247
248impl InternedString {
249    fn as_str(&self) -> &str {
250        self.content.as_str()
251    }
252}
253
254impl PartialEq<&str> for InternedString {
255    fn eq(&self, other: &&str) -> bool {
256        self.as_str() == *other
257    }
258}
259
260impl StackGraph {
261    /// Adds an interned string to the stack graph, ensuring that there's only ever one copy of a
262    /// particular string stored in the graph.
263    pub fn add_string<S: AsRef<str> + ?Sized>(&mut self, string: &S) -> Handle<InternedString> {
264        let string = string.as_ref();
265        if let Some(handle) = self.string_handles.get(string) {
266            return *handle;
267        }
268
269        let interned = self.interned_strings.add(string);
270        let hash_key = unsafe { interned.as_hash_key() };
271        let handle = self.strings.add(InternedString { content: interned });
272        self.string_handles.insert(hash_key, handle);
273        handle
274    }
275
276    /// Returns an iterator over all of the handles of all of the interned strings in this stack
277    /// graph. (Note that because we're only returning _handles_, this iterator does not retain a
278    /// reference to the `StackGraph`.)
279    pub fn iter_strings(&self) -> impl Iterator<Item = Handle<InternedString>> {
280        self.strings.iter_handles()
281    }
282}
283
284impl Index<Handle<InternedString>> for StackGraph {
285    type Output = str;
286    #[inline(always)]
287    fn index(&self, handle: Handle<InternedString>) -> &str {
288        self.strings.get(handle).as_str()
289    }
290}
291
292#[doc(hidden)]
293pub struct DisplayInternedString<'a> {
294    wrapped: Handle<InternedString>,
295    graph: &'a StackGraph,
296}
297
298impl<'a> Display for DisplayInternedString<'a> {
299    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
300        write!(f, "{}", &self.graph[self.wrapped])
301    }
302}
303
304impl Handle<InternedString> {
305    pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
306        DisplayInternedString {
307            wrapped: self,
308            graph,
309        }
310    }
311}
312
313//-------------------------------------------------------------------------------------------------
314// Files
315
316/// A source file that we have extracted stack graph data from.
317///
318/// It's up to you to choose what names to use for your files, but they must be unique within a
319/// stack graph.  If you are analyzing files from the local filesystem, the file's path is a good
320/// choice.  If your files belong to packages or repositories, they should include the package or
321/// repository IDs to make sure that files in different packages or repositories don't clash with
322/// each other.
323pub struct File {
324    /// The name of this source file.
325    name: InternedStringContent,
326}
327
328impl File {
329    pub fn name(&self) -> &str {
330        self.name.as_str()
331    }
332}
333
334impl StackGraph {
335    /// Adds a file to the stack graph.  There can only ever be one file with a particular name in
336    /// the graph.  If a file with the requested name already exists, we return `Err`; if it
337    /// doesn't already exist, we return `Ok`.  In both cases, the value of the result is the
338    /// file's handle.
339    pub fn add_file<S: AsRef<str> + ?Sized>(
340        &mut self,
341        name: &S,
342    ) -> Result<Handle<File>, Handle<File>> {
343        let name = name.as_ref();
344        if let Some(handle) = self.file_handles.get(name) {
345            return Err(*handle);
346        }
347
348        let interned = self.interned_strings.add(name);
349        let hash_key = unsafe { interned.as_hash_key() };
350        let handle = self.files.add(File { name: interned });
351        self.file_handles.insert(hash_key, handle);
352        Ok(handle)
353    }
354
355    /// Adds a file to the stack graph, returning its handle.  There can only ever be one file with
356    /// a particular name in the graph, so if you call this multiple times with the same name,
357    /// you'll get the same handle each time.
358    #[inline(always)]
359    pub fn get_or_create_file<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> Handle<File> {
360        self.add_file(name).unwrap_or_else(|handle| handle)
361    }
362
363    /// Returns the file with a particular name, if it exists.
364    pub fn get_file<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<Handle<File>> {
365        let name = name.as_ref();
366        self.file_handles.get(name).copied()
367    }
368}
369
370impl StackGraph {
371    /// Returns an iterator of all of the nodes that belong to a particular file.  Note that this
372    /// does **_not_** include the singleton _root_ or _jump to scope_ nodes.
373    pub fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
374        self.node_id_handles.nodes_for_file(file)
375    }
376
377    /// Returns an iterator over all of the handles of all of the files in this stack graph.  (Note
378    /// that because we're only returning _handles_, this iterator does not retain a reference to
379    /// the `StackGraph`.)
380    pub fn iter_files(&self) -> impl Iterator<Item = Handle<File>> + '_ {
381        self.files.iter_handles()
382    }
383}
384
385impl Display for File {
386    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
387        write!(f, "{}", self.name())
388    }
389}
390
391impl Index<Handle<File>> for StackGraph {
392    type Output = File;
393    #[inline(always)]
394    fn index(&self, handle: Handle<File>) -> &File {
395        &self.files.get(handle)
396    }
397}
398
399#[doc(hidden)]
400pub struct DisplayFile<'a> {
401    wrapped: Handle<File>,
402    graph: &'a StackGraph,
403}
404
405impl<'a> Display for DisplayFile<'a> {
406    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
407        write!(f, "{}", self.graph[self.wrapped])
408    }
409}
410
411impl Handle<File> {
412    pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
413        DisplayFile {
414            wrapped: self,
415            graph,
416        }
417    }
418}
419
420//-------------------------------------------------------------------------------------------------
421// Nodes
422
423/// Uniquely identifies a node in a stack graph.
424///
425/// Each node (except for the _root node_ and _jump to scope_ node) lives in a file, and has a
426/// _local ID_ that must be unique within its file.
427#[repr(C)]
428#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
429pub struct NodeID {
430    file: ControlledOption<Handle<File>>,
431    local_id: u32,
432}
433
434pub(crate) const ROOT_NODE_ID: u32 = 1;
435pub(crate) const JUMP_TO_NODE_ID: u32 = 2;
436
437impl NodeID {
438    /// Returns the ID of the singleton _root node_.
439    #[inline(always)]
440    pub fn root() -> NodeID {
441        NodeID {
442            file: ControlledOption::none(),
443            local_id: ROOT_NODE_ID,
444        }
445    }
446
447    /// Returns the ID of the singleton _jump to scope_ node.
448    #[inline(always)]
449    pub fn jump_to() -> NodeID {
450        NodeID {
451            file: ControlledOption::none(),
452            local_id: JUMP_TO_NODE_ID,
453        }
454    }
455
456    /// Creates a new file-local node ID.
457    #[inline(always)]
458    pub fn new_in_file(file: Handle<File>, local_id: u32) -> NodeID {
459        NodeID {
460            file: ControlledOption::some(file),
461            local_id,
462        }
463    }
464
465    /// Returns whether this ID refers to the singleton _root node_.
466    #[inline(always)]
467    pub fn is_root(self) -> bool {
468        self.file.is_none() && self.local_id == ROOT_NODE_ID
469    }
470
471    /// Returns whether this ID refers to the singleton _jump to scope_ node.
472    #[inline(always)]
473    pub fn is_jump_to(self) -> bool {
474        self.file.is_none() && self.local_id == JUMP_TO_NODE_ID
475    }
476
477    /// Returns the file that this node belongs to.  Returns `None` for the singleton _root_ and
478    /// _jump to scope_ nodes, which belong to all files.
479    #[inline(always)]
480    pub fn file(self) -> Option<Handle<File>> {
481        self.file.into()
482    }
483
484    /// Returns the local ID of this node within its file.  Panics if this node ID refers to the
485    /// singleton _root_ or _jump to scope_ nodes.
486    #[inline(always)]
487    pub fn local_id(self) -> u32 {
488        self.local_id
489    }
490
491    /// Returns whether this node belongs to a particular file.  Always returns `true` for the
492    /// singleton _root_ and _jump to scope_ nodes, which belong to all files.
493    #[inline(always)]
494    pub fn is_in_file(self, file: Handle<File>) -> bool {
495        match self.file.into_option() {
496            Some(this_file) => this_file == file,
497            _ => true,
498        }
499    }
500}
501
502#[doc(hidden)]
503pub struct DisplayNodeID<'a> {
504    wrapped: NodeID,
505    graph: &'a StackGraph,
506}
507
508impl<'a> Display for DisplayNodeID<'a> {
509    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
510        match self.wrapped.file.into_option() {
511            Some(file) => write!(f, "{}({})", file.display(self.graph), self.wrapped.local_id),
512            None => {
513                if self.wrapped.is_root() {
514                    write!(f, "[root]")
515                } else if self.wrapped.is_jump_to() {
516                    write!(f, "[jump]")
517                } else {
518                    unreachable!();
519                }
520            }
521        }
522    }
523}
524
525impl NodeID {
526    pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
527        DisplayNodeID {
528            wrapped: self,
529            graph,
530        }
531    }
532}
533
534/// A node in a stack graph.
535#[repr(C)]
536pub enum Node {
537    DropScopes(DropScopesNode),
538    JumpTo(JumpToNode),
539    PopScopedSymbol(PopScopedSymbolNode),
540    PopSymbol(PopSymbolNode),
541    PushScopedSymbol(PushScopedSymbolNode),
542    PushSymbol(PushSymbolNode),
543    Root(RootNode),
544    Scope(ScopeNode),
545}
546
547impl Node {
548    #[inline(always)]
549    pub fn is_exported_scope(&self) -> bool {
550        match self {
551            Node::Scope(node) => node.is_exported,
552            _ => false,
553        }
554    }
555
556    #[inline(always)]
557    pub fn is_definition(&self) -> bool {
558        match self {
559            Node::PopScopedSymbol(node) => node.is_definition,
560            Node::PopSymbol(node) => node.is_definition,
561            _ => false,
562        }
563    }
564
565    #[inline(always)]
566    pub fn is_reference(&self) -> bool {
567        match self {
568            Node::PushScopedSymbol(node) => node.is_reference,
569            Node::PushSymbol(node) => node.is_reference,
570            _ => false,
571        }
572    }
573
574    #[inline(always)]
575    pub fn is_jump_to(&self) -> bool {
576        matches!(self, Node::JumpTo(_))
577    }
578
579    #[inline(always)]
580    pub fn is_root(&self) -> bool {
581        matches!(self, Node::Root(_))
582    }
583
584    #[inline(always)]
585    pub fn is_endpoint(&self) -> bool {
586        self.is_definition() || self.is_exported_scope() || self.is_reference() || self.is_root()
587    }
588
589    /// Returns this node's symbol, if it has one.  (_Pop symbol_, _pop scoped symbol_, _push
590    /// symbol_, and _push scoped symbol_ nodes have symbols.)
591    pub fn symbol(&self) -> Option<Handle<Symbol>> {
592        match self {
593            Node::PushScopedSymbol(node) => Some(node.symbol),
594            Node::PushSymbol(node) => Some(node.symbol),
595            Node::PopScopedSymbol(node) => Some(node.symbol),
596            Node::PopSymbol(node) => Some(node.symbol),
597            _ => None,
598        }
599    }
600
601    /// Returns this node's attached scope, if it has one.  (_Push scoped symbol_ nodes have
602    /// attached scopes.)
603    pub fn scope(&self) -> Option<NodeID> {
604        match self {
605            Node::PushScopedSymbol(node) => Some(node.scope),
606            _ => None,
607        }
608    }
609
610    /// Returns the ID of this node.
611    pub fn id(&self) -> NodeID {
612        match self {
613            Node::DropScopes(node) => node.id,
614            Node::JumpTo(node) => node.id,
615            Node::PushScopedSymbol(node) => node.id,
616            Node::PushSymbol(node) => node.id,
617            Node::PopScopedSymbol(node) => node.id,
618            Node::PopSymbol(node) => node.id,
619            Node::Root(node) => node.id,
620            Node::Scope(node) => node.id,
621        }
622    }
623
624    /// Returns the file that this node belongs to.  Returns `None` for the singleton _root_ and
625    /// _jump to scope_ nodes, which belong to all files.
626    #[inline(always)]
627    pub fn file(&self) -> Option<Handle<File>> {
628        self.id().file()
629    }
630
631    /// Returns whether this node belongs to a particular file.  Always returns `true` for the
632    /// singleton _root_ and _jump to scope_ nodes, which belong to all files.
633    #[inline(always)]
634    pub fn is_in_file(&self, file: Handle<File>) -> bool {
635        self.id().is_in_file(file)
636    }
637
638    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
639        DisplayNode {
640            wrapped: self,
641            graph,
642        }
643    }
644}
645
646impl StackGraph {
647    /// Returns a handle to the stack graph's singleton _jump to scope_ node.
648    #[inline(always)]
649    pub fn jump_to_node() -> Handle<Node> {
650        Handle::new(unsafe { NonZeroU32::new_unchecked(2) })
651    }
652
653    /// Returns a handle to the stack graph's singleton _root node_.
654    #[inline(always)]
655    pub fn root_node() -> Handle<Node> {
656        Handle::new(unsafe { NonZeroU32::new_unchecked(1) })
657    }
658
659    /// Returns an unused [`NodeID`][] for the given file.
660    ///
661    /// [`NodeID`]: struct.NodeID.html
662    pub fn new_node_id(&mut self, file: Handle<File>) -> NodeID {
663        self.node_id_handles.unused_id(file)
664    }
665
666    /// Returns an iterator of all of the nodes in the graph.  (Note that because we're only
667    /// returning _handles_, this iterator does not retain a reference to the `StackGraph`.)
668    pub fn iter_nodes(&self) -> impl Iterator<Item = Handle<Node>> {
669        self.nodes.iter_handles()
670    }
671
672    /// Returns the handle to the node with a particular ID, if it exists.
673    pub fn node_for_id(&self, id: NodeID) -> Option<Handle<Node>> {
674        if id.file().is_some() {
675            self.node_id_handles.try_handle_for_id(id)
676        } else if id.is_root() {
677            Some(StackGraph::root_node())
678        } else if id.is_jump_to() {
679            Some(StackGraph::jump_to_node())
680        } else {
681            None
682        }
683    }
684
685    pub(crate) fn add_node(&mut self, id: NodeID, node: Node) -> Option<Handle<Node>> {
686        if let Some(_) = self.node_id_handles.handle_for_id(id) {
687            return None;
688        }
689        let handle = self.nodes.add(node);
690        self.node_id_handles.set_handle_for_id(id, handle);
691        Some(handle)
692    }
693
694    pub(crate) fn get_or_create_node(&mut self, id: NodeID, node: Node) -> Handle<Node> {
695        if let Some(handle) = self.node_id_handles.handle_for_id(id) {
696            return handle;
697        }
698        let handle = self.nodes.add(node);
699        self.node_id_handles.set_handle_for_id(id, handle);
700        handle
701    }
702}
703
704#[doc(hidden)]
705pub struct DisplayNode<'a> {
706    wrapped: &'a Node,
707    graph: &'a StackGraph,
708}
709
710impl<'a> Display for DisplayNode<'a> {
711    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
712        match self.wrapped {
713            Node::DropScopes(node) => node.display(self.graph).fmt(f),
714            Node::JumpTo(node) => node.fmt(f),
715            Node::PushScopedSymbol(node) => node.display(self.graph).fmt(f),
716            Node::PushSymbol(node) => node.display(self.graph).fmt(f),
717            Node::PopScopedSymbol(node) => node.display(self.graph).fmt(f),
718            Node::PopSymbol(node) => node.display(self.graph).fmt(f),
719            Node::Root(node) => node.fmt(f),
720            Node::Scope(node) => node.display(self.graph).fmt(f),
721        }
722    }
723}
724
725impl Handle<Node> {
726    pub fn display(self, graph: &StackGraph) -> impl Display + '_ {
727        DisplayNode {
728            wrapped: &graph[self],
729            graph,
730        }
731    }
732}
733
734impl Index<Handle<Node>> for StackGraph {
735    type Output = Node;
736    #[inline(always)]
737    fn index(&self, handle: Handle<Node>) -> &Node {
738        self.nodes.get(handle)
739    }
740}
741
742impl IndexMut<Handle<Node>> for StackGraph {
743    #[inline(always)]
744    fn index_mut(&mut self, handle: Handle<Node>) -> &mut Node {
745        self.nodes.get_mut(handle)
746    }
747}
748
749/// Removes everything from the current scope stack.
750#[repr(C)]
751pub struct DropScopesNode {
752    /// The unique identifier for this node.
753    pub id: NodeID,
754    _symbol: ControlledOption<Handle<Symbol>>,
755    _scope: NodeID,
756    _is_endpoint: bool,
757}
758
759impl From<DropScopesNode> for Node {
760    fn from(node: DropScopesNode) -> Node {
761        Node::DropScopes(node)
762    }
763}
764
765impl StackGraph {
766    /// Adds a _drop scopes_ node to the stack graph.
767    pub fn add_drop_scopes_node(&mut self, id: NodeID) -> Option<Handle<Node>> {
768        let node = DropScopesNode {
769            id,
770            _symbol: ControlledOption::none(),
771            _scope: NodeID::default(),
772            _is_endpoint: false,
773        };
774        self.add_node(id, node.into())
775    }
776}
777
778impl DropScopesNode {
779    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
780        DisplayDropScopesNode {
781            wrapped: self,
782            graph,
783        }
784    }
785}
786
787#[doc(hidden)]
788pub struct DisplayDropScopesNode<'a> {
789    wrapped: &'a DropScopesNode,
790    graph: &'a StackGraph,
791}
792
793impl<'a> Display for DisplayDropScopesNode<'a> {
794    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
795        if f.alternate() {
796            write!(f, "[{}]", self.wrapped.id.display(self.graph))
797        } else {
798            write!(f, "[{} drop scopes]", self.wrapped.id.display(self.graph))
799        }
800    }
801}
802
803/// The singleton "jump to" node, which allows a name binding path to jump back to another part of
804/// the graph.
805#[repr(C)]
806pub struct JumpToNode {
807    id: NodeID,
808    _symbol: ControlledOption<Handle<Symbol>>,
809    _scope: NodeID,
810    _is_endpoint: bool,
811}
812
813impl From<JumpToNode> for Node {
814    fn from(node: JumpToNode) -> Node {
815        Node::JumpTo(node)
816    }
817}
818
819impl JumpToNode {
820    fn new() -> JumpToNode {
821        JumpToNode {
822            id: NodeID::jump_to(),
823            _symbol: ControlledOption::none(),
824            _scope: NodeID::default(),
825            _is_endpoint: false,
826        }
827    }
828}
829
830impl Display for JumpToNode {
831    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
832        write!(f, "[jump to scope]")
833    }
834}
835
836/// Pops a scoped symbol from the symbol stack.  If the top of the symbol stack doesn't match the
837/// requested symbol, or if the top of the symbol stack doesn't have an attached scope list, then
838/// the path is not allowed to enter this node.
839#[repr(C)]
840pub struct PopScopedSymbolNode {
841    /// The unique identifier for this node.
842    pub id: NodeID,
843    /// The symbol to pop off the symbol stack.
844    pub symbol: Handle<Symbol>,
845    _scope: NodeID,
846    /// Whether this node represents a reference in the source language.
847    pub is_definition: bool,
848}
849
850impl From<PopScopedSymbolNode> for Node {
851    fn from(node: PopScopedSymbolNode) -> Node {
852        Node::PopScopedSymbol(node)
853    }
854}
855
856impl StackGraph {
857    /// Adds a _pop scoped symbol_ node to the stack graph.
858    pub fn add_pop_scoped_symbol_node(
859        &mut self,
860        id: NodeID,
861        symbol: Handle<Symbol>,
862        is_definition: bool,
863    ) -> Option<Handle<Node>> {
864        let node = PopScopedSymbolNode {
865            id,
866            symbol,
867            _scope: NodeID::default(),
868            is_definition,
869        };
870        self.add_node(id, node.into())
871    }
872}
873
874impl PopScopedSymbolNode {
875    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
876        DisplayPopScopedSymbolNode {
877            wrapped: self,
878            graph,
879        }
880    }
881}
882
883#[doc(hidden)]
884pub struct DisplayPopScopedSymbolNode<'a> {
885    wrapped: &'a PopScopedSymbolNode,
886    graph: &'a StackGraph,
887}
888
889impl<'a> Display for DisplayPopScopedSymbolNode<'a> {
890    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
891        if f.alternate() {
892            write!(f, "[{}]", self.wrapped.id.display(self.graph))
893        } else {
894            write!(
895                f,
896                "[{} {} {}]",
897                self.wrapped.id.display(self.graph),
898                if self.wrapped.is_definition {
899                    "scoped definition"
900                } else {
901                    "pop scoped"
902                },
903                self.wrapped.symbol.display(self.graph),
904            )
905        }
906    }
907}
908
909/// Pops a symbol from the symbol stack.  If the top of the symbol stack doesn't match the
910/// requested symbol, then the path is not allowed to enter this node.
911#[repr(C)]
912pub struct PopSymbolNode {
913    /// The unique identifier for this node.
914    pub id: NodeID,
915    /// The symbol to pop off the symbol stack.
916    pub symbol: Handle<Symbol>,
917    _scope: NodeID,
918    /// Whether this node represents a reference in the source language.
919    pub is_definition: bool,
920}
921
922impl From<PopSymbolNode> for Node {
923    fn from(node: PopSymbolNode) -> Node {
924        Node::PopSymbol(node)
925    }
926}
927
928impl StackGraph {
929    /// Adds a _pop symbol_ node to the stack graph.
930    pub fn add_pop_symbol_node(
931        &mut self,
932        id: NodeID,
933        symbol: Handle<Symbol>,
934        is_definition: bool,
935    ) -> Option<Handle<Node>> {
936        let node = PopSymbolNode {
937            id,
938            symbol,
939            _scope: NodeID::default(),
940            is_definition,
941        };
942        self.add_node(id, node.into())
943    }
944}
945
946impl PopSymbolNode {
947    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
948        DisplayPopSymbolNode {
949            wrapped: self,
950            graph,
951        }
952    }
953}
954
955#[doc(hidden)]
956pub struct DisplayPopSymbolNode<'a> {
957    wrapped: &'a PopSymbolNode,
958    graph: &'a StackGraph,
959}
960
961impl<'a> Display for DisplayPopSymbolNode<'a> {
962    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
963        if f.alternate() {
964            write!(f, "[{}]", self.wrapped.id.display(self.graph))
965        } else {
966            write!(
967                f,
968                "[{} {} {}]",
969                self.wrapped.id.display(self.graph),
970                if self.wrapped.is_definition {
971                    "definition"
972                } else {
973                    "pop"
974                },
975                self.wrapped.symbol.display(self.graph),
976            )
977        }
978    }
979}
980
981/// Pushes a scoped symbol onto the symbol stack.
982#[repr(C)]
983pub struct PushScopedSymbolNode {
984    /// The unique identifier for this node.
985    pub id: NodeID,
986    /// The symbol to push onto the symbol stack.
987    pub symbol: Handle<Symbol>,
988    /// The exported scope node that should be attached to the scoped symbol.  The node ID must
989    /// refer to an exported scope node.
990    pub scope: NodeID,
991    /// Whether this node represents a reference in the source language.
992    pub is_reference: bool,
993    _phantom: (),
994}
995
996impl From<PushScopedSymbolNode> for Node {
997    fn from(node: PushScopedSymbolNode) -> Node {
998        Node::PushScopedSymbol(node)
999    }
1000}
1001
1002impl StackGraph {
1003    /// Adds a _push scoped symbol_ node to the stack graph.
1004    pub fn add_push_scoped_symbol_node(
1005        &mut self,
1006        id: NodeID,
1007        symbol: Handle<Symbol>,
1008        scope: NodeID,
1009        is_reference: bool,
1010    ) -> Option<Handle<Node>> {
1011        let node = PushScopedSymbolNode {
1012            id,
1013            symbol,
1014            scope,
1015            is_reference,
1016            _phantom: (),
1017        };
1018        self.add_node(id, node.into())
1019    }
1020}
1021
1022impl PushScopedSymbolNode {
1023    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1024        DisplayPushScopedSymbolNode {
1025            wrapped: self,
1026            graph,
1027        }
1028    }
1029}
1030
1031#[doc(hidden)]
1032pub struct DisplayPushScopedSymbolNode<'a> {
1033    wrapped: &'a PushScopedSymbolNode,
1034    graph: &'a StackGraph,
1035}
1036
1037impl<'a> Display for DisplayPushScopedSymbolNode<'a> {
1038    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1039        if f.alternate() {
1040            write!(f, "[{}]", self.wrapped.id.display(self.graph))
1041        } else {
1042            write!(
1043                f,
1044                "[{} {} {} {}]",
1045                self.wrapped.id.display(self.graph),
1046                if self.wrapped.is_reference {
1047                    "scoped reference"
1048                } else {
1049                    "push scoped"
1050                },
1051                self.wrapped.symbol.display(self.graph),
1052                self.wrapped.scope.display(self.graph),
1053            )
1054        }
1055    }
1056}
1057
1058/// Pushes a symbol onto the symbol stack.
1059#[repr(C)]
1060pub struct PushSymbolNode {
1061    /// The unique identifier for this node.
1062    pub id: NodeID,
1063    /// The symbol to push onto the symbol stack.
1064    pub symbol: Handle<Symbol>,
1065    _scope: NodeID,
1066    /// Whether this node represents a reference in the source language.
1067    pub is_reference: bool,
1068}
1069
1070impl From<PushSymbolNode> for Node {
1071    fn from(node: PushSymbolNode) -> Node {
1072        Node::PushSymbol(node)
1073    }
1074}
1075
1076impl StackGraph {
1077    /// Adds a _push symbol_ node to the stack graph.
1078    pub fn add_push_symbol_node(
1079        &mut self,
1080        id: NodeID,
1081        symbol: Handle<Symbol>,
1082        is_reference: bool,
1083    ) -> Option<Handle<Node>> {
1084        let node = PushSymbolNode {
1085            id,
1086            symbol,
1087            _scope: NodeID::default(),
1088            is_reference,
1089        };
1090        self.add_node(id, node.into())
1091    }
1092}
1093
1094impl PushSymbolNode {
1095    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1096        DisplayPushSymbolNode {
1097            wrapped: self,
1098            graph,
1099        }
1100    }
1101}
1102
1103#[doc(hidden)]
1104pub struct DisplayPushSymbolNode<'a> {
1105    wrapped: &'a PushSymbolNode,
1106    graph: &'a StackGraph,
1107}
1108
1109impl<'a> Display for DisplayPushSymbolNode<'a> {
1110    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1111        if f.alternate() {
1112            write!(f, "[{}]", self.wrapped.id.display(self.graph))
1113        } else {
1114            write!(
1115                f,
1116                "[{} {} {}]",
1117                self.wrapped.id.display(self.graph),
1118                if self.wrapped.is_reference {
1119                    "reference"
1120                } else {
1121                    "push"
1122                },
1123                self.wrapped.symbol.display(self.graph),
1124            )
1125        }
1126    }
1127}
1128
1129/// The singleton root node, which allows a name binding path to cross between files.
1130#[repr(C)]
1131pub struct RootNode {
1132    id: NodeID,
1133    _symbol: ControlledOption<Handle<Symbol>>,
1134    _scope: NodeID,
1135    _is_endpoint: bool,
1136}
1137
1138impl From<RootNode> for Node {
1139    fn from(node: RootNode) -> Node {
1140        Node::Root(node)
1141    }
1142}
1143
1144impl RootNode {
1145    fn new() -> RootNode {
1146        RootNode {
1147            id: NodeID::root(),
1148            _symbol: ControlledOption::none(),
1149            _scope: NodeID::default(),
1150            _is_endpoint: false,
1151        }
1152    }
1153}
1154
1155impl Display for RootNode {
1156    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1157        write!(f, "[root]")
1158    }
1159}
1160
1161struct NodeIDHandles {
1162    files: SupplementalArena<File, Vec<Option<Handle<Node>>>>,
1163}
1164
1165impl NodeIDHandles {
1166    fn new() -> NodeIDHandles {
1167        NodeIDHandles {
1168            files: SupplementalArena::new(),
1169        }
1170    }
1171
1172    fn try_handle_for_id(&self, node_id: NodeID) -> Option<Handle<Node>> {
1173        let file_entry = self.files.get(node_id.file().unwrap())?;
1174        let node_index = node_id.local_id as usize;
1175        if node_index >= file_entry.len() {
1176            return None;
1177        }
1178        file_entry[node_index]
1179    }
1180
1181    fn handle_for_id(&mut self, node_id: NodeID) -> Option<Handle<Node>> {
1182        let file_entry = &mut self.files[node_id.file().unwrap()];
1183        let node_index = node_id.local_id as usize;
1184        if node_index >= file_entry.len() {
1185            file_entry.resize(node_index + 1, None);
1186        }
1187        file_entry[node_index]
1188    }
1189
1190    fn set_handle_for_id(&mut self, node_id: NodeID, handle: Handle<Node>) {
1191        let file_entry = &mut self.files[node_id.file().unwrap()];
1192        let node_index = node_id.local_id as usize;
1193        file_entry[node_index] = Some(handle);
1194    }
1195
1196    fn unused_id(&mut self, file: Handle<File>) -> NodeID {
1197        let local_id = self
1198            .files
1199            .get(file)
1200            .map(|file_entry| file_entry.len() as u32)
1201            .unwrap_or(0);
1202        NodeID::new_in_file(file, local_id)
1203    }
1204
1205    fn nodes_for_file(&self, file: Handle<File>) -> impl Iterator<Item = Handle<Node>> + '_ {
1206        let file_entry = match self.files.get(file) {
1207            Some(file_entry) => file_entry,
1208            None => return Either::Left(std::iter::empty()),
1209        };
1210        Either::Right(file_entry.iter().filter_map(|entry| *entry))
1211    }
1212}
1213
1214/// A node that adds structure to the graph. If the node is exported, it can be
1215/// referred to on the scope stack, which allows "jump to" nodes in any other
1216/// part of the graph can jump back here.
1217#[repr(C)]
1218pub struct ScopeNode {
1219    /// The unique identifier for this node.
1220    pub id: NodeID,
1221    _symbol: ControlledOption<Handle<Symbol>>,
1222    _scope: NodeID,
1223    pub is_exported: bool,
1224}
1225
1226impl From<ScopeNode> for Node {
1227    fn from(node: ScopeNode) -> Node {
1228        Node::Scope(node)
1229    }
1230}
1231
1232impl StackGraph {
1233    /// Adds a _scope_ node to the stack graph.
1234    pub fn add_scope_node(&mut self, id: NodeID, is_exported: bool) -> Option<Handle<Node>> {
1235        let node = ScopeNode {
1236            id,
1237            _symbol: ControlledOption::none(),
1238            _scope: NodeID::default(),
1239            is_exported,
1240        };
1241        self.add_node(id, node.into())
1242    }
1243}
1244
1245impl ScopeNode {
1246    pub fn display<'a>(&'a self, graph: &'a StackGraph) -> impl Display + 'a {
1247        DisplayScopeNode {
1248            wrapped: self,
1249            graph,
1250        }
1251    }
1252}
1253
1254#[doc(hidden)]
1255pub struct DisplayScopeNode<'a> {
1256    wrapped: &'a ScopeNode,
1257    graph: &'a StackGraph,
1258}
1259
1260impl<'a> Display for DisplayScopeNode<'a> {
1261    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1262        if f.alternate() {
1263            write!(f, "[{}]", self.wrapped.id.display(self.graph))
1264        } else {
1265            write!(
1266                f,
1267                "[{}{} scope]",
1268                self.wrapped.id.display(self.graph),
1269                if self.wrapped.is_exported {
1270                    " exported"
1271                } else {
1272                    ""
1273                },
1274            )
1275        }
1276    }
1277}
1278
1279//-------------------------------------------------------------------------------------------------
1280// Edges
1281
1282/// Connects two nodes in a stack graph.
1283///
1284/// These edges provide the basic graph connectivity that allow us to search for name binding paths
1285/// in a stack graph.  (Though not all sequence of edges is a well-formed name binding: the nodes
1286/// that you encounter along the path must also satisfy all of the rules for maintaining correct
1287/// symbol and scope stacks.)
1288#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1289pub struct Edge {
1290    pub source: Handle<Node>,
1291    pub sink: Handle<Node>,
1292    pub precedence: i32,
1293}
1294
1295pub(crate) struct OutgoingEdge {
1296    sink: Handle<Node>,
1297    precedence: i32,
1298}
1299
1300impl StackGraph {
1301    /// Adds a new edge to the stack graph.
1302    pub fn add_edge(&mut self, source: Handle<Node>, sink: Handle<Node>, precedence: i32) {
1303        let edges = &mut self.outgoing_edges[source];
1304        if let Err(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
1305            edges.insert(index, OutgoingEdge { sink, precedence });
1306            self.incoming_edges[sink] += Degree::One;
1307        }
1308    }
1309
1310    /// Sets edge precedence of the given edge.
1311    pub fn set_edge_precedence(
1312        &mut self,
1313        source: Handle<Node>,
1314        sink: Handle<Node>,
1315        precedence: i32,
1316    ) {
1317        let edges = &mut self.outgoing_edges[source];
1318        if let Ok(index) = edges.binary_search_by_key(&sink, |o| o.sink) {
1319            edges[index].precedence = precedence;
1320        }
1321    }
1322
1323    /// Returns an iterator of all of the edges that begin at a particular source node.
1324    pub fn outgoing_edges(&self, source: Handle<Node>) -> impl Iterator<Item = Edge> + '_ {
1325        match self.outgoing_edges.get(source) {
1326            Some(edges) => Either::Right(edges.iter().map(move |o| Edge {
1327                source,
1328                sink: o.sink,
1329                precedence: o.precedence,
1330            })),
1331            None => Either::Left(std::iter::empty()),
1332        }
1333    }
1334
1335    /// Returns the number of edges that end at a particular sink node.
1336    pub fn incoming_edge_degree(&self, sink: Handle<Node>) -> Degree {
1337        self.incoming_edges
1338            .get(sink)
1339            .cloned()
1340            .unwrap_or(Degree::Zero)
1341    }
1342}
1343
1344//-------------------------------------------------------------------------------------------------
1345// Source code
1346
1347/// Contains information about a range of code in a source code file.
1348#[repr(C)]
1349#[derive(Default)]
1350pub struct SourceInfo {
1351    /// The location in its containing file of the source code that this node represents.
1352    pub span: lsp_positions::Span,
1353    /// The kind of syntax entity this node represents (e.g. `function`, `class`, `method`, etc.).
1354    pub syntax_type: ControlledOption<Handle<InternedString>>,
1355    /// The full content of the line containing this node in its source file.
1356    pub containing_line: ControlledOption<Handle<InternedString>>,
1357    /// The location in its containing file of the source code that this node's definiens represents.
1358    /// This is used for things like the bodies of functions, rather than the RHSes of equations.
1359    /// If you need one of these to make the type checker happy, but you don't have one, just use
1360    /// lsp_positions::Span::default(), as this will correspond to the all-0s spans which mean "no definiens".
1361    pub definiens_span: lsp_positions::Span,
1362    /// The fully qualified name is a representation of the symbol that captures its name and its
1363    /// embedded context (e.g. `foo.bar` for the symbol `bar` defined in the module `foo`).
1364    pub fully_qualified_name: ControlledOption<Handle<InternedString>>,
1365}
1366
1367impl StackGraph {
1368    /// Returns information about the source code that a stack graph node represents.
1369    pub fn source_info(&self, node: Handle<Node>) -> Option<&SourceInfo> {
1370        self.source_info.get(node)
1371    }
1372
1373    /// Returns a mutable reference to the information about the source code that a stack graph
1374    /// node represents.
1375    pub fn source_info_mut(&mut self, node: Handle<Node>) -> &mut SourceInfo {
1376        &mut self.source_info[node]
1377    }
1378}
1379
1380//-------------------------------------------------------------------------------------------------
1381// Debug info
1382
1383/// Contains debug info about a stack graph node as key-value pairs of strings.
1384#[derive(Default)]
1385pub struct DebugInfo {
1386    entries: Vec<DebugEntry>,
1387}
1388
1389impl DebugInfo {
1390    pub fn add(&mut self, key: Handle<InternedString>, value: Handle<InternedString>) {
1391        self.entries.push(DebugEntry { key, value });
1392    }
1393
1394    pub fn iter(&self) -> std::slice::Iter<DebugEntry> {
1395        self.entries.iter()
1396    }
1397}
1398
1399/// A debug entry consisting of a string key-value air of strings.
1400pub struct DebugEntry {
1401    pub key: Handle<InternedString>,
1402    pub value: Handle<InternedString>,
1403}
1404
1405impl StackGraph {
1406    /// Returns debug information about the stack graph node.
1407    pub fn node_debug_info(&self, node: Handle<Node>) -> Option<&DebugInfo> {
1408        self.node_debug_info.get(node)
1409    }
1410
1411    /// Returns a mutable reference to the debug info about the stack graph node.
1412    pub fn node_debug_info_mut(&mut self, node: Handle<Node>) -> &mut DebugInfo {
1413        &mut self.node_debug_info[node]
1414    }
1415
1416    /// Returns debug information about the stack graph edge.
1417    pub fn edge_debug_info(&self, source: Handle<Node>, sink: Handle<Node>) -> Option<&DebugInfo> {
1418        self.edge_debug_info.get(source).and_then(|es| {
1419            match es.binary_search_by_key(&sink, |e| e.0) {
1420                Ok(idx) => Some(&es[idx].1),
1421                Err(_) => None,
1422            }
1423        })
1424    }
1425
1426    /// Returns a mutable reference to the debug info about the stack graph edge.
1427    pub fn edge_debug_info_mut(
1428        &mut self,
1429        source: Handle<Node>,
1430        sink: Handle<Node>,
1431    ) -> &mut DebugInfo {
1432        let es = &mut self.edge_debug_info[source];
1433        let idx = match es.binary_search_by_key(&sink, |e| e.0) {
1434            Ok(idx) => idx,
1435            Err(idx) => {
1436                es.insert(idx, (sink, DebugInfo::default()));
1437                idx
1438            }
1439        };
1440        &mut es[idx].1
1441    }
1442}
1443
1444//-------------------------------------------------------------------------------------------------
1445// Stack graphs
1446
1447#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1448#[repr(u8)]
1449pub enum Degree {
1450    Zero,
1451    One,
1452    Multiple,
1453}
1454
1455impl Default for Degree {
1456    fn default() -> Self {
1457        Self::Zero
1458    }
1459}
1460
1461impl std::ops::Add for Degree {
1462    type Output = Self;
1463    fn add(self, rhs: Self) -> Self::Output {
1464        match (self, rhs) {
1465            (Self::Zero, result) | (result, Self::Zero) => result,
1466            _ => Self::Multiple,
1467        }
1468    }
1469}
1470
1471impl std::ops::AddAssign for Degree {
1472    fn add_assign(&mut self, rhs: Self) {
1473        *self = *self + rhs;
1474    }
1475}
1476
1477/// Contains all of the nodes and edges that make up a stack graph.
1478pub struct StackGraph {
1479    interned_strings: InternedStringArena,
1480    pub(crate) symbols: Arena<Symbol>,
1481    symbol_handles: FxHashMap<&'static str, Handle<Symbol>>,
1482    pub(crate) strings: Arena<InternedString>,
1483    string_handles: FxHashMap<&'static str, Handle<InternedString>>,
1484    pub(crate) files: Arena<File>,
1485    file_handles: FxHashMap<&'static str, Handle<File>>,
1486    pub(crate) nodes: Arena<Node>,
1487    pub(crate) source_info: SupplementalArena<Node, SourceInfo>,
1488    node_id_handles: NodeIDHandles,
1489    outgoing_edges: SupplementalArena<Node, SmallVec<[OutgoingEdge; 4]>>,
1490    incoming_edges: SupplementalArena<Node, Degree>,
1491    pub(crate) node_debug_info: SupplementalArena<Node, DebugInfo>,
1492    pub(crate) edge_debug_info: SupplementalArena<Node, SmallVec<[(Handle<Node>, DebugInfo); 4]>>,
1493}
1494
1495impl StackGraph {
1496    /// Creates a new, initially empty stack graph.
1497    pub fn new() -> StackGraph {
1498        StackGraph::default()
1499    }
1500
1501    /// Copies the given stack graph into this stack graph. Panics if any of the files
1502    /// in the other stack graph are already defined in the current one.
1503    pub fn add_from_graph(
1504        &mut self,
1505        other: &StackGraph,
1506    ) -> Result<Vec<Handle<File>>, Handle<File>> {
1507        let mut files = HashMap::new();
1508        for other_file in other.iter_files() {
1509            let file = self.add_file(other[other_file].name())?;
1510            files.insert(other_file, file);
1511        }
1512        let files = files;
1513        let node_id = |other_node_id: NodeID| {
1514            if other_node_id.is_root() {
1515                NodeID::root()
1516            } else if other_node_id.is_jump_to() {
1517                NodeID::jump_to()
1518            } else {
1519                NodeID::new_in_file(
1520                    files[&other_node_id.file.into_option().unwrap()],
1521                    other_node_id.local_id,
1522                )
1523            }
1524        };
1525        let mut nodes = HashMap::new();
1526        nodes.insert(Self::root_node(), Self::root_node());
1527        nodes.insert(Self::jump_to_node(), Self::jump_to_node());
1528        for other_file in files.keys().cloned() {
1529            let file = files[&other_file];
1530            for other_node in other.nodes_for_file(other_file) {
1531                let value: Node = match other[other_node] {
1532                    Node::DropScopes(DropScopesNode { id, .. }) => DropScopesNode {
1533                        id: NodeID::new_in_file(file, id.local_id),
1534                        _symbol: ControlledOption::default(),
1535                        _scope: NodeID::default(),
1536                        _is_endpoint: bool::default(),
1537                    }
1538                    .into(),
1539                    Node::JumpTo(JumpToNode { .. }) => JumpToNode {
1540                        id: NodeID::jump_to(),
1541                        _symbol: ControlledOption::default(),
1542                        _scope: NodeID::default(),
1543                        _is_endpoint: bool::default(),
1544                    }
1545                    .into(),
1546                    Node::PopScopedSymbol(PopScopedSymbolNode {
1547                        id,
1548                        symbol,
1549                        is_definition,
1550                        ..
1551                    }) => PopScopedSymbolNode {
1552                        id: NodeID::new_in_file(file, id.local_id),
1553                        symbol: self.add_symbol(&other[symbol]),
1554                        _scope: NodeID::default(),
1555                        is_definition: is_definition,
1556                    }
1557                    .into(),
1558                    Node::PopSymbol(PopSymbolNode {
1559                        id,
1560                        symbol,
1561                        is_definition,
1562                        ..
1563                    }) => PopSymbolNode {
1564                        id: NodeID::new_in_file(file, id.local_id),
1565                        symbol: self.add_symbol(&other[symbol]),
1566                        _scope: NodeID::default(),
1567                        is_definition: is_definition,
1568                    }
1569                    .into(),
1570                    Node::PushScopedSymbol(PushScopedSymbolNode {
1571                        id,
1572                        symbol,
1573                        scope,
1574                        is_reference,
1575                        ..
1576                    }) => PushScopedSymbolNode {
1577                        id: NodeID::new_in_file(file, id.local_id),
1578                        symbol: self.add_symbol(&other[symbol]),
1579                        scope: node_id(scope),
1580                        is_reference: is_reference,
1581                        _phantom: (),
1582                    }
1583                    .into(),
1584                    Node::PushSymbol(PushSymbolNode {
1585                        id,
1586                        symbol,
1587                        is_reference,
1588                        ..
1589                    }) => PushSymbolNode {
1590                        id: NodeID::new_in_file(file, id.local_id),
1591                        symbol: self.add_symbol(&other[symbol]),
1592                        _scope: NodeID::default(),
1593                        is_reference: is_reference,
1594                    }
1595                    .into(),
1596                    Node::Root(RootNode { .. }) => RootNode {
1597                        id: NodeID::root(),
1598                        _symbol: ControlledOption::default(),
1599                        _scope: NodeID::default(),
1600                        _is_endpoint: bool::default(),
1601                    }
1602                    .into(),
1603                    Node::Scope(ScopeNode {
1604                        id, is_exported, ..
1605                    }) => ScopeNode {
1606                        id: NodeID::new_in_file(file, id.local_id),
1607                        _symbol: ControlledOption::default(),
1608                        _scope: NodeID::default(),
1609                        is_exported: is_exported,
1610                    }
1611                    .into(),
1612                };
1613                let node = self.add_node(value.id(), value).unwrap();
1614                nodes.insert(other_node, node);
1615                if let Some(source_info) = other.source_info(other_node) {
1616                    *self.source_info_mut(node) = SourceInfo {
1617                        span: source_info.span.clone(),
1618                        syntax_type: source_info
1619                            .syntax_type
1620                            .into_option()
1621                            .map(|st| self.add_string(&other[st]))
1622                            .into(),
1623                        containing_line: source_info
1624                            .containing_line
1625                            .into_option()
1626                            .map(|cl| self.add_string(&other[cl]))
1627                            .into(),
1628                        definiens_span: source_info.definiens_span.clone(),
1629                        fully_qualified_name: ControlledOption::default(),
1630                    };
1631                }
1632                if let Some(debug_info) = other.node_debug_info(other_node) {
1633                    *self.node_debug_info_mut(node) = DebugInfo {
1634                        entries: debug_info
1635                            .entries
1636                            .iter()
1637                            .map(|e| DebugEntry {
1638                                key: self.add_string(&other[e.key]),
1639                                value: self.add_string(&other[e.value]),
1640                            })
1641                            .collect::<Vec<_>>(),
1642                    };
1643                }
1644            }
1645            for other_node in nodes.keys().cloned() {
1646                for other_edge in other.outgoing_edges(other_node) {
1647                    self.add_edge(
1648                        nodes[&other_edge.source],
1649                        nodes[&other_edge.sink],
1650                        other_edge.precedence,
1651                    );
1652                }
1653            }
1654        }
1655        Ok(files.into_values().collect())
1656    }
1657}
1658
1659impl Default for StackGraph {
1660    fn default() -> StackGraph {
1661        let mut nodes = Arena::new();
1662        nodes.add(RootNode::new().into());
1663        nodes.add(JumpToNode::new().into());
1664
1665        StackGraph {
1666            interned_strings: InternedStringArena::new(),
1667            symbols: Arena::new(),
1668            symbol_handles: FxHashMap::default(),
1669            strings: Arena::new(),
1670            string_handles: FxHashMap::default(),
1671            files: Arena::new(),
1672            file_handles: FxHashMap::default(),
1673            nodes,
1674            source_info: SupplementalArena::new(),
1675            node_id_handles: NodeIDHandles::new(),
1676            outgoing_edges: SupplementalArena::new(),
1677            incoming_edges: SupplementalArena::new(),
1678            node_debug_info: SupplementalArena::new(),
1679            edge_debug_info: SupplementalArena::new(),
1680        }
1681    }
1682}