Skip to content

Commit

Permalink
Add a type for representing raw-agnostic format vars
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Nov 4, 2024
1 parent 2180b52 commit 78d6bd6
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 19 deletions.
27 changes: 8 additions & 19 deletions impl/src/fmt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::ast::Field;
use crate::attr::{Display, Trait};
use crate::scan_expr::scan_expr;
use proc_macro2::{Span, TokenStream, TokenTree};
use crate::unraw::IdentUnraw;
use proc_macro2::{TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use std::collections::{BTreeSet as Set, HashMap as Map};
use syn::ext::IdentExt;
Expand Down Expand Up @@ -86,10 +87,10 @@ impl Display<'_> {
};
implied_bounds.insert((field, bound));
}
let formatvar = match &member {
let formatvar = IdentUnraw::new(match &member {
Member::Unnamed(index) => format_ident!("_{}", index),
Member::Named(ident) => ident.clone(),
};
});
out += &formatvar.to_string();
if !named_args.insert(formatvar.clone()) {
// Already specified in the format argument list.
Expand All @@ -98,7 +99,7 @@ impl Display<'_> {
if !has_trailing_comma {
args.extend(quote_spanned!(span=> ,));
}
let local = raw_if_needed(&formatvar);
let local = formatvar.to_local();
args.extend(quote_spanned!(span=> #formatvar = #local));
if read.starts_with('}') && member_index.contains_key(&member) {
has_bonus_display = true;
Expand All @@ -116,7 +117,7 @@ impl Display<'_> {
}

struct FmtArguments {
named: Set<Ident>,
named: Set<IdentUnraw>,
unnamed: bool,
}

Expand Down Expand Up @@ -154,7 +155,7 @@ fn try_explicit_named_args(input: ParseStream) -> Result<FmtArguments> {
break;
}
if input.peek(Ident::peek_any) && input.peek2(Token![=]) && !input.peek2(Token![==]) {
let ident = input.call(Ident::parse_any)?;
let ident: IdentUnraw = input.parse()?;
input.parse::<Token![=]>()?;
args.named.insert(ident);
} else {
Expand Down Expand Up @@ -186,7 +187,7 @@ fn fallback_explicit_named_args(input: ParseStream) -> Result<FmtArguments> {
&& !input.peek3(Token![==])
{
input.parse::<Token![,]>()?;
let ident = input.call(Ident::parse_any)?;
let ident: IdentUnraw = input.parse()?;
input.parse::<Token![=]>()?;
args.named.insert(ident);
}
Expand Down Expand Up @@ -238,15 +239,3 @@ fn take_ident<'a>(read: &mut &'a str) -> &'a str {
*read = rest;
ident
}

fn raw_if_needed(ident: &Ident) -> Ident {
let repr = ident.to_string();
if syn::parse_str::<Ident>(&repr).is_err() {
if let "_" | "super" | "self" | "Self" | "crate" = repr.as_str() {
// Some identifiers are never allowed to appear as raw, like r#self and r#_.
} else {
return Ident::new_raw(&repr, Span::call_site());
}
}
ident.clone()
}
1 change: 1 addition & 0 deletions impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod generics;
mod prop;
mod scan_expr;
mod span;
mod unraw;
mod valid;

use proc_macro::TokenStream;
Expand Down
67 changes: 67 additions & 0 deletions impl/src/unraw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::ToTokens;
use std::cmp::Ordering;
use std::fmt::{self, Display};
use syn::ext::IdentExt as _;
use syn::parse::{Parse, ParseStream, Result};

#[derive(Clone)]
#[repr(transparent)]
pub(crate) struct IdentUnraw(Ident);

impl IdentUnraw {
pub fn new(ident: Ident) -> Self {
IdentUnraw(ident)
}

pub fn to_local(&self) -> Ident {
let unraw = self.0.unraw();
let repr = unraw.to_string();
if syn::parse_str::<Ident>(&repr).is_err() {
if let "_" | "super" | "self" | "Self" | "crate" = repr.as_str() {
// Some identifiers are never allowed to appear as raw, like r#self and r#_.
} else {
return Ident::new_raw(&repr, Span::call_site());
}
}
unraw
}
}

impl Display for IdentUnraw {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(&self.0.unraw(), formatter)
}
}

impl Eq for IdentUnraw {}

impl PartialEq for IdentUnraw {
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(&self.0.unraw(), &other.0.unraw())
}
}

impl Ord for IdentUnraw {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&self.0.unraw(), &other.0.unraw())
}
}

impl PartialOrd for IdentUnraw {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Self::cmp(self, other))
}
}

impl Parse for IdentUnraw {
fn parse(input: ParseStream) -> Result<Self> {
input.call(Ident::parse_any).map(IdentUnraw::new)
}
}

impl ToTokens for IdentUnraw {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.unraw().to_tokens(tokens);
}
}

0 comments on commit 78d6bd6

Please sign in to comment.