Add UnescapedName and make Name hold escaped name

This commit is contained in:
Ryo Yoshida 2022-08-01 14:20:05 +09:00
parent e70681f208
commit 53ec791dc6
No known key found for this signature in database
GPG key ID: E25698A930586171

View file

@ -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 {