hir_expand/
name.rs

1//! See [`Name`].
2
3use std::fmt;
4
5use intern::{Symbol, sym};
6use span::{Edition, SyntaxContext};
7use syntax::utils::is_raw_identifier;
8use syntax::{ast, format_smolstr};
9
10/// `Name` is a wrapper around string, which is used in hir for both references
11/// and declarations. In theory, names should also carry hygiene info, but we are
12/// not there yet!
13///
14/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix.
15/// This is because we want to show (in completions etc.) names as raw depending on the needs
16/// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
17/// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
18#[derive(Clone, PartialEq, Eq, Hash)]
19pub struct Name {
20    symbol: Symbol,
21    // If you are making this carry actual hygiene, beware that the special handling for variables and labels
22    // in bodies can go.
23    ctx: (),
24}
25
26impl fmt::Debug for Name {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        f.debug_struct("Name")
29            .field("symbol", &self.symbol.as_str())
30            .field("ctx", &self.ctx)
31            .finish()
32    }
33}
34
35impl Ord for Name {
36    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
37        self.symbol.as_str().cmp(other.symbol.as_str())
38    }
39}
40
41impl PartialOrd for Name {
42    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
43        Some(self.cmp(other))
44    }
45}
46
47// No need to strip `r#`, all comparisons are done against well-known symbols.
48impl PartialEq<Symbol> for Name {
49    fn eq(&self, sym: &Symbol) -> bool {
50        self.symbol == *sym
51    }
52}
53
54impl PartialEq<&Symbol> for Name {
55    fn eq(&self, &sym: &&Symbol) -> bool {
56        self.symbol == *sym
57    }
58}
59
60impl PartialEq<Name> for Symbol {
61    fn eq(&self, name: &Name) -> bool {
62        *self == name.symbol
63    }
64}
65
66impl PartialEq<Name> for &Symbol {
67    fn eq(&self, name: &Name) -> bool {
68        **self == name.symbol
69    }
70}
71
72impl Name {
73    fn new_text(text: &str) -> Name {
74        Name { symbol: Symbol::intern(text), ctx: () }
75    }
76
77    pub fn new(text: &str, mut ctx: SyntaxContext) -> Name {
78        // For comparisons etc. we remove the edition, because sometimes we search for some `Name`
79        // and we don't know which edition it came from.
80        // Can't do that for all `SyntaxContextId`s because it breaks Salsa.
81        ctx.remove_root_edition();
82        _ = ctx;
83        match text.strip_prefix("r#") {
84            Some(text) => Self::new_text(text),
85            None => Self::new_text(text),
86        }
87    }
88
89    pub fn new_root(text: &str) -> Name {
90        // The edition doesn't matter for hygiene.
91        Self::new(text, SyntaxContext::root(Edition::Edition2015))
92    }
93
94    pub fn new_tuple_field(idx: usize) -> Name {
95        let symbol = match idx {
96            0 => sym::INTEGER_0,
97            1 => sym::INTEGER_1,
98            2 => sym::INTEGER_2,
99            3 => sym::INTEGER_3,
100            4 => sym::INTEGER_4,
101            5 => sym::INTEGER_5,
102            6 => sym::INTEGER_6,
103            7 => sym::INTEGER_7,
104            8 => sym::INTEGER_8,
105            9 => sym::INTEGER_9,
106            10 => sym::INTEGER_10,
107            11 => sym::INTEGER_11,
108            12 => sym::INTEGER_12,
109            13 => sym::INTEGER_13,
110            14 => sym::INTEGER_14,
111            15 => sym::INTEGER_15,
112            _ => Symbol::intern(&idx.to_string()),
113        };
114        Name { symbol, ctx: () }
115    }
116
117    pub fn new_lifetime(lt: &str) -> Name {
118        match lt.strip_prefix("'r#") {
119            Some(lt) => Self::new_text(&format_smolstr!("'{lt}")),
120            None => Self::new_text(lt),
121        }
122    }
123
124    pub fn new_symbol(symbol: Symbol, ctx: SyntaxContext) -> Self {
125        debug_assert!(!symbol.as_str().starts_with("r#"));
126        _ = ctx;
127        Self { symbol, ctx: () }
128    }
129
130    // FIXME: This needs to go once we have hygiene
131    pub fn new_symbol_root(sym: Symbol) -> Self {
132        Self::new_symbol(sym, SyntaxContext::root(Edition::Edition2015))
133    }
134
135    /// A fake name for things missing in the source code.
136    ///
137    /// For example, `impl Foo for {}` should be treated as a trait impl for a
138    /// type with a missing name. Similarly, `struct S { : u32 }` should have a
139    /// single field with a missing name.
140    ///
141    /// Ideally, we want a `gensym` semantics for missing names -- each missing
142    /// name is equal only to itself. It's not clear how to implement this in
143    /// salsa though, so we punt on that bit for a moment.
144    pub const fn missing() -> Name {
145        Name { symbol: sym::MISSING_NAME, ctx: () }
146    }
147
148    /// Returns true if this is a fake name for things missing in the source code. See
149    /// [`missing()`][Self::missing] for details.
150    ///
151    /// Use this method instead of comparing with `Self::missing()` as missing names
152    /// (ideally should) have a `gensym` semantics.
153    pub fn is_missing(&self) -> bool {
154        self == &Name::missing()
155    }
156
157    /// Generates a new name that attempts to be unique. Should only be used when body lowering and
158    /// creating desugared locals and labels. The caller is responsible for picking an index
159    /// that is stable across re-executions
160    pub fn generate_new_name(idx: usize) -> Name {
161        Name::new_text(&format!("<ra@gennew>{idx}"))
162    }
163
164    /// Returns the tuple index this name represents if it is a tuple field.
165    pub fn as_tuple_index(&self) -> Option<usize> {
166        self.symbol.as_str().parse().ok()
167    }
168
169    /// Whether this name needs to be escaped in the given edition via `r#`.
170    pub fn needs_escape(&self, edition: Edition) -> bool {
171        is_raw_identifier(self.symbol.as_str(), edition)
172    }
173
174    /// Returns the text this name represents if it isn't a tuple field.
175    ///
176    /// Do not use this for user-facing text, use `display` instead to handle editions properly.
177    // FIXME: This should take a database argument to hide the interning
178    pub fn as_str(&self) -> &str {
179        self.symbol.as_str()
180    }
181
182    pub fn display<'a>(
183        &'a self,
184        db: &dyn crate::db::ExpandDatabase,
185        edition: Edition,
186    ) -> impl fmt::Display + 'a {
187        _ = db;
188        self.display_no_db(edition)
189    }
190
191    // FIXME: Remove this in favor of `display`, see fixme on `as_str`
192    #[doc(hidden)]
193    pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
194        Display { name: self, edition }
195    }
196
197    pub fn symbol(&self) -> &Symbol {
198        &self.symbol
199    }
200}
201
202struct Display<'a> {
203    name: &'a Name,
204    edition: Edition,
205}
206
207impl fmt::Display for Display<'_> {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let mut symbol = self.name.symbol.as_str();
210
211        if symbol == "'static" {
212            // FIXME: '`static` can also be a label, and there it does need escaping.
213            // But knowing where it is will require adding a parameter to `display()`,
214            // and that is an infectious change.
215            return f.write_str(symbol);
216        }
217
218        if let Some(s) = symbol.strip_prefix('\'') {
219            f.write_str("'")?;
220            symbol = s;
221        }
222        if is_raw_identifier(symbol, self.edition) {
223            f.write_str("r#")?;
224        }
225        f.write_str(symbol)
226    }
227}
228
229pub trait AsName {
230    fn as_name(&self) -> Name;
231}
232
233impl AsName for ast::NameRef {
234    fn as_name(&self) -> Name {
235        match self.as_tuple_field() {
236            Some(idx) => Name::new_tuple_field(idx),
237            None => Name::new_root(&self.text()),
238        }
239    }
240}
241
242impl AsName for ast::Name {
243    fn as_name(&self) -> Name {
244        Name::new_root(&self.text())
245    }
246}
247
248impl AsName for ast::NameOrNameRef {
249    fn as_name(&self) -> Name {
250        match self {
251            ast::NameOrNameRef::Name(it) => it.as_name(),
252            ast::NameOrNameRef::NameRef(it) => it.as_name(),
253        }
254    }
255}
256
257impl<Span> AsName for tt::Ident<Span> {
258    fn as_name(&self) -> Name {
259        Name::new_root(self.sym.as_str())
260    }
261}
262
263impl AsName for ast::FieldKind {
264    fn as_name(&self) -> Name {
265        match self {
266            ast::FieldKind::Name(nr) => nr.as_name(),
267            ast::FieldKind::Index(idx) => {
268                let idx = idx.text().parse::<usize>().unwrap_or(0);
269                Name::new_tuple_field(idx)
270            }
271        }
272    }
273}
274
275impl AsName for base_db::BuiltDependency {
276    fn as_name(&self) -> Name {
277        Name::new_symbol_root((*self.name).clone())
278    }
279}