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}