fmt.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. use crate::ast::Field;
  2. use crate::attr::{Display, Trait};
  3. use proc_macro2::TokenTree;
  4. use quote::{format_ident, quote_spanned};
  5. use std::collections::{BTreeSet as Set, HashMap as Map};
  6. use syn::ext::IdentExt;
  7. use syn::parse::{ParseStream, Parser};
  8. use syn::{Ident, Index, LitStr, Member, Result, Token};
  9. impl Display<'_> {
  10. // Transform `"error {var}"` to `"error {}", var`.
  11. pub fn expand_shorthand(&mut self, fields: &[Field]) {
  12. let raw_args = self.args.clone();
  13. let mut named_args = explicit_named_args.parse2(raw_args).unwrap();
  14. let mut member_index = Map::new();
  15. for (i, field) in fields.iter().enumerate() {
  16. member_index.insert(&field.member, i);
  17. }
  18. let span = self.fmt.span();
  19. let fmt = self.fmt.value();
  20. let mut read = fmt.as_str();
  21. let mut out = String::new();
  22. let mut args = self.args.clone();
  23. let mut has_bonus_display = false;
  24. let mut implied_bounds = Set::new();
  25. let mut has_trailing_comma = false;
  26. if let Some(TokenTree::Punct(punct)) = args.clone().into_iter().last() {
  27. if punct.as_char() == ',' {
  28. has_trailing_comma = true;
  29. }
  30. }
  31. while let Some(brace) = read.find('{') {
  32. out += &read[..brace + 1];
  33. read = &read[brace + 1..];
  34. if read.starts_with('{') {
  35. out.push('{');
  36. read = &read[1..];
  37. continue;
  38. }
  39. let next = match read.chars().next() {
  40. Some(next) => next,
  41. None => return,
  42. };
  43. let member = match next {
  44. '0'..='9' => {
  45. let int = take_int(&mut read);
  46. let member = match int.parse::<u32>() {
  47. Ok(index) => Member::Unnamed(Index { index, span }),
  48. Err(_) => return,
  49. };
  50. if !member_index.contains_key(&member) {
  51. out += &int;
  52. continue;
  53. }
  54. member
  55. }
  56. 'a'..='z' | 'A'..='Z' | '_' => {
  57. let mut ident = take_ident(&mut read);
  58. ident.set_span(span);
  59. Member::Named(ident)
  60. }
  61. _ => continue,
  62. };
  63. if let Some(&field) = member_index.get(&member) {
  64. let end_spec = match read.find('}') {
  65. Some(end_spec) => end_spec,
  66. None => return,
  67. };
  68. let bound = match read[..end_spec].chars().next_back() {
  69. Some('?') => Trait::Debug,
  70. Some('o') => Trait::Octal,
  71. Some('x') => Trait::LowerHex,
  72. Some('X') => Trait::UpperHex,
  73. Some('p') => Trait::Pointer,
  74. Some('b') => Trait::Binary,
  75. Some('e') => Trait::LowerExp,
  76. Some('E') => Trait::UpperExp,
  77. Some(_) | None => Trait::Display,
  78. };
  79. implied_bounds.insert((field, bound));
  80. }
  81. let local = match &member {
  82. Member::Unnamed(index) => format_ident!("_{}", index),
  83. Member::Named(ident) => ident.clone(),
  84. };
  85. let mut formatvar = local.clone();
  86. if formatvar.to_string().starts_with("r#") {
  87. formatvar = format_ident!("r_{}", formatvar);
  88. }
  89. if formatvar.to_string().starts_with('_') {
  90. // Work around leading underscore being rejected by 1.40 and
  91. // older compilers. https://github.com/rust-lang/rust/pull/66847
  92. formatvar = format_ident!("field_{}", formatvar);
  93. }
  94. out += &formatvar.to_string();
  95. if !named_args.insert(formatvar.clone()) {
  96. // Already specified in the format argument list.
  97. continue;
  98. }
  99. if !has_trailing_comma {
  100. args.extend(quote_spanned!(span=> ,));
  101. }
  102. args.extend(quote_spanned!(span=> #formatvar = #local));
  103. if read.starts_with('}') && member_index.contains_key(&member) {
  104. has_bonus_display = true;
  105. args.extend(quote_spanned!(span=> .as_display()));
  106. }
  107. has_trailing_comma = false;
  108. }
  109. out += read;
  110. self.fmt = LitStr::new(&out, self.fmt.span());
  111. self.args = args;
  112. self.has_bonus_display = has_bonus_display;
  113. self.implied_bounds = implied_bounds;
  114. }
  115. }
  116. fn explicit_named_args(input: ParseStream) -> Result<Set<Ident>> {
  117. let mut named_args = Set::new();
  118. while !input.is_empty() {
  119. if input.peek(Token![,]) && input.peek2(Ident::peek_any) && input.peek3(Token![=]) {
  120. input.parse::<Token![,]>()?;
  121. let ident = input.call(Ident::parse_any)?;
  122. input.parse::<Token![=]>()?;
  123. named_args.insert(ident);
  124. } else {
  125. input.parse::<TokenTree>()?;
  126. }
  127. }
  128. Ok(named_args)
  129. }
  130. fn take_int(read: &mut &str) -> String {
  131. let mut int = String::new();
  132. for (i, ch) in read.char_indices() {
  133. match ch {
  134. '0'..='9' => int.push(ch),
  135. _ => {
  136. *read = &read[i..];
  137. break;
  138. }
  139. }
  140. }
  141. int
  142. }
  143. fn take_ident(read: &mut &str) -> Ident {
  144. let mut ident = String::new();
  145. let raw = read.starts_with("r#");
  146. if raw {
  147. ident.push_str("r#");
  148. *read = &read[2..];
  149. }
  150. for (i, ch) in read.char_indices() {
  151. match ch {
  152. 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch),
  153. _ => {
  154. *read = &read[i..];
  155. break;
  156. }
  157. }
  158. }
  159. Ident::parse_any.parse_str(&ident).unwrap()
  160. }