//! See [`Name`].
use std::fmt;
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);
/// 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),
TupleField(usize),
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Repr::Text(text) => fmt::Display::fmt(&text, f),
Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
}
}
}
fn is_raw_identifier(name: &str) -> bool {
let is_keyword = SyntaxKind::from_keyword(name).is_some();
is_keyword && !matches!(name, "self" | "crate" | "super" | "Self")
}
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 Name {
/// Note: this is private to make creating name from random string hard.
/// Hopefully, this should allow us to integrate hygiene cleaner in the
/// future, and to switch to interned representation of names.
const fn new_text(text: SmolStr) -> Name {
Name(Repr::Text(text))
}
pub fn new_tuple_field(idx: usize) -> Name {
Name(Repr::TupleField(idx))
}
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
Self::new_text(lt.text().into())
}
/// Shortcut to create inline plain text name
const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text))
}
/// Resolve a name from the text of token.
fn resolve(raw_text: &str) -> Name {
match raw_text.strip_prefix("r#") {
// When `raw_text` starts with "r#" but the name does not coincide with any
// keyword, we never need the prefix so we strip it.
Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)),
// Keywords (in the current edition) *can* be used as a name in earlier editions of
// Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their
// escaped form.
None if is_raw_identifier(raw_text) => {
Name::new_text(SmolStr::from_iter(["r#", raw_text]))
}
_ => Name::new_text(raw_text.into()),
}
}
/// A fake name for things missing in the source code.
///
/// For example, `impl Foo for {}` should be treated as a trait impl for a
/// type with a missing name. Similarly, `struct S { : u32 }` should have a
/// single field with a missing name.
///
/// Ideally, we want a `gensym` semantics for missing names -- each missing
/// name is equal only to itself. It's not clear how to implement this in
/// salsa though, so we punt on that bit for a moment.
pub const fn missing() -> Name {
Name::new_inline("[missing name]")
}
/// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
Repr::TupleField(idx) => Some(idx),
_ => None,
}
}
/// Returns the text this name represents if it isn't a tuple field.
pub fn as_text(&self) -> Option<SmolStr> {
match &self.0 {
Repr::Text(it) => Some(it.clone()),
_ => None,
}
}
/// 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 {
Repr::Text(it) => it.clone(),
Repr::TupleField(it) => SmolStr::new(&it.to_string()),
}
}
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 {
fn as_name(&self) -> Name;
}
impl AsName for ast::NameRef {
fn as_name(&self) -> Name {
match self.as_tuple_field() {
Some(idx) => Name::new_tuple_field(idx),
None => Name::resolve(&self.text()),
}
}
}
impl AsName for ast::Name {
fn as_name(&self) -> Name {
Name::resolve(&self.text())
}
}
impl AsName for ast::NameOrNameRef {
fn as_name(&self) -> Name {
match self {
ast::NameOrNameRef::Name(it) => it.as_name(),
ast::NameOrNameRef::NameRef(it) => it.as_name(),
}
}
}
impl AsName for tt::Ident {
fn as_name(&self) -> Name {
Name::resolve(&self.text)
}
}
impl AsName for ast::FieldKind {
fn as_name(&self) -> Name {
match self {
ast::FieldKind::Name(nr) => nr.as_name(),
ast::FieldKind::Index(idx) => {
let idx = idx.text().parse::<usize>().unwrap_or(0);
Name::new_tuple_field(idx)
}
}
}
}
impl AsName for base_db::Dependency {
fn as_name(&self) -> Name {
Name::new_text(SmolStr::new(&*self.name))
}
}
pub mod known {
macro_rules! known_names {
($($ident:ident),* $(,)?) => {
$(
#[allow(bad_style)]
pub const $ident: super::Name =
super::Name::new_inline(stringify!($ident));
)*
};
}
known_names!(
// Primitives
isize,
i8,
i16,
i32,
i64,
i128,
usize,
u8,
u16,
u32,
u64,
u128,
f32,
f64,
bool,
char,
str,
// Special names
macro_rules,
doc,
cfg,
cfg_attr,
register_attr,
register_tool,
// Components of known path (value or mod name)
std,
core,
alloc,
iter,
ops,
future,
result,
boxed,
option,
prelude,
rust_2015,
rust_2018,
rust_2021,
v1,
// Components of known path (type name)
Iterator,
IntoIterator,
Item,
IntoIter,
Try,
Ok,
Future,
IntoFuture,
Result,
Option,
Output,
Target,
Box,
RangeFrom,
RangeFull,
RangeInclusive,
RangeToInclusive,
RangeTo,
Range,
Residual,
FromResidual,
Neg,
Not,
None,
Index,
// Components of known path (function name)
filter_map,
next,
iter_mut,
len,
is_empty,
new,
// Builtin macros
asm,
assert,
column,
compile_error,
concat_idents,
concat_bytes,
concat,
const_format_args,
core_panic,
env,
file,
format_args_nl,
format_args,
global_asm,
include_bytes,
include_str,
include,
line,
llvm_asm,
log_syntax,
module_path,
option_env,
std_panic,
stringify,
trace_macros,
unreachable,
// Builtin derives
Copy,
Clone,
Default,
Debug,
Hash,
Ord,
PartialOrd,
Eq,
PartialEq,
// Builtin attributes
bench,
cfg_accessible,
cfg_eval,
crate_type,
derive,
global_allocator,
test,
test_case,
recursion_limit,
feature,
// Safe intrinsics
abort,
add_with_overflow,
black_box,
bitreverse,
bswap,
caller_location,
ctlz,
ctpop,
cttz,
discriminant_value,
forget,
likely,
maxnumf32,
maxnumf64,
min_align_of_val,
min_align_of,
minnumf32,
minnumf64,
mul_with_overflow,
needs_drop,
ptr_guaranteed_eq,
ptr_guaranteed_ne,
rotate_left,
rotate_right,
rustc_peek,
saturating_add,
saturating_sub,
size_of_val,
size_of,
sub_with_overflow,
type_id,
type_name,
unlikely,
variant_count,
wrapping_add,
wrapping_mul,
wrapping_sub,
// known methods of lang items
eq,
ne,
ge,
gt,
le,
lt,
// lang items
add_assign,
add,
bitand_assign,
bitand,
bitor_assign,
bitor,
bitxor_assign,
bitxor,
branch,
deref_mut,
deref,
div_assign,
div,
fn_mut,
fn_once,
future_trait,
index,
index_mut,
into_future,
mul_assign,
mul,
neg,
not,
owned_box,
partial_ord,
poll,
r#fn,
rem_assign,
rem,
shl_assign,
shl,
shr_assign,
shr,
sub_assign,
sub,
);
// self/Self cannot be used as an identifier
pub const SELF_PARAM: super::Name = super::Name::new_inline("self");
pub const SELF_TYPE: super::Name = super::Name::new_inline("Self");
pub const STATIC_LIFETIME: super::Name = super::Name::new_inline("'static");
#[macro_export]
macro_rules! name {
(self) => {
$crate::name::known::SELF_PARAM
};
(Self) => {
$crate::name::known::SELF_TYPE
};
('static) => {
$crate::name::known::STATIC_LIFETIME
};
($ident:ident) => {
$crate::name::known::$ident
};
}
}
pub use crate::name;