@@ -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 ) ]
1115pub 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 ) ]
1519pub 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 ) ]
1826enum 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+
5289impl < ' 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
150200pub trait AsName {
0 commit comments