Support mixing shorthand and non-shorthand format args
This commit is contained in:
parent
03e522411e
commit
c05e9ed4ec
|
@ -1,24 +1,25 @@
|
||||||
use crate::ast::Field;
|
use crate::ast::Field;
|
||||||
use crate::attr::Display;
|
use crate::attr::Display;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenTree;
|
||||||
use quote::{format_ident, quote_spanned};
|
use quote::{format_ident, quote_spanned};
|
||||||
use std::collections::HashSet as Set;
|
use std::collections::HashSet as Set;
|
||||||
use syn::{Ident, Index, LitStr, Member};
|
use syn::ext::IdentExt;
|
||||||
|
use syn::parse::{ParseStream, Parser};
|
||||||
|
use syn::{Ident, Index, LitStr, Member, Result, Token};
|
||||||
|
|
||||||
impl Display<'_> {
|
impl Display<'_> {
|
||||||
// Transform `"error {var}"` to `"error {}", var`.
|
// Transform `"error {var}"` to `"error {}", var`.
|
||||||
pub fn expand_shorthand(&mut self, fields: &[Field]) {
|
pub fn expand_shorthand(&mut self, fields: &[Field]) {
|
||||||
if !self.args.is_empty() {
|
let raw_args = self.args.clone();
|
||||||
return;
|
let mut named_args = explicit_named_args.parse2(raw_args).unwrap();
|
||||||
}
|
let fields: Set<Member> = fields.iter().map(|f| f.member.clone()).collect();
|
||||||
|
|
||||||
let span = self.fmt.span();
|
let span = self.fmt.span();
|
||||||
let fmt = self.fmt.value();
|
let fmt = self.fmt.value();
|
||||||
let mut read = fmt.as_str();
|
let mut read = fmt.as_str();
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
let mut args = TokenStream::new();
|
let mut args = self.args.clone();
|
||||||
let mut has_bonus_display = false;
|
let mut has_bonus_display = false;
|
||||||
let fields: Set<Member> = fields.iter().map(|f| f.member.clone()).collect();
|
|
||||||
|
|
||||||
while let Some(brace) = read.find('{') {
|
while let Some(brace) = read.find('{') {
|
||||||
out += &read[..brace + 1];
|
out += &read[..brace + 1];
|
||||||
|
@ -44,13 +45,24 @@ impl Display<'_> {
|
||||||
let ident = take_ident(&mut read);
|
let ident = take_ident(&mut read);
|
||||||
Member::Named(Ident::new(&ident, span))
|
Member::Named(Ident::new(&ident, span))
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => continue,
|
||||||
};
|
};
|
||||||
let ident = match &member {
|
let local = match &member {
|
||||||
Member::Unnamed(index) => format_ident!("_{}", index),
|
Member::Unnamed(index) => format_ident!("_{}", index),
|
||||||
Member::Named(ident) => ident.clone(),
|
Member::Named(ident) => ident.clone(),
|
||||||
};
|
};
|
||||||
args.extend(quote_spanned!(span=> , #ident));
|
let mut formatvar = local.clone();
|
||||||
|
if formatvar.to_string().starts_with('_') {
|
||||||
|
// Work around leading underscore being rejected by 1.40 and
|
||||||
|
// older compilers. https://github.com/rust-lang/rust/pull/66847
|
||||||
|
formatvar = format_ident!("field_{}", formatvar);
|
||||||
|
}
|
||||||
|
out += &formatvar.to_string();
|
||||||
|
if !named_args.insert(formatvar.clone()) {
|
||||||
|
// Already specified in the format argument list.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
args.extend(quote_spanned!(span=> , #formatvar = #local));
|
||||||
if read.starts_with('}') && fields.contains(&member) {
|
if read.starts_with('}') && fields.contains(&member) {
|
||||||
has_bonus_display = true;
|
has_bonus_display = true;
|
||||||
args.extend(quote_spanned!(span=> .as_display()));
|
args.extend(quote_spanned!(span=> .as_display()));
|
||||||
|
@ -65,6 +77,23 @@ impl Display<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn explicit_named_args(input: ParseStream) -> Result<Set<Ident>> {
|
||||||
|
let mut named_args = Set::new();
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
if input.peek(Token![,]) && input.peek2(Ident::peek_any) && input.peek3(Token![=]) {
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
let ident: Ident = input.parse()?;
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
named_args.insert(ident);
|
||||||
|
} else {
|
||||||
|
input.parse::<TokenTree>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(named_args)
|
||||||
|
}
|
||||||
|
|
||||||
fn take_int(read: &mut &str) -> String {
|
fn take_int(read: &mut &str) -> String {
|
||||||
let mut int = String::new();
|
let mut int = String::new();
|
||||||
for (i, ch) in read.char_indices() {
|
for (i, ch) in read.char_indices() {
|
||||||
|
|
|
@ -127,3 +127,15 @@ fn test_void() {
|
||||||
#[error("...")]
|
#[error("...")]
|
||||||
pub enum Error {}
|
pub enum Error {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mixed() {
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
|
||||||
|
struct Error {
|
||||||
|
a: usize,
|
||||||
|
d: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue