Skip to content

Commit

Permalink
Auto merge of rust-lang#77135 - Aaron1011:pretty-ignore-paren, r=petr…
Browse files Browse the repository at this point in the history
…ochenkov

Refactor AST pretty-printing to allow skipping insertion of extra parens

Fixes rust-lang#75734
Makes progress towards rust-lang#43081
Unblocks PR rust-lang#76130

When pretty-printing an AST node, we may insert additional parenthesis
to ensure that precedence is properly preserved in code we output.
However, the proc macro implementation relies on comparing a
pretty-printed AST node to the captured `TokenStream`. Inserting extra
parenthesis changes the structure of the reparsed `TokenStream`, making
the comparison fail.

This PR refactors the AST pretty-printing code to allow skipping the
insertion of additional parenthesis. Several freestanding methods are
moved to trait methods on `PrintState`, which keep track of an internal
`insert_extra_parens` flag. This flag is normally `true`, but we expose
a public method which allows pretty-printing a nonterminal with
`insert_extra_parens = false`.

To avoid changing the public interface of `rustc_ast_pretty`, the
freestanding `_to_string` methods are changed to delegate to a
newly-crated `State`. The main pretty-printing code is moved to a new
`state` module to ensure that it does not accidentally call any of these
public helper functions (instead, the internal functions with the same
name should be used).
  • Loading branch information
bors committed Oct 14, 2020
2 parents f243a2a + 9a6ea38 commit 4ba5068
Show file tree
Hide file tree
Showing 14 changed files with 633 additions and 197 deletions.
21 changes: 16 additions & 5 deletions compiler/rustc_ast/src/token.rs
Expand Up @@ -810,25 +810,36 @@ impl Nonterminal {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
let matches_prefix = |prefix| {
// Check for a path that ends with 'prefix*/src/lib.rs'
let matches_prefix = |prefix, filename| {
// Check for a path that ends with 'prefix*/src/<filename>'
let mut iter = path.components().rev();
iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs")
iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
&& iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
&& iter
.next()
.and_then(|p| p.as_os_str().to_str())
.map_or(false, |p| p.starts_with(prefix))
};

if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl"))
|| (macro_name == sym::arrays && matches_prefix("js-sys"))
if (macro_name == sym::impl_macros
&& matches_prefix("time-macros-impl", "lib.rs"))
|| (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
return Some((*ident, *is_raw));
}
}

if macro_name == sym::tuple_from_req
&& (matches_prefix("actix-web", "extract.rs")
|| matches_prefix("actori-web", "extract.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$T") {
return Some((*ident, *is_raw));
}
}
}
}
}
Expand Down
104 changes: 104 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -0,0 +1,104 @@
#[cfg(test)]
mod tests;

pub mod state;
pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State};

use rustc_ast as ast;
use rustc_ast::token::{Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};

pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String {
let state = State::without_insert_extra_parens();
state.nonterminal_to_string(nt)
}

pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
State::new().nonterminal_to_string(nt)
}

/// Print the token kind precisely, without converting `$crate` into its respective crate name.
pub fn token_kind_to_string(tok: &TokenKind) -> String {
State::new().token_kind_to_string(tok)
}

/// Print the token precisely, without converting `$crate` into its respective crate name.
pub fn token_to_string(token: &Token) -> String {
State::new().token_to_string(token)
}

pub fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
State::new().token_to_string_ext(token, convert_dollar_crate)
}

pub fn ty_to_string(ty: &ast::Ty) -> String {
State::new().ty_to_string(ty)
}

pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
State::new().bounds_to_string(bounds)
}

pub fn pat_to_string(pat: &ast::Pat) -> String {
State::new().pat_to_string(pat)
}

pub fn expr_to_string(e: &ast::Expr) -> String {
State::new().expr_to_string(e)
}

pub fn tt_to_string(tt: &TokenTree) -> String {
State::new().tt_to_string(tt)
}

pub fn tts_to_string(tokens: &TokenStream) -> String {
State::new().tts_to_string(tokens)
}

pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
State::new().stmt_to_string(stmt)
}

pub fn item_to_string(i: &ast::Item) -> String {
State::new().item_to_string(i)
}

pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
State::new().generic_params_to_string(generic_params)
}

pub fn path_to_string(p: &ast::Path) -> String {
State::new().path_to_string(p)
}

pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
State::new().path_segment_to_string(p)
}

pub fn vis_to_string(v: &ast::Visibility) -> String {
State::new().vis_to_string(v)
}

pub fn block_to_string(blk: &ast::Block) -> String {
State::new().block_to_string(blk)
}

pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
State::new().meta_list_item_to_string(li)
}

pub fn attr_item_to_string(ai: &ast::AttrItem) -> String {
State::new().attr_item_to_string(ai)
}

pub fn attribute_to_string(attr: &ast::Attribute) -> String {
State::new().attribute_to_string(attr)
}

pub fn param_to_string(arg: &ast::Param) -> String {
State::new().param_to_string(arg)
}

pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
State::new().to_string(f)
}

0 comments on commit 4ba5068

Please sign in to comment.