1 //! See [`Name`]. 2 3 use std::fmt; 4 5 use syntax::{ast, utils::is_raw_identifier, SmolStr}; 6 7 /// `Name` is a wrapper around string, which is used in hir for both references 8 /// and declarations. In theory, names should also carry hygiene info, but we are 9 /// 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#". 14 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 15 pub struct Name(Repr); 16 17 /// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. 18 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 19 pub struct UnescapedName<'a>(&'a Name); 20 21 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 22 enum Repr { 23 Text(SmolStr), 24 TupleField(usize), 25 } 26 27 impl<'a> UnescapedName<'a> { 28 /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over 29 /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. to_smol_str(&self) -> SmolStr30 pub fn to_smol_str(&self) -> SmolStr { 31 match &self.0 .0 { 32 Repr::Text(it) => { 33 if let Some(stripped) = it.strip_prefix("r#") { 34 SmolStr::new(stripped) 35 } else { 36 it.clone() 37 } 38 } 39 Repr::TupleField(it) => SmolStr::new(it.to_string()), 40 } 41 } 42 display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a43 pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { 44 _ = db; 45 UnescapedDisplay { name: self } 46 } 47 } 48 49 impl Name { 50 /// Note: this is private to make creating name from random string hard. 51 /// Hopefully, this should allow us to integrate hygiene cleaner in the 52 /// future, and to switch to interned representation of names. new_text(text: SmolStr) -> Name53 const fn new_text(text: SmolStr) -> Name { 54 Name(Repr::Text(text)) 55 } 56 new_tuple_field(idx: usize) -> Name57 pub fn new_tuple_field(idx: usize) -> Name { 58 Name(Repr::TupleField(idx)) 59 } 60 new_lifetime(lt: &ast::Lifetime) -> Name61 pub fn new_lifetime(lt: &ast::Lifetime) -> Name { 62 Self::new_text(lt.text().into()) 63 } 64 65 /// Shortcut to create inline plain text name. Panics if `text.len() > 22` new_inline(text: &str) -> Name66 const fn new_inline(text: &str) -> Name { 67 Name::new_text(SmolStr::new_inline(text)) 68 } 69 70 /// Resolve a name from the text of token. resolve(raw_text: &str) -> Name71 fn resolve(raw_text: &str) -> Name { 72 match raw_text.strip_prefix("r#") { 73 // When `raw_text` starts with "r#" but the name does not coincide with any 74 // keyword, we never need the prefix so we strip it. 75 Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), 76 // Keywords (in the current edition) *can* be used as a name in earlier editions of 77 // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their 78 // escaped form. 79 None if is_raw_identifier(raw_text) => { 80 Name::new_text(SmolStr::from_iter(["r#", raw_text])) 81 } 82 _ => Name::new_text(raw_text.into()), 83 } 84 } 85 86 /// A fake name for things missing in the source code. 87 /// 88 /// For example, `impl Foo for {}` should be treated as a trait impl for a 89 /// type with a missing name. Similarly, `struct S { : u32 }` should have a 90 /// single field with a missing name. 91 /// 92 /// Ideally, we want a `gensym` semantics for missing names -- each missing 93 /// name is equal only to itself. It's not clear how to implement this in 94 /// salsa though, so we punt on that bit for a moment. missing() -> Name95 pub const fn missing() -> Name { 96 Name::new_inline("[missing name]") 97 } 98 99 /// Generates a new name which is only equal to itself, by incrementing a counter. Due 100 /// its implementation, it should not be used in things that salsa considers, like 101 /// type names or field names, and it should be only used in names of local variables 102 /// and labels and similar things. generate_new_name() -> Name103 pub fn generate_new_name() -> Name { 104 use std::sync::atomic::{AtomicUsize, Ordering}; 105 static CNT: AtomicUsize = AtomicUsize::new(0); 106 let c = CNT.fetch_add(1, Ordering::Relaxed); 107 Name::new_text(format!("<ra@gennew>{c}").into()) 108 } 109 110 /// Returns the tuple index this name represents if it is a tuple field. as_tuple_index(&self) -> Option<usize>111 pub fn as_tuple_index(&self) -> Option<usize> { 112 match self.0 { 113 Repr::TupleField(idx) => Some(idx), 114 _ => None, 115 } 116 } 117 118 /// Returns the text this name represents if it isn't a tuple field. as_text(&self) -> Option<SmolStr>119 pub fn as_text(&self) -> Option<SmolStr> { 120 match &self.0 { 121 Repr::Text(it) => Some(it.clone()), 122 _ => None, 123 } 124 } 125 126 /// Returns the text this name represents if it isn't a tuple field. as_str(&self) -> Option<&str>127 pub fn as_str(&self) -> Option<&str> { 128 match &self.0 { 129 Repr::Text(it) => Some(it), 130 _ => None, 131 } 132 } 133 134 /// Returns the textual representation of this name as a [`SmolStr`]. 135 /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in 136 /// the general case. to_smol_str(&self) -> SmolStr137 pub fn to_smol_str(&self) -> SmolStr { 138 match &self.0 { 139 Repr::Text(it) => it.clone(), 140 Repr::TupleField(it) => SmolStr::new(it.to_string()), 141 } 142 } 143 unescaped(&self) -> UnescapedName<'_>144 pub fn unescaped(&self) -> UnescapedName<'_> { 145 UnescapedName(self) 146 } 147 is_escaped(&self) -> bool148 pub fn is_escaped(&self) -> bool { 149 match &self.0 { 150 Repr::Text(it) => it.starts_with("r#"), 151 Repr::TupleField(_) => false, 152 } 153 } 154 display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a155 pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { 156 _ = db; 157 Display { name: self } 158 } 159 } 160 161 struct Display<'a> { 162 name: &'a Name, 163 } 164 165 impl<'a> fmt::Display for Display<'a> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 167 match &self.name.0 { 168 Repr::Text(text) => fmt::Display::fmt(&text, f), 169 Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), 170 } 171 } 172 } 173 174 struct UnescapedDisplay<'a> { 175 name: &'a UnescapedName<'a>, 176 } 177 178 impl<'a> fmt::Display for UnescapedDisplay<'a> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 180 match &self.name.0 .0 { 181 Repr::Text(text) => { 182 let text = text.strip_prefix("r#").unwrap_or(text); 183 fmt::Display::fmt(&text, f) 184 } 185 Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), 186 } 187 } 188 } 189 190 pub trait AsName { as_name(&self) -> Name191 fn as_name(&self) -> Name; 192 } 193 194 impl AsName for ast::NameRef { as_name(&self) -> Name195 fn as_name(&self) -> Name { 196 match self.as_tuple_field() { 197 Some(idx) => Name::new_tuple_field(idx), 198 None => Name::resolve(&self.text()), 199 } 200 } 201 } 202 203 impl AsName for ast::Name { as_name(&self) -> Name204 fn as_name(&self) -> Name { 205 Name::resolve(&self.text()) 206 } 207 } 208 209 impl AsName for ast::NameOrNameRef { as_name(&self) -> Name210 fn as_name(&self) -> Name { 211 match self { 212 ast::NameOrNameRef::Name(it) => it.as_name(), 213 ast::NameOrNameRef::NameRef(it) => it.as_name(), 214 } 215 } 216 } 217 218 impl<Span> AsName for tt::Ident<Span> { as_name(&self) -> Name219 fn as_name(&self) -> Name { 220 Name::resolve(&self.text) 221 } 222 } 223 224 impl AsName for ast::FieldKind { as_name(&self) -> Name225 fn as_name(&self) -> Name { 226 match self { 227 ast::FieldKind::Name(nr) => nr.as_name(), 228 ast::FieldKind::Index(idx) => { 229 let idx = idx.text().parse::<usize>().unwrap_or(0); 230 Name::new_tuple_field(idx) 231 } 232 } 233 } 234 } 235 236 impl AsName for base_db::Dependency { as_name(&self) -> Name237 fn as_name(&self) -> Name { 238 Name::new_text(SmolStr::new(&*self.name)) 239 } 240 } 241 242 pub mod known { 243 macro_rules! known_names { 244 ($($ident:ident),* $(,)?) => { 245 $( 246 #[allow(bad_style)] 247 pub const $ident: super::Name = 248 super::Name::new_inline(stringify!($ident)); 249 )* 250 }; 251 } 252 253 known_names!( 254 // Primitives 255 isize, 256 i8, 257 i16, 258 i32, 259 i64, 260 i128, 261 usize, 262 u8, 263 u16, 264 u32, 265 u64, 266 u128, 267 f32, 268 f64, 269 bool, 270 char, 271 str, 272 // Special names 273 macro_rules, 274 doc, 275 cfg, 276 cfg_attr, 277 register_attr, 278 register_tool, 279 // Components of known path (value or mod name) 280 std, 281 core, 282 alloc, 283 iter, 284 ops, 285 future, 286 result, 287 boxed, 288 option, 289 prelude, 290 rust_2015, 291 rust_2018, 292 rust_2021, 293 v1, 294 // Components of known path (type name) 295 Iterator, 296 IntoIterator, 297 Item, 298 IntoIter, 299 Try, 300 Ok, 301 Future, 302 IntoFuture, 303 Result, 304 Option, 305 Output, 306 Target, 307 Box, 308 RangeFrom, 309 RangeFull, 310 RangeInclusive, 311 RangeToInclusive, 312 RangeTo, 313 Range, 314 Neg, 315 Not, 316 None, 317 Index, 318 // Components of known path (function name) 319 filter_map, 320 next, 321 iter_mut, 322 len, 323 is_empty, 324 new, 325 // Builtin macros 326 asm, 327 assert, 328 column, 329 compile_error, 330 concat_idents, 331 concat_bytes, 332 concat, 333 const_format_args, 334 core_panic, 335 env, 336 file, 337 format_args_nl, 338 format_args, 339 global_asm, 340 include_bytes, 341 include_str, 342 include, 343 line, 344 llvm_asm, 345 log_syntax, 346 module_path, 347 option_env, 348 std_panic, 349 stringify, 350 trace_macros, 351 unreachable, 352 // Builtin derives 353 Copy, 354 Clone, 355 Default, 356 Debug, 357 Hash, 358 Ord, 359 PartialOrd, 360 Eq, 361 PartialEq, 362 // Builtin attributes 363 bench, 364 cfg_accessible, 365 cfg_eval, 366 crate_type, 367 derive, 368 global_allocator, 369 no_core, 370 no_std, 371 test, 372 test_case, 373 recursion_limit, 374 feature, 375 // known methods of lang items 376 call_once, 377 call_mut, 378 call, 379 eq, 380 ne, 381 ge, 382 gt, 383 le, 384 lt, 385 // known fields of lang items 386 pieces, 387 // lang items 388 add_assign, 389 add, 390 bitand_assign, 391 bitand, 392 bitor_assign, 393 bitor, 394 bitxor_assign, 395 bitxor, 396 branch, 397 deref_mut, 398 deref, 399 div_assign, 400 div, 401 drop, 402 fn_mut, 403 fn_once, 404 future_trait, 405 index, 406 index_mut, 407 into_future, 408 mul_assign, 409 mul, 410 neg, 411 not, 412 owned_box, 413 partial_ord, 414 poll, 415 r#fn, 416 rem_assign, 417 rem, 418 shl_assign, 419 shl, 420 shr_assign, 421 shr, 422 sub_assign, 423 sub, 424 unsafe_cell, 425 va_list 426 ); 427 428 // self/Self cannot be used as an identifier 429 pub const SELF_PARAM: super::Name = super::Name::new_inline("self"); 430 pub const SELF_TYPE: super::Name = super::Name::new_inline("Self"); 431 432 pub const STATIC_LIFETIME: super::Name = super::Name::new_inline("'static"); 433 434 #[macro_export] 435 macro_rules! name { 436 (self) => { 437 $crate::name::known::SELF_PARAM 438 }; 439 (Self) => { 440 $crate::name::known::SELF_TYPE 441 }; 442 ('static) => { 443 $crate::name::known::STATIC_LIFETIME 444 }; 445 ($ident:ident) => { 446 $crate::name::known::$ident 447 }; 448 } 449 } 450 451 pub use crate::name; 452