Skip to main content

hir_expand/
name.rs

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