mago_reference/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use serde::Deserialize;
use serde::Serialize;

use mago_interner::StringIdentifier;
use mago_interner::ThreadedInterner;
use mago_semantics::Semantics;
use mago_span::HasSpan;
use mago_span::Span;
use mago_walker::Walker;

use crate::internal::context::Context;
use crate::internal::walker::ReferenceFindingWalker;
use crate::query::Query;

pub mod query;

mod internal;

/// Represents the kind of reference that is found in code.
///
/// Each variant corresponds to a specific way in which a symbol might be
/// referred to in code. For instance, an import statement, a usage, or
/// a definition site.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub enum ReferenceKind {
    /// Indicates where a symbol is imported into a scope.
    Import,

    /// Refers to places in code where the symbol is used.
    Usage,

    /// Points to the definition site(s) of the symbol.
    Definition,

    /// Identifies where a symbol (like an interface) is
    /// implemented, or where a trait is applied.
    Implementation,

    /// Tracks places where a class is extended.
    Extension,
}

/// Describes a single reference to a symbol in the source code.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct Reference {
    /// The identifier for the referenced symbol, as stored in the interner.
    pub value: StringIdentifier,

    /// The kind of reference—import, usage, definition, etc. (see [`ReferenceKind`]).
    pub kind: ReferenceKind,

    /// The [`Span`] in the source code where this reference appears.
    pub span: Span,
}

/// Provides functionality for discovering references (imports, usages, definitions, etc.)
/// of a symbol within a program’s semantics.
///
/// The [`ReferenceFinder`] can locate references by walking through the AST
/// (via a [`Walker`]) and collecting relevant `Reference` items.
#[derive(Debug, Clone)]
pub struct ReferenceFinder<'a> {
    /// The interner used to resolve symbol names (e.g., function names, class names, etc.).
    interner: &'a ThreadedInterner,
}

impl<'a> ReferenceFinder<'a> {
    /// Creates a new `ReferenceFinder` that uses the given [`ThreadedInterner`]
    /// for string lookups and resolutions.
    pub fn new(interner: &'a ThreadedInterner) -> Self {
        Self { interner }
    }

    /// Finds all references that match the given [`Query`] within the provided [`Semantics`].
    ///
    /// This method:
    /// 1. Creates a [`Context`] that holds the interner, the query, and the current semantics.
    /// 2. Uses a specialized [`Walker`] (`ReferenceFindingWalker`) to traverse the AST of the program.
    /// 3. Gathers references (e.g., [`ReferenceKind::Usage`], [`ReferenceKind::Definition`]) in the context.
    /// 4. Returns all discovered references as a `Vec<Reference>`.
    ///
    /// # Parameters
    ///
    /// - `semantics`: The [`Semantics`] representing the parsed and analyzed code.
    /// - `query`: A [`Query`] describing what symbol or reference type we’re looking for.
    ///
    /// # Returns
    ///
    /// A list of [`Reference`] objects describing where and how the symbol is referenced
    /// in the code.
    pub fn find(&self, semantics: &Semantics, query: Query) -> Vec<Reference> {
        let mut context = Context::new(self.interner, &query, semantics);

        ReferenceFindingWalker.walk_program(&semantics.program, &mut context);

        context.take_references()
    }
}

impl HasSpan for Reference {
    fn span(&self) -> Span {
        self.span
    }
}