Skip to content

Commit 53ec791

Browse files
committed
Add UnescapedName and make Name hold escaped name
1 parent e70681f commit 53ec791

File tree

1 file changed

+52
-2
lines changed

1 file changed

+52
-2
lines changed

crates/hir-expand/src/name.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ use syntax::{ast, SmolStr, SyntaxKind};
77
/// `Name` is a wrapper around string, which is used in hir for both references
88
/// and declarations. In theory, names should also carry hygiene info, but we are
99
/// not there yet!
10+
///
11+
/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
12+
/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
13+
/// name without "r#".
1014
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1115
pub struct Name(Repr);
1216

1317
/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier
1418
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1519
pub struct EscapedName<'a>(&'a Name);
1620

21+
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
22+
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
23+
pub struct UnescapedName<'a>(&'a Name);
24+
1725
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1826
enum Repr {
1927
Text(SmolStr),
@@ -49,6 +57,35 @@ impl<'a> fmt::Display for EscapedName<'a> {
4957
}
5058
}
5159

60+
impl<'a> fmt::Display for UnescapedName<'a> {
61+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62+
match &self.0 .0 {
63+
Repr::Text(text) => {
64+
let text = text.strip_prefix("r#").unwrap_or(text);
65+
fmt::Display::fmt(&text, f)
66+
}
67+
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
68+
}
69+
}
70+
}
71+
72+
impl<'a> UnescapedName<'a> {
73+
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
74+
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
75+
pub fn to_smol_str(&self) -> SmolStr {
76+
match &self.0 .0 {
77+
Repr::Text(it) => {
78+
if let Some(stripped) = it.strip_prefix("r#") {
79+
SmolStr::new(stripped)
80+
} else {
81+
it.clone()
82+
}
83+
}
84+
Repr::TupleField(it) => SmolStr::new(&it.to_string()),
85+
}
86+
}
87+
}
88+
5289
impl<'a> EscapedName<'a> {
5390
pub fn is_escaped(&self) -> bool {
5491
match &self.0 .0 {
@@ -97,9 +134,11 @@ impl Name {
97134

98135
/// Resolve a name from the text of token.
99136
fn resolve(raw_text: &str) -> Name {
137+
// When `raw_text` starts with "r#" but the name does not coincide with any
138+
// keyword, we never need the prefix so we strip it.
100139
match raw_text.strip_prefix("r#") {
101-
Some(text) => Name::new_text(SmolStr::new(text)),
102-
None => Name::new_text(raw_text.into()),
140+
Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
141+
_ => Name::new_text(raw_text.into()),
103142
}
104143
}
105144

@@ -145,6 +184,17 @@ impl Name {
145184
pub fn escaped(&self) -> EscapedName<'_> {
146185
EscapedName(self)
147186
}
187+
188+
pub fn unescaped(&self) -> UnescapedName<'_> {
189+
UnescapedName(self)
190+
}
191+
192+
pub fn is_escaped(&self) -> bool {
193+
match &self.0 {
194+
Repr::Text(it) => it.starts_with("r#"),
195+
Repr::TupleField(_) => false,
196+
}
197+
}
148198
}
149199

150200
pub trait AsName {

0 commit comments

Comments
 (0)