leo_ast/common/
path.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17use crate::{Expression, Identifier, Node, NodeID, simple_node_impl};
18
19use leo_span::{Span, Symbol};
20
21use itertools::Itertools;
22use serde::{Deserialize, Serialize};
23use std::{fmt, hash::Hash};
24
25/// A Path in a program.
26#[derive(Clone, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
27pub struct Path {
28    /// The qualifying namespace segments written by the user, excluding the item itself.
29    /// e.g., in `foo::bar::baz`, this would be `[foo, bar]`.
30    qualifier: Vec<Identifier>,
31
32    /// The final item in the path, e.g., `baz` in `foo::bar::baz`.
33    identifier: Identifier,
34
35    /// Is this path an absolute path? e.g. `::foo::bar::baz`.
36    is_absolute: bool,
37
38    /// The fully resolved path. We may not know this until the pass PathResolution pass runs.
39    /// For path that refer to global items (structs, consts, functions), `absolute_path` is
40    /// guaranteed to be set after the pass `PathResolution`.
41    absolute_path: Option<Vec<Symbol>>,
42
43    /// A span locating where the path occurred in the source.
44    pub span: Span,
45
46    /// The ID of the node.
47    pub id: NodeID,
48}
49
50simple_node_impl!(Path);
51
52impl Path {
53    /// Creates a new `Path` from the given components.
54    ///
55    /// - `qualifier`: The namespace segments (e.g., `foo::bar` in `foo::bar::baz`).
56    /// - `identifier`: The final item in the path (e.g., `baz`).
57    /// - `is_absolute`: Whether the path is absolute (starts with `::`).
58    /// - `absolute_path`: Optionally, the fully resolved symbolic path.
59    /// - `span`: The source code span for this path.
60    /// - `id`: The node ID.
61    pub fn new(
62        qualifier: Vec<Identifier>,
63        identifier: Identifier,
64        is_absolute: bool,
65        absolute_path: Option<Vec<Symbol>>,
66        span: Span,
67        id: NodeID,
68    ) -> Self {
69        Self { qualifier, identifier, is_absolute, absolute_path, span, id }
70    }
71
72    /// Returns the final identifier of the path (e.g., `baz` in `foo::bar::baz`).
73    pub fn identifier(&self) -> Identifier {
74        self.identifier
75    }
76
77    /// Returns a slice of the qualifier segments (e.g., `[foo, bar]` in `foo::bar::baz`).
78    pub fn qualifier(&self) -> &[Identifier] {
79        self.qualifier.as_slice()
80    }
81
82    /// Returns `true` if the path is absolute (i.e., starts with `::`).
83    pub fn is_absolute(&self) -> bool {
84        self.is_absolute
85    }
86
87    /// Returns a `Vec<Symbol>` representing the full symbolic path:
88    /// the qualifier segments followed by the final identifier.
89    ///
90    /// Note: this refers to the user path which is not necessarily the absolute path.
91    pub fn as_symbols(&self) -> Vec<Symbol> {
92        self.qualifier.iter().map(|segment| segment.name).chain(std::iter::once(self.identifier.name)).collect()
93    }
94
95    /// Returns an optional vector of `Symbol`s representing the resolved absolute path,
96    /// or `None` if resolution has not yet occurred.
97    pub fn try_absolute_path(&self) -> Option<Vec<Symbol>> {
98        if self.is_absolute { Some(self.as_symbols()) } else { self.absolute_path.clone() }
99    }
100
101    /// Returns a vector of `Symbol`s representing the resolved absolute path.
102    ///
103    /// If the path is not an absolute path, this method panics if the absolute path has not been resolved yet.
104    /// For relative paths, this is expected to be called only after path resolution has occurred.
105    pub fn absolute_path(&self) -> Vec<Symbol> {
106        if self.is_absolute {
107            self.as_symbols()
108        } else {
109            self.absolute_path.as_ref().expect("absolute path must be known at this stage").to_vec()
110        }
111    }
112
113    /// Converts this `Path` into an absolute path by setting its `is_absolute` flag to `true`.
114    ///
115    /// This does not alter the qualifier or identifier, nor does it compute or modify
116    /// the resolved `absolute_path`.
117    pub fn into_absolute(mut self) -> Self {
118        self.is_absolute = true;
119        self
120    }
121
122    /// Returns a new `Path` instance with the last segment's `Symbol` and the last symbol
123    /// in the `absolute_path` (if present) replaced with `new_symbol`.
124    ///
125    /// Other fields remain unchanged.
126    pub fn with_updated_last_symbol(mut self, new_symbol: Symbol) -> Self {
127        // Update identifier
128        self.identifier.name = new_symbol;
129
130        // Update absolute_path's last symbol if present
131        if let Some(ref mut abs_path) = self.absolute_path {
132            if let Some(last) = abs_path.last_mut() {
133                *last = new_symbol;
134            }
135        }
136
137        self
138    }
139
140    /// Sets `self.absolute_path` to `absolute_path`
141    pub fn with_absolute_path(mut self, absolute_path: Option<Vec<Symbol>>) -> Self {
142        self.absolute_path = absolute_path;
143        self
144    }
145
146    /// Sets the `absolute_path` by prepending the given `module_prefix` to the path's
147    /// own qualifier and identifier. Returns the updated `Path`.
148    pub fn with_module_prefix(mut self, module_prefix: &[Symbol]) -> Self {
149        let full_path = module_prefix
150            .iter()
151            .cloned()
152            .chain(self.qualifier.iter().map(|id| id.name))
153            .chain(std::iter::once(self.identifier.name))
154            .collect();
155
156        self.absolute_path = Some(full_path);
157        self
158    }
159}
160
161impl fmt::Display for Path {
162    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163        if self.is_absolute {
164            write!(f, "::")?;
165        }
166        if self.qualifier.is_empty() {
167            write!(f, "{}", self.identifier)
168        } else {
169            write!(f, "{}::{}", self.qualifier.iter().format("::"), self.identifier)
170        }
171    }
172}
173
174impl fmt::Debug for Path {
175    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176        // Print user path (Display impl)
177        write!(f, "{self}")?;
178
179        // Print resolved absolute path if available
180        if let Some(abs_path) = &self.absolute_path {
181            write!(f, "(::{})", abs_path.iter().format("::"))
182        } else {
183            write!(f, "()")
184        }
185    }
186}
187
188impl From<Path> for Expression {
189    fn from(value: Path) -> Self {
190        Expression::Path(value)
191    }
192}