Skip to content
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

Add syntux module #3880

Merged
merged 12 commits into from
Oct 25, 2019
Prev Previous commit
Next Next commit
Add Parser::parse_crate
  • Loading branch information
topecongiro committed Oct 22, 2019
commit 64c44f4e4a50df3eb9c528366856716bc79fc4d5
112 changes: 15 additions & 97 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

use std::collections::HashMap;
use std::io::{self, Write};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::time::{Duration, Instant};

use syntax::ast;
use syntax::errors::DiagnosticBuilder;
use syntax::parse;
use syntax::source_map::{SourceMap, Span, DUMMY_SP};
use syntax::source_map::{SourceMap, Span};

use self::newline_style::apply_newline_style;
use crate::comment::{CharClasses, FullCodeCharKind};
use crate::config::{Config, FileName, Verbosity};
use crate::ignore_path::IgnorePathSet;
use crate::issues::BadIssueSeeker;
use crate::syntux::parser::{Parser, ParserError};
use crate::syntux::session::{ErrorEmission, ParseSess};
use crate::utils::count_newlines;
use crate::visitor::{FmtVisitor, SnippetProvider};
Expand Down Expand Up @@ -74,6 +73,7 @@ fn format_project<T: FormatHandler>(
}

// Parse the crate.
let is_stdin = !input.is_text();
scampi marked this conversation as resolved.
Show resolved Hide resolved
let error_emission = if config.hide_parse_errors() {
ErrorEmission::Silence
} else {
Expand All @@ -82,17 +82,17 @@ fn format_project<T: FormatHandler>(
let mut parse_session = ParseSess::new(error_emission, ignore_path_set);
let mut report = FormatReport::new();
let directory_ownership = input.to_directory_ownership();
let krate = match parse_crate(
input,
&parse_session,
config,
&mut report,
directory_ownership,
) {

let krate = match Parser::parse_crate(config, input, directory_ownership, &parse_session) {
Ok(krate) => krate,
// Surface parse error via Session (errors are merged there from report)
Err(ErrorKind::ParseError) => return Ok(report),
Err(e) => return Err(e),
Err(e) => {
let forbid_verbose = is_stdin || e != ParserError::ParsePanicError;
should_emit_verbose(forbid_verbose, config, || {
eprintln!("The Rust parser panicked");
});
report.add_parsing_error();
return Ok(report);
}
};
timer = timer.done_parsing();

Expand Down Expand Up @@ -627,93 +627,11 @@ impl<'a> FormatLines<'a> {
}
}

fn parse_crate(
input: Input,
parse_session: &ParseSess,
config: &Config,
report: &mut FormatReport,
directory_ownership: Option<parse::DirectoryOwnership>,
) -> Result<ast::Crate, ErrorKind> {
let input_is_stdin = input.is_text();

let parser = match input {
Input::File(ref file) => {
// Use `new_sub_parser_from_file` when we the input is a submodule.
Ok(if let Some(dir_own) = directory_ownership {
parse::new_sub_parser_from_file(
parse_session.inner(),
file,
dir_own,
None,
DUMMY_SP,
)
} else {
parse::new_parser_from_file(parse_session.inner(), file)
})
}
Input::Text(text) => parse::maybe_new_parser_from_source_str(
parse_session.inner(),
syntax::source_map::FileName::Custom("stdin".to_owned()),
text,
)
.map(|mut parser| {
parser.recurse_into_file_modules = false;
parser
}),
};

let result = match parser {
Ok(mut parser) => {
parser.cfg_mods = false;
if config.skip_children() {
parser.recurse_into_file_modules = false;
}

let mut parser = AssertUnwindSafe(parser);
catch_unwind(move || parser.0.parse_crate_mod().map_err(|d| vec![d]))
}
Err(diagnostics) => {
parse_session.emit_diagnostics(diagnostics);
report.add_parsing_error();
return Err(ErrorKind::ParseError);
}
};

match result {
Ok(Ok(c)) => {
if !parse_session.has_errors() {
return Ok(c);
}
// This scenario occurs when the parser encountered errors
// but was still able to recover. If all of the parser errors
// occurred in files that are ignored, then reset
// the error count and continue.
// https://github.com/rust-lang/rustfmt/issues/3779
if parse_session.can_reset_errors() {
parse_session.reset_errors();
return Ok(c);
}
}
Ok(Err(mut diagnostics)) => diagnostics.iter_mut().for_each(DiagnosticBuilder::emit),
Err(_) => {
// Note that if you see this message and want more information,
// then run the `parse_crate_mod` function above without
// `catch_unwind` so rustfmt panics and you can get a backtrace.
should_emit_verbose(input_is_stdin, config, || {
println!("The Rust parser panicked")
});
}
}

report.add_parsing_error();
Err(ErrorKind::ParseError)
}

fn should_emit_verbose<F>(is_stdin: bool, config: &Config, f: F)
fn should_emit_verbose<F>(forbid_verbose_output: bool, config: &Config, f: F)
where
F: Fn(),
{
if config.verbose() == Verbosity::Verbose && !is_stdin {
if config.verbose() == Verbosity::Verbose && !forbid_verbose_output {
f();
}
}
1 change: 1 addition & 0 deletions src/syntux.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! This module defines a thin abstract layer on top of the rustc's parser and syntax libraries.

pub(crate) mod parser;
pub(crate) mod session;
147 changes: 131 additions & 16 deletions src/syntux/parser.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,154 @@
use std::panic::{catch_unwind, AssertUnwindSafe};

use syntax::ast;
use syntax::errors::Diagnostic;
use syntax::parse::parser::Parser as RawParser;
use syntax::parse::DirectoryOwnership;
use syntax::source_map::DUMMY_SP;

use crate::Input;
use crate::syntux::session::ParseSess;
use crate::{Config, Input};

/// A parser for Rust source code.
pub struct Parser<'a> {
pub(crate) struct Parser<'a> {
parser: RawParser<'a>,
sess: &'a ParseSess,
}

/// A builder for the `Parser`.
pub struct ParserBuilder {
silent: bool,
input: Input,
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
config: Option<&'a Config>,
sess: Option<&'a ParseSess>,
input: Option<Input>,
directory_ownership: Option<DirectoryOwnership>,
}

impl ParserBuilder {
pub fn input(&mut self, input: Input) -> &mut ParserBuilder {
self.input = input;
impl<'a> ParserBuilder<'a> {
pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
self.input = Some(input);
self
}

pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
self.sess = Some(sess);
self
}

pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
self.config = Some(config);
self
}

pub fn build(self) -> Result<Parser, ParserError> {
match self.input {
Input::File(ref file) => {
parse::new_parser_from_file(par)
pub(crate) fn directory_ownership(
mut self,
directory_ownership: Option<DirectoryOwnership>,
) -> ParserBuilder<'a> {
self.directory_ownership = directory_ownership;
self
}

pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
let config = self.config.ok_or(ParserError::NoConfig)?;
let sess = self.sess.ok_or(ParserError::NoParseSess)?;
let input = self.input.ok_or(ParserError::NoInput)?;

let mut parser = match Self::parser(sess.inner(), input, self.directory_ownership) {
Ok(p) => p,
Err(db) => {
sess.emit_diagnostics(db);
// report.add_parsing_error();
scampi marked this conversation as resolved.
Show resolved Hide resolved
return Err(ParserError::ParserCreationError);
}
};

parser.cfg_mods = false;

if config.skip_children() {
parser.recurse_into_file_modules = false;
}

Ok(Parser { parser, sess })
}

fn parser(
sess: &'a syntax::parse::ParseSess,
input: Input,
directory_ownership: Option<DirectoryOwnership>,
) -> Result<syntax::parse::parser::Parser<'a>, Vec<Diagnostic>> {
match input {
Input::File(ref file) => Ok(if let Some(directory_ownership) = directory_ownership {
syntax::parse::new_sub_parser_from_file(
sess,
file,
directory_ownership,
None,
DUMMY_SP,
)
} else {
syntax::parse::new_parser_from_file(sess, file)
}),
Input::Text(text) => syntax::parse::maybe_new_parser_from_source_str(
sess,
syntax::source_map::FileName::Custom("stdin".to_owned()),
text,
)
.map(|mut parser| {
parser.recurse_into_file_modules = false;
parser
}),
}
Ok(Parser)
}
}

enum ParserError {
#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
NoConfig,
NoParseSess,
NoInput,
ParserCreationError,
ParseError,
ParsePanicError,
}

impl<'a> Parser<'a> {
pub fn from_string(text: String) -> Result<Parser<'a>, ParserError> {
pub(crate) fn parse_crate(
config: &'a Config,
input: Input,
directory_ownership: Option<DirectoryOwnership>,
sess: &'a ParseSess,
) -> Result<ast::Crate, ParserError> {
let mut parser = ParserBuilder::default()
.config(config)
.input(input)
.directory_ownership(directory_ownership)
.sess(sess)
.build()?;

parser.parse_crate_inner()
}
}

fn parse_crate_inner(&mut self) -> Result<ast::Crate, ParserError> {
let mut parser = AssertUnwindSafe(&mut self.parser);

match catch_unwind(move || parser.parse_crate_mod()) {
Ok(Ok(krate)) => {
if !self.sess.has_errors() {
return Ok(krate);
}

if self.sess.can_reset_errors() {
self.sess.reset_errors();
return Ok(krate);
}

Err(ParserError::ParseError)
}
Ok(Err(mut db)) => {
db.emit();
Err(ParserError::ParseError)
}
Err(_) => Err(ParserError::ParsePanicError),
}
}
}