Skip to main content

mir_analyzer/
symbol.rs

1//! Per-expression resolved symbol data, retained from body analysis.
2//!
3//! The static analyzer already resolves types for every expression during
4//! analysis but historically discarded the intermediate state.  This module
5//! exposes that data so that downstream tools can build position indexes for
6//! hover, go-to-definition, and completions.
7
8use std::sync::Arc;
9
10use mir_types::Type;
11use php_ast::Span;
12
13/// A single resolved symbol observed during body analysis.
14#[derive(Debug, Clone)]
15pub struct ResolvedSymbol {
16    /// Absolute path of the file this symbol was found in.
17    pub file: Arc<str>,
18    /// Byte-offset span in the source file.
19    pub span: Span,
20    /// What kind of symbol this is.
21    pub kind: ReferenceKind,
22    /// The resolved type at this location.
23    pub resolved_type: Type,
24}
25
26impl ResolvedSymbol {
27    /// Return the key used in the salsa db's reference-location table for this
28    /// symbol, or `None` for kinds that are not tracked there (e.g. variables).
29    ///
30    /// Key format mirrors `MirDatabase::record_reference_location`:
31    /// - method / static call : `"ClassName::methodname"` (method lowercased)
32    /// - property access      : `"ClassName::propName"`
33    /// - function call        : fully-qualified function name
34    /// - class reference      : fully-qualified class name
35    ///
36    /// Prefer [`Self::to_symbol`] for type-safe access.
37    pub fn codebase_key(&self) -> Option<String> {
38        match &self.kind {
39            ReferenceKind::MethodCall { class, method }
40            | ReferenceKind::StaticCall { class, method } => {
41                Some(format!("{}::{}", class, method.to_lowercase()))
42            }
43            ReferenceKind::PropertyAccess { class, property } => {
44                Some(format!("{class}::{property}"))
45            }
46            ReferenceKind::FunctionCall(fqn) => Some(fqn.to_string()),
47            ReferenceKind::ClassReference(fqcn) => Some(fqcn.to_string()),
48            ReferenceKind::Variable(_) => None,
49        }
50    }
51
52    /// Convert this `ResolvedSymbol` to a typed [`crate::Name`] for use with
53    /// [`crate::AnalysisSession::definition_of`], [`crate::AnalysisSession::references_to`],
54    /// or [`crate::AnalysisSession::hover`].
55    ///
56    /// Returns `None` for kinds that don't map to a codebase-level symbol
57    /// (currently only `Variable` — local variables aren't tracked in the
58    /// codebase symbol table).
59    pub fn to_symbol(&self) -> Option<crate::Name> {
60        match &self.kind {
61            ReferenceKind::MethodCall { class, method }
62            | ReferenceKind::StaticCall { class, method } => {
63                Some(crate::Name::method(class.clone(), method.as_ref()))
64            }
65            ReferenceKind::PropertyAccess { class, property } => {
66                Some(crate::Name::property(class.clone(), property.clone()))
67            }
68            ReferenceKind::FunctionCall(fqn) => Some(crate::Name::function(fqn.clone())),
69            ReferenceKind::ClassReference(fqcn) => Some(crate::Name::class(fqcn.clone())),
70            ReferenceKind::Variable(_) => None,
71        }
72    }
73}
74
75/// One declaration emitted by [`crate::AnalysisSession::document_symbols`].
76/// Tool-agnostic shape for outline / breadcrumb features; consumers map this
77/// onto whatever protocol-specific symbol type they need.
78///
79/// Forms a tree: classes/interfaces/traits/enums have `children` populated
80/// with their methods, properties, and constants. Top-level functions and
81/// constants have empty `children`.
82#[derive(Debug, Clone)]
83pub struct DocumentSymbol {
84    /// FQCN for classes/interfaces/traits/enums; FQN for functions /
85    /// constants; short name for members nested inside a class.
86    pub name: Arc<str>,
87    /// Coarse kind suitable for icon / severity selection in outlines.
88    pub kind: DeclarationKind,
89    /// Source location of the declaration (file + 1-based lines, 0-based
90    /// columns). `None` only for synthetic stub-only definitions that don't
91    /// have a recorded source span.
92    pub location: Option<mir_types::Location>,
93    /// For container symbols (Class, Interface, Trait, Enum), the nested
94    /// methods, properties, and constants declared on this type. Empty for
95    /// leaf kinds (Function, Constant, etc.).
96    pub children: Vec<DocumentSymbol>,
97}
98
99/// Coarse declaration kind used by [`DocumentSymbol`]. Maps onto outline-style
100/// symbol kinds in any consumer protocol.
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum DeclarationKind {
103    Class,
104    Interface,
105    Trait,
106    Enum,
107    Function,
108    Method,
109    Property,
110    Constant,
111    EnumCase,
112}
113
114/// The kind of symbol reference that was resolved.
115#[derive(Debug, Clone)]
116pub enum ReferenceKind {
117    /// A variable reference (`$foo`).
118    Variable(Arc<str>),
119    /// An instance method call (`$obj->method()`).
120    MethodCall { class: Arc<str>, method: Arc<str> },
121    /// A static method call (`Foo::method()`).
122    StaticCall { class: Arc<str>, method: Arc<str> },
123    /// A property access (`$obj->prop`).
124    PropertyAccess { class: Arc<str>, property: Arc<str> },
125    /// A free function call (`foo()`).
126    FunctionCall(Arc<str>),
127    /// A class reference (`new Foo`, `instanceof Foo`, type hints).
128    ClassReference(Arc<str>),
129}