Skip to content

Improve rustc_parse::Parser's debuggability #124779

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 3 commits into from
May 8, 2024
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
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ impl<'t> Iterator for RefTokenTreeCursor<'t> {
/// involve associated types) for getting individual elements, or
/// `RefTokenTreeCursor` if you really want an `Iterator`, e.g. in a `for`
/// loop.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct TokenTreeCursor {
pub stream: TokenStream,
index: usize,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![allow(rustc::untranslatable_diagnostic)]
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(debug_closure_helpers)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
Expand Down
62 changes: 53 additions & 9 deletions compiler/rustc_parse/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) use item::FnParseMode;
pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
pub use path::PathStyle;

use core::fmt;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing};
Expand Down Expand Up @@ -46,7 +47,7 @@ use crate::errors::{
};

bitflags::bitflags! {
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
struct Restrictions: u8 {
const STMT_EXPR = 1 << 0;
const NO_STRUCT_LITERAL = 1 << 1;
Expand All @@ -72,7 +73,7 @@ enum BlockMode {

/// Whether or not we should force collection of tokens for an AST node,
/// regardless of whether or not it has attributes
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ForceCollect {
Yes,
No,
Expand Down Expand Up @@ -120,7 +121,7 @@ macro_rules! maybe_recover_from_interpolated_ty_qpath {
};
}

#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub enum Recovery {
Allowed,
Forbidden,
Expand Down Expand Up @@ -170,7 +171,7 @@ pub struct Parser<'a> {
capture_state: CaptureState,
/// This allows us to recover when the user forget to add braces around
/// multiple statements in the closure body.
pub current_closure: Option<ClosureSpans>,
current_closure: Option<ClosureSpans>,
/// Whether the parser is allowed to do recovery.
/// This is disabled when parsing macro arguments, see #103534
pub recovery: Recovery,
Expand All @@ -182,7 +183,7 @@ pub struct Parser<'a> {
rustc_data_structures::static_assert_size!(Parser<'_>, 264);

/// Stores span information about a closure.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ClosureSpans {
pub whole_closure: Span,
pub closing_pipe: Span,
Expand Down Expand Up @@ -211,15 +212,15 @@ pub type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>);
/// Controls how we capture tokens. Capturing can be expensive,
/// so we try to avoid performing capturing in cases where
/// we will never need an `AttrTokenStream`.
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum Capturing {
/// We aren't performing any capturing - this is the default mode.
No,
/// We are capturing tokens
Yes,
}

#[derive(Clone)]
#[derive(Clone, Debug)]
struct CaptureState {
capturing: Capturing,
replace_ranges: Vec<ReplaceRange>,
Expand All @@ -230,7 +231,7 @@ struct CaptureState {
/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
/// use this type to emit them as a linear sequence. But a linear sequence is
/// what the parser expects, for the most part.
#[derive(Clone)]
#[derive(Clone, Debug)]
struct TokenCursor {
// Cursor for the current (innermost) token stream. The delimiters for this
// token stream are found in `self.stack.last()`; when that is `None` then
Expand Down Expand Up @@ -335,6 +336,7 @@ enum TokenExpectType {
}

/// A sequence separator.
#[derive(Debug)]
struct SeqSep {
/// The separator token.
sep: Option<TokenKind>,
Expand All @@ -352,6 +354,7 @@ impl SeqSep {
}
}

#[derive(Debug)]
pub enum FollowedByType {
Yes,
No,
Expand All @@ -376,7 +379,7 @@ pub enum Trailing {
Yes,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenDescription {
ReservedIdentifier,
Keyword,
Expand Down Expand Up @@ -1534,6 +1537,47 @@ impl<'a> Parser<'a> {
})
}

// debug view of the parser's token stream, up to `{lookahead}` tokens
pub fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ {
struct DebugParser<'dbg> {
parser: &'dbg Parser<'dbg>,
lookahead: usize,
}

impl fmt::Debug for DebugParser<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { parser, lookahead } = self;
let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of

// we don't need N spans, but we want at least one, so print all of prev_token
dbg_fmt.field("prev_token", &parser.prev_token);
// make it easier to peek farther ahead by taking TokenKinds only until EOF
let tokens = (0..*lookahead)
.map(|i| parser.look_ahead(i, |tok| tok.kind.clone()))
.scan(parser.prev_token == TokenKind::Eof, |eof, tok| {
let current = eof.then_some(tok.clone()); // include a trailing EOF token
*eof |= &tok == &TokenKind::Eof;
current
});
dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish());
dbg_fmt.field("approx_token_stream_pos", &parser.num_bump_calls);

// some fields are interesting for certain values, as they relate to macro parsing
if let Some(subparser) = parser.subparser_name {
dbg_fmt.field("subparser_name", &subparser);
}
if let Recovery::Forbidden = parser.recovery {
dbg_fmt.field("recovery", &parser.recovery);
}

// imply there's "more to know" than this view
dbg_fmt.finish_non_exhaustive()
}
}

DebugParser { parser: self, lookahead }
}

pub fn clear_expected_tokens(&mut self) {
self.expected_tokens.clear();
}
Expand Down
Loading