about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRyo Yoshida <low.ryoshida@gmail.com>2022-08-01 14:20:05 +0900
committerRyo Yoshida <low.ryoshida@gmail.com>2022-08-11 01:03:08 +0900
commit53ec791dc6b22e581f9e5aabeda5edfc806564cb (patch)
tree924dcd69aa49e0f41aa02c501090c24a45d4c25d
parente70681f208403b7de148ba027a6d14603aeaa461 (diff)
downloadrust-53ec791dc6b22e581f9e5aabeda5edfc806564cb.tar.gz
rust-53ec791dc6b22e581f9e5aabeda5edfc806564cb.zip
Add `UnescapedName` and make `Name` hold escaped name
-rw-r--r--crates/hir-expand/src/name.rs54
1 files changed, 52 insertions, 2 deletions
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 47d191822d8..18b0793f10e 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -7,6 +7,10 @@ use syntax::{ast, SmolStr, SyntaxKind};
 /// `Name` is a wrapper around string, which is used in hir for both references
 /// and declarations. In theory, names should also carry hygiene info, but we are
 /// not there yet!
+///
+/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it
+/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the
+/// name without "r#".
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct Name(Repr);
 
@@ -14,6 +18,10 @@ pub struct Name(Repr);
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct EscapedName<'a>(&'a Name);
 
+/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct UnescapedName<'a>(&'a Name);
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
 enum Repr {
     Text(SmolStr),
@@ -49,6 +57,35 @@ impl<'a> fmt::Display for EscapedName<'a> {
     }
 }
 
+impl<'a> fmt::Display for UnescapedName<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self.0 .0 {
+            Repr::Text(text) => {
+                let text = text.strip_prefix("r#").unwrap_or(text);
+                fmt::Display::fmt(&text, f)
+            }
+            Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
+        }
+    }
+}
+
+impl<'a> UnescapedName<'a> {
+    /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
+    /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
+    pub fn to_smol_str(&self) -> SmolStr {
+        match &self.0 .0 {
+            Repr::Text(it) => {
+                if let Some(stripped) = it.strip_prefix("r#") {
+                    SmolStr::new(stripped)
+                } else {
+                    it.clone()
+                }
+            }
+            Repr::TupleField(it) => SmolStr::new(&it.to_string()),
+        }
+    }
+}
+
 impl<'a> EscapedName<'a> {
     pub fn is_escaped(&self) -> bool {
         match &self.0 .0 {
@@ -97,9 +134,11 @@ impl Name {
 
     /// Resolve a name from the text of token.
     fn resolve(raw_text: &str) -> Name {
+        // When `raw_text` starts with "r#" but the name does not coincide with any
+        // keyword, we never need the prefix so we strip it.
         match raw_text.strip_prefix("r#") {
-            Some(text) => Name::new_text(SmolStr::new(text)),
-            None => Name::new_text(raw_text.into()),
+            Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
+            _ => Name::new_text(raw_text.into()),
         }
     }
 
@@ -145,6 +184,17 @@ impl Name {
     pub fn escaped(&self) -> EscapedName<'_> {
         EscapedName(self)
     }
+
+    pub fn unescaped(&self) -> UnescapedName<'_> {
+        UnescapedName(self)
+    }
+
+    pub fn is_escaped(&self) -> bool {
+        match &self.0 {
+            Repr::Text(it) => it.starts_with("r#"),
+            Repr::TupleField(_) => false,
+        }
+    }
 }
 
 pub trait AsName {