Skip to main content

mago_names/
lib.rs

1use std::collections::HashSet;
2
3use ahash::HashMap;
4use serde::Serialize;
5
6use mago_span::HasPosition;
7use mago_span::Position;
8
9pub mod kind;
10pub mod resolver;
11pub mod scope;
12
13mod internal;
14
15/// Stores the results of a name resolution pass over a PHP program.
16///
17/// Maps source code positions (specifically, the starting byte offset of identifiers)
18/// to their resolved fully qualified name (`StringIdentifier`) and a flag indicating
19/// whether the resolution involved an explicit `use` alias or construct.
20#[derive(Debug, Clone, Eq, PartialEq, Serialize, Default)]
21pub struct ResolvedNames<'arena> {
22    /// Internal map storing: position (byte offset) -> (Resolved Name ID, Was Imported Flag)
23    names: HashMap<u32, (&'arena str, bool)>,
24}
25
26impl<'arena> ResolvedNames<'arena> {
27    /// Returns the total number of resolved names stored.
28    #[must_use]
29    pub fn len(&self) -> usize {
30        self.names.len()
31    }
32
33    /// Returns `true` if no resolved names are stored.
34    #[must_use]
35    pub fn is_empty(&self) -> bool {
36        self.names.is_empty()
37    }
38
39    /// Checks if a resolved name exists for the given source `Position`.
40    #[must_use]
41    pub fn contains(&self, position: &Position) -> bool {
42        self.names.contains_key(&position.offset)
43    }
44
45    /// Gets the resolved name identifier for the given source position.
46    ///
47    /// # Panics
48    ///
49    /// Panics if no resolved name is found at the specified `position`.
50    /// Use `contains` first if unsure.
51    pub fn get<T: HasPosition>(&self, position: &T) -> &'arena str {
52        self.names.get(&position.offset()).map(|(name, _)| name).expect("resolved name not found at position")
53    }
54
55    /// Attempts to resolve the name at the given source position.
56    ///
57    /// Returns `Some(&str)` if a resolved name exists at the position,
58    /// or `None` if no name is found.
59    pub fn resolve<T: HasPosition>(&self, position: &T) -> Option<&'arena str> {
60        self.names.get(&position.offset()).map(|(name, _)| *name)
61    }
62
63    /// Checks if the name resolved at the given position originated from an explicit `use` alias or construct.
64    ///
65    /// Returns `false` if the name was resolved relative to the namespace, is a definition,
66    /// or if no name is found at the position.
67    pub fn is_imported<T: HasPosition>(&self, position: &T) -> bool {
68        self.names
69            .get(&position.offset()) // Get Option<(StringIdentifier, bool)>
70            .is_some_and(|(_, imported)| *imported) // Default to false if position not found
71    }
72
73    /// Inserts a resolution result into the map (intended for internal use).
74    ///
75    /// Associates the resolved `name` identifier and its `imported` status with the
76    /// given `position` (byte offset).
77    pub(crate) fn insert_at<T: HasPosition>(&mut self, position: &T, name: &'arena str, imported: bool) {
78        self.names.insert(position.offset(), (name, imported));
79    }
80
81    /// Returns a `HashSet` containing references to all stored resolution results.
82    ///
83    /// Each element in the set is a reference to a tuple: `(&usize, &(StringIdentifier, bool))`,
84    /// representing `(&position, &(resolved_name_id, was_imported_flag))`.
85    #[must_use]
86    pub fn all(&self) -> HashSet<(&u32, &(&'arena str, bool))> {
87        self.names.iter().collect()
88    }
89}