Rollup merge of #69387 - petrochenkov:idprint, r=Mark-Simulacrum
Deduplicate identifier printing a bit https://github.com/rust-lang/rust/pull/67010 introduced a couple more subtly different ways to print an identifier. This PR attempts to restore the order. The most basic identifier printing interface is `Formatter`-based now, so `String`s are not allocated unless required. r? @Mark-Simulacrum
This commit is contained in:
commit
41bf200073
@ -3,7 +3,7 @@ use crate::pp::{self, Breaks};
|
||||
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::source_map::{SourceMap, Spanned};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::symbol::{kw, sym, IdentPrinter};
|
||||
use rustc_span::{BytePos, FileName, Span};
|
||||
use syntax::ast::{self, BlockCheckMode, PatKind, RangeEnd, RangeSyntax};
|
||||
use syntax::ast::{Attribute, GenericArg, MacArgs};
|
||||
@ -196,40 +196,6 @@ pub fn literal_to_string(lit: token::Lit) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
/// Print an ident from AST, `$crate` is converted into its respective crate name.
|
||||
pub fn ast_ident_to_string(ident: ast::Ident, is_raw: bool) -> String {
|
||||
ident_to_string(ident.name, is_raw, Some(ident.span))
|
||||
}
|
||||
|
||||
// AST pretty-printer is used as a fallback for turning AST structures into token streams for
|
||||
// proc macros. Additionally, proc macros may stringify their input and expect it survive the
|
||||
// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
|
||||
// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
|
||||
// hygiene data, most importantly name of the crate it refers to.
|
||||
// As a result we print `$crate` as `crate` if it refers to the local crate
|
||||
// and as `::other_crate_name` if it refers to some other crate.
|
||||
// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
|
||||
// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
|
||||
// so we should not perform this lossy conversion if the top level call to the pretty-printer was
|
||||
// done for a token stream or a single token.
|
||||
fn ident_to_string(name: ast::Name, is_raw: bool, convert_dollar_crate: Option<Span>) -> String {
|
||||
if is_raw {
|
||||
format!("r#{}", name)
|
||||
} else {
|
||||
if name == kw::DollarCrate {
|
||||
if let Some(span) = convert_dollar_crate {
|
||||
let converted = span.ctxt().dollar_crate_name();
|
||||
return if converted.is_path_segment_keyword() {
|
||||
converted.to_string()
|
||||
} else {
|
||||
format!("::{}", converted)
|
||||
};
|
||||
}
|
||||
}
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
|
||||
pub fn token_kind_to_string(tok: &TokenKind) -> String {
|
||||
token_kind_to_string_ext(tok, None)
|
||||
@ -280,7 +246,7 @@ fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>)
|
||||
token::Literal(lit) => literal_to_string(lit),
|
||||
|
||||
/* Name components */
|
||||
token::Ident(s, is_raw) => ident_to_string(s, is_raw, convert_dollar_crate),
|
||||
token::Ident(s, is_raw) => IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string(),
|
||||
token::Lifetime(s) => s.to_string(),
|
||||
|
||||
/* Other */
|
||||
@ -315,7 +281,7 @@ pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
|
||||
token::NtBlock(ref e) => block_to_string(e),
|
||||
token::NtStmt(ref e) => stmt_to_string(e),
|
||||
token::NtPat(ref e) => pat_to_string(e),
|
||||
token::NtIdent(e, is_raw) => ast_ident_to_string(e, is_raw),
|
||||
token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
|
||||
token::NtLifetime(e) => e.to_string(),
|
||||
token::NtLiteral(ref e) => expr_to_string(e),
|
||||
token::NtTT(ref tree) => tt_to_string(tree.clone()),
|
||||
@ -819,7 +785,7 @@ impl<'a> PrintState<'a> for State<'a> {
|
||||
}
|
||||
|
||||
fn print_ident(&mut self, ident: ast::Ident) {
|
||||
self.s.word(ast_ident_to_string(ident, ident.is_raw_guess()));
|
||||
self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
|
||||
self.ann.post(self, AnnNode::Ident(&ident))
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
|
||||
use rustc_ast_pretty::pp::{self, Breaks};
|
||||
use rustc_ast_pretty::pprust::{self, Comments, PrintState};
|
||||
use rustc_ast_pretty::pprust::{Comments, PrintState};
|
||||
use rustc_span::source_map::{SourceMap, Spanned};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::symbol::{kw, IdentPrinter};
|
||||
use rustc_span::{self, BytePos, FileName};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use syntax::ast;
|
||||
@ -126,7 +126,7 @@ impl<'a> PrintState<'a> for State<'a> {
|
||||
}
|
||||
|
||||
fn print_ident(&mut self, ident: ast::Ident) {
|
||||
self.s.word(pprust::ast_ident_to_string(ident, ident.is_raw_guess()));
|
||||
self.s.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
|
||||
self.ann.post(self, AnnNode::Name(&ident.name))
|
||||
}
|
||||
|
||||
|
@ -893,19 +893,17 @@ impl Hash for Ident {
|
||||
|
||||
impl fmt::Debug for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_raw_guess() {
|
||||
write!(f, "r#")?;
|
||||
}
|
||||
write!(f, "{}{:?}", self.name, self.span.ctxt())
|
||||
fmt::Display::fmt(self, f)?;
|
||||
fmt::Debug::fmt(&self.span.ctxt(), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// This implementation is supposed to be used in error messages, so it's expected to be identical
|
||||
/// to printing the original identifier token written in source code (`token_to_string`),
|
||||
/// except that AST identifiers don't keep the rawness flag, so we have to guess it.
|
||||
impl fmt::Display for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_raw_guess() {
|
||||
write!(f, "r#")?;
|
||||
}
|
||||
fmt::Display::fmt(&self.name, f)
|
||||
fmt::Display::fmt(&IdentPrinter::new(self.name, self.is_raw_guess(), None), f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,6 +927,59 @@ impl UseSpecializedDecodable for Ident {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the most general way to print identifiers.
|
||||
/// AST pretty-printer is used as a fallback for turning AST structures into token streams for
|
||||
/// proc macros. Additionally, proc macros may stringify their input and expect it survive the
|
||||
/// stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
|
||||
/// So we need to somehow pretty-print `$crate` in a way preserving at least some of its
|
||||
/// hygiene data, most importantly name of the crate it refers to.
|
||||
/// As a result we print `$crate` as `crate` if it refers to the local crate
|
||||
/// and as `::other_crate_name` if it refers to some other crate.
|
||||
/// Note, that this is only done if the ident token is printed from inside of AST pretty-pringing,
|
||||
/// but not otherwise. Pretty-printing is the only way for proc macros to discover token contents,
|
||||
/// so we should not perform this lossy conversion if the top level call to the pretty-printer was
|
||||
/// done for a token stream or a single token.
|
||||
pub struct IdentPrinter {
|
||||
symbol: Symbol,
|
||||
is_raw: bool,
|
||||
/// Span used for retrieving the crate name to which `$crate` refers to,
|
||||
/// if this field is `None` then the `$crate` conversion doesn't happen.
|
||||
convert_dollar_crate: Option<Span>,
|
||||
}
|
||||
|
||||
impl IdentPrinter {
|
||||
/// The most general `IdentPrinter` constructor. Do not use this.
|
||||
pub fn new(symbol: Symbol, is_raw: bool, convert_dollar_crate: Option<Span>) -> IdentPrinter {
|
||||
IdentPrinter { symbol, is_raw, convert_dollar_crate }
|
||||
}
|
||||
|
||||
/// This implementation is supposed to be used when printing identifiers
|
||||
/// as a part of pretty-printing for larger AST pieces.
|
||||
/// Do not use this either.
|
||||
pub fn for_ast_ident(ident: Ident, is_raw: bool) -> IdentPrinter {
|
||||
IdentPrinter::new(ident.name, is_raw, Some(ident.span))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IdentPrinter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_raw {
|
||||
f.write_str("r#")?;
|
||||
} else {
|
||||
if self.symbol == kw::DollarCrate {
|
||||
if let Some(span) = self.convert_dollar_crate {
|
||||
let converted = span.ctxt().dollar_crate_name();
|
||||
if !converted.is_path_segment_keyword() {
|
||||
f.write_str("::")?;
|
||||
}
|
||||
return fmt::Display::fmt(&converted, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt::Display::fmt(&self.symbol, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An interned string.
|
||||
///
|
||||
/// Internally, a `Symbol` is implemented as an index, and all operations
|
||||
|
Loading…
Reference in New Issue
Block a user