ra_ap_hir_expand/
name.rs

1//! See [`Name`].
2
3use std::fmt;
4
5use intern::{sym, Symbol};
6use span::{Edition, SyntaxContextId};
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: SyntaxContextId) -> 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, SyntaxContextId::root(Edition::Edition2015))
92    }
93
94    pub fn new_tuple_field(idx: usize) -> Name {
95        let symbol = match idx {
96            0 => sym::INTEGER_0.clone(),
97            1 => sym::INTEGER_1.clone(),
98            2 => sym::INTEGER_2.clone(),
99            3 => sym::INTEGER_3.clone(),
100            4 => sym::INTEGER_4.clone(),
101            5 => sym::INTEGER_5.clone(),
102            6 => sym::INTEGER_6.clone(),
103            7 => sym::INTEGER_7.clone(),
104            8 => sym::INTEGER_8.clone(),
105            9 => sym::INTEGER_9.clone(),
106            10 => sym::INTEGER_10.clone(),
107            11 => sym::INTEGER_11.clone(),
108            12 => sym::INTEGER_12.clone(),
109            13 => sym::INTEGER_13.clone(),
110            14 => sym::INTEGER_14.clone(),
111            15 => sym::INTEGER_15.clone(),
112            _ => Symbol::intern(&idx.to_string()),
113        };
114        Name { symbol, ctx: () }
115    }
116
117    pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
118        let text = lt.text();
119        match text.strip_prefix("'r#") {
120            Some(text) => Self::new_text(&format_smolstr!("'{text}")),
121            None => Self::new_text(text.as_str()),
122        }
123    }
124
125    pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
126        debug_assert!(!symbol.as_str().starts_with("r#"));
127        _ = ctx;
128        Self { symbol, ctx: () }
129    }
130
131    // FIXME: This needs to go once we have hygiene
132    pub fn new_symbol_root(sym: Symbol) -> Self {
133        Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015))
134    }
135
136    /// A fake name for things missing in the source code.
137    ///
138    /// For example, `impl Foo for {}` should be treated as a trait impl for a
139    /// type with a missing name. Similarly, `struct S { : u32 }` should have a
140    /// single field with a missing name.
141    ///
142    /// Ideally, we want a `gensym` semantics for missing names -- each missing
143    /// name is equal only to itself. It's not clear how to implement this in
144    /// salsa though, so we punt on that bit for a moment.
145    pub const fn missing() -> Name {
146        Name { symbol: sym::consts::MISSING_NAME, ctx: () }
147    }
148
149    /// Returns true if this is a fake name for things missing in the source code. See
150    /// [`missing()`][Self::missing] for details.
151    ///
152    /// Use this method instead of comparing with `Self::missing()` as missing names
153    /// (ideally should) have a `gensym` semantics.
154    pub fn is_missing(&self) -> bool {
155        self == &Name::missing()
156    }
157
158    /// Generates a new name that attempts to be unique. Should only be used when body lowering and
159    /// creating desugared locals and labels. The caller is responsible for picking an index
160    /// that is stable across re-executions
161    pub fn generate_new_name(idx: usize) -> Name {
162        Name::new_text(&format!("<ra@gennew>{idx}"))
163    }
164
165    /// Returns the tuple index this name represents if it is a tuple field.
166    pub fn as_tuple_index(&self) -> Option<usize> {
167        self.symbol.as_str().parse().ok()
168    }
169
170    /// Whether this name needs to be escaped in the given edition via `r#`.
171    pub fn needs_escape(&self, edition: Edition) -> bool {
172        is_raw_identifier(self.symbol.as_str(), edition)
173    }
174
175    /// Returns the text this name represents if it isn't a tuple field.
176    ///
177    /// Do not use this for user-facing text, use `display` instead to handle editions properly.
178    // FIXME: This should take a database argument to hide the interning
179    pub fn as_str(&self) -> &str {
180        self.symbol.as_str()
181    }
182
183    pub fn display<'a>(
184        &'a self,
185        db: &dyn crate::db::ExpandDatabase,
186        edition: Edition,
187    ) -> impl fmt::Display + 'a {
188        _ = db;
189        self.display_no_db(edition)
190    }
191
192    // FIXME: Remove this in favor of `display`, see fixme on `as_str`
193    #[doc(hidden)]
194    pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
195        Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) }
196    }
197
198    pub fn symbol(&self) -> &Symbol {
199        &self.symbol
200    }
201}
202
203struct Display<'a> {
204    name: &'a Name,
205    needs_escaping: bool,
206}
207
208impl fmt::Display for Display<'_> {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        if self.needs_escaping {
211            write!(f, "r#")?;
212        }
213        fmt::Display::fmt(self.name.symbol.as_str(), f)
214    }
215}
216
217pub trait AsName {
218    fn as_name(&self) -> Name;
219}
220
221impl AsName for ast::NameRef {
222    fn as_name(&self) -> Name {
223        match self.as_tuple_field() {
224            Some(idx) => Name::new_tuple_field(idx),
225            None => Name::new_root(&self.text()),
226        }
227    }
228}
229
230impl AsName for ast::Name {
231    fn as_name(&self) -> Name {
232        Name::new_root(&self.text())
233    }
234}
235
236impl AsName for ast::NameOrNameRef {
237    fn as_name(&self) -> Name {
238        match self {
239            ast::NameOrNameRef::Name(it) => it.as_name(),
240            ast::NameOrNameRef::NameRef(it) => it.as_name(),
241        }
242    }
243}
244
245impl<Span> AsName for tt::Ident<Span> {
246    fn as_name(&self) -> Name {
247        Name::new_root(self.sym.as_str())
248    }
249}
250
251impl AsName for ast::FieldKind {
252    fn as_name(&self) -> Name {
253        match self {
254            ast::FieldKind::Name(nr) => nr.as_name(),
255            ast::FieldKind::Index(idx) => {
256                let idx = idx.text().parse::<usize>().unwrap_or(0);
257                Name::new_tuple_field(idx)
258            }
259        }
260    }
261}
262
263impl AsName for base_db::Dependency {
264    fn as_name(&self) -> Name {
265        Name::new_symbol_root((*self.name).clone())
266    }
267}