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 /// The fully resolved path. We may not know this until the pass PathResolution pass runs.
36 /// For path that refer to global items (structs, consts, functions), `absolute_path` is
37 /// guaranteed to be set after the pass `PathResolution`.
38 absolute_path: Option<Vec<Symbol>>,
39
40 /// A span locating where the path occurred in the source.
41 pub span: Span,
42
43 /// The ID of the node.
44 pub id: NodeID,
45}
46
47simple_node_impl!(Path);
48
49impl Path {
50 pub fn new(
51 qualifier: Vec<Identifier>,
52 identifier: Identifier,
53 absolute_path: Option<Vec<Symbol>>,
54 span: Span,
55 id: NodeID,
56 ) -> Self {
57 Self { qualifier, identifier, absolute_path, span, id }
58 }
59
60 pub fn identifier(&self) -> Identifier {
61 self.identifier
62 }
63
64 pub fn qualifier(&self) -> &[Identifier] {
65 self.qualifier.as_slice()
66 }
67
68 /// Returns a `Vec<Symbol>` representing the full symbolic path:
69 /// the qualifier segments followed by the final identifier.
70 ///
71 /// Note: this refers to the user path which is not necessarily the absolute path.
72 pub fn as_symbols(&self) -> Vec<Symbol> {
73 self.qualifier.iter().map(|segment| segment.name).chain(std::iter::once(self.identifier.name)).collect()
74 }
75
76 /// Returns an optional slice of `Symbol`s representing the resolved absolute path,
77 /// or `None` if resolution has not yet occurred.
78 pub fn try_absolute_path(&self) -> Option<&[Symbol]> {
79 self.absolute_path.as_deref()
80 }
81
82 /// Returns a slice of `Symbol`s representing the resolved absolute path.
83 ///
84 /// # Panics
85 ///
86 /// Panics if the absolute path has not been resolved yet. This is expected to be
87 /// called only after path resolution has occurred.
88 pub fn absolute_path(&self) -> &[Symbol] {
89 self.absolute_path.as_deref().expect("absolute path must be known at this stage")
90 }
91
92 /// Returns a new `Path` instance with the last segment's `Symbol` and the last symbol
93 /// in the `absolute_path` (if present) replaced with `new_symbol`.
94 ///
95 /// Other fields remain unchanged.
96 pub fn with_updated_last_symbol(mut self, new_symbol: Symbol) -> Self {
97 // Update identifier
98 self.identifier.name = new_symbol;
99
100 // Update absolute_path's last symbol if present
101 if let Some(ref mut abs_path) = self.absolute_path {
102 if let Some(last) = abs_path.last_mut() {
103 *last = new_symbol;
104 }
105 }
106
107 self
108 }
109
110 /// Sets `self.absolute_path` to `absolute_path`
111 pub fn with_absolute_path(mut self, absolute_path: Option<Vec<Symbol>>) -> Self {
112 self.absolute_path = absolute_path;
113 self
114 }
115
116 /// Sets the `absolute_path` by prepending the given `module_prefix` to the path's
117 /// own qualifier and identifier. Returns the updated `Path`.
118 pub fn with_module_prefix(mut self, module_prefix: &[Symbol]) -> Self {
119 let full_path = module_prefix
120 .iter()
121 .cloned()
122 .chain(self.qualifier.iter().map(|id| id.name))
123 .chain(std::iter::once(self.identifier.name))
124 .collect();
125
126 self.absolute_path = Some(full_path);
127 self
128 }
129}
130
131impl fmt::Display for Path {
132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 if self.qualifier.is_empty() {
134 write!(f, "{}", self.identifier)
135 } else {
136 write!(f, "{}::{}", self.qualifier.iter().format("::"), self.identifier)
137 }
138 }
139}
140
141impl fmt::Debug for Path {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 // Print user path (Display impl)
144 write!(f, "{self}")?;
145
146 // Print resolved absolute path if available
147 if let Some(abs_path) = &self.absolute_path {
148 write!(f, "(::{})", abs_path.iter().format("::"))
149 } else {
150 write!(f, "()")
151 }
152 }
153}
154
155impl From<Path> for Expression {
156 fn from(value: Path) -> Self {
157 Expression::Path(value)
158 }
159}