Skip to content

Deduplicate identifier printing a bit #69387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 4 additions & 38 deletions src/librustc_ast_pretty/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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))
}

Expand Down
6 changes: 3 additions & 3 deletions src/librustc_hir/print.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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))
}

Expand Down
67 changes: 59 additions & 8 deletions src/librustc_span/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand All @@ -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
Expand Down