refactor: backport syntux mod

This commit is contained in:
Caleb Cartwright 2020-03-26 21:25:34 -05:00
parent 9699c96cf1
commit bd5dff4012
22 changed files with 959 additions and 766 deletions

View file

@ -56,6 +56,7 @@ ignore = "0.4.11"
annotate-snippets = { version = "0.6", features = ["ansi_term"] }
structopt = "0.3"
rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" }
lazy_static = "1.0.0"
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
@ -93,6 +94,3 @@ version = "647.0.0"
[dependencies.syntax]
package = "rustc-ap-rustc_ast"
version = "647.0.0"
[dev-dependencies]
lazy_static = "1.0.0"

View file

@ -43,7 +43,7 @@ pub(crate) fn rewrite_closure(
if let ast::ExprKind::Block(ref block, _) = body.kind {
// The body of the closure is an empty block.
if block.stmts.is_empty() && !block_contains_comment(block, context.source_map) {
if block.stmts.is_empty() && !block_contains_comment(context, block) {
return body
.rewrite(context, shape)
.map(|s| format!("{} {}", prefix, s));
@ -116,7 +116,7 @@ fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -
is_unsafe_block(block)
|| block.stmts.len() > 1
|| has_attributes
|| block_contains_comment(block, context.source_map)
|| block_contains_comment(context, block)
|| prefix.contains('\n')
}
@ -309,7 +309,7 @@ pub(crate) fn rewrite_last_closure(
ast::ExprKind::Block(ref block, _)
if !is_unsafe_block(block)
&& !context.inside_macro()
&& is_simple_block(block, Some(&body.attrs), context.source_map) =>
&& is_simple_block(context, block, Some(&body.attrs)) =>
{
stmt_expr(&block.stmts[0]).unwrap_or(body)
}

View file

@ -1556,10 +1556,10 @@ pub(crate) fn recover_comment_removed(
// We missed some comments. Warn and keep the original text.
if context.config.error_on_unformatted() {
context.report.append(
context.source_map.span_to_filename(span).into(),
context.parse_sess.span_to_filename(span),
vec![FormattingError::from_span(
span,
&context.source_map,
&context.parse_sess,
ErrorKind::LostComment,
)],
);

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::cmp::min;
use itertools::Itertools;
use rustc_span::{source_map::SourceMap, BytePos, Span};
use rustc_span::{BytePos, Span};
use syntax::token::{DelimToken, LitKind};
use syntax::{ast, ptr};
@ -430,7 +430,7 @@ fn rewrite_empty_block(
return None;
}
if !block_contains_comment(block, context.source_map) && shape.width >= 2 {
if !block_contains_comment(context, block) && shape.width >= 2 {
return Some(format!("{}{}{{}}", prefix, label_str));
}
@ -487,7 +487,7 @@ fn rewrite_single_line_block(
label: Option<ast::Label>,
shape: Shape,
) -> Option<String> {
if is_simple_block(block, attrs, context.source_map) {
if is_simple_block(context, block, attrs) {
let expr_shape = shape.offset_left(last_line_width(prefix))?;
let expr_str = block.stmts[0].rewrite(context, expr_shape)?;
let label_str = rewrite_label(label);
@ -750,8 +750,8 @@ impl<'a> ControlFlow<'a> {
let fixed_cost = self.keyword.len() + " { } else { }".len();
if let ast::ExprKind::Block(ref else_node, _) = else_block.kind {
if !is_simple_block(self.block, None, context.source_map)
|| !is_simple_block(else_node, None, context.source_map)
if !is_simple_block(context, self.block, None)
|| !is_simple_block(context, else_node, None)
|| pat_expr_str.contains('\n')
{
return None;
@ -1134,9 +1134,8 @@ fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Op
}
}
pub(crate) fn block_contains_comment(block: &ast::Block, source_map: &SourceMap) -> bool {
let snippet = source_map.span_to_snippet(block.span).unwrap();
contains_comment(&snippet)
pub(crate) fn block_contains_comment(context: &RewriteContext<'_>, block: &ast::Block) -> bool {
contains_comment(context.snippet(block.span))
}
// Checks that a block contains no statements, an expression and no comments or
@ -1144,37 +1143,37 @@ pub(crate) fn block_contains_comment(block: &ast::Block, source_map: &SourceMap)
// FIXME: incorrectly returns false when comment is contained completely within
// the expression.
pub(crate) fn is_simple_block(
context: &RewriteContext<'_>,
block: &ast::Block,
attrs: Option<&[ast::Attribute]>,
source_map: &SourceMap,
) -> bool {
block.stmts.len() == 1
&& stmt_is_expr(&block.stmts[0])
&& !block_contains_comment(block, source_map)
&& !block_contains_comment(context, block)
&& attrs.map_or(true, |a| a.is_empty())
}
/// Checks whether a block contains at most one statement or expression, and no
/// comments or attributes.
pub(crate) fn is_simple_block_stmt(
context: &RewriteContext<'_>,
block: &ast::Block,
attrs: Option<&[ast::Attribute]>,
source_map: &SourceMap,
) -> bool {
block.stmts.len() <= 1
&& !block_contains_comment(block, source_map)
&& !block_contains_comment(context, block)
&& attrs.map_or(true, |a| a.is_empty())
}
/// Checks whether a block contains no statements, expressions, comments, or
/// inner attributes.
pub(crate) fn is_empty_block(
context: &RewriteContext<'_>,
block: &ast::Block,
attrs: Option<&[ast::Attribute]>,
source_map: &SourceMap,
) -> bool {
block.stmts.is_empty()
&& !block_contains_comment(block, source_map)
&& !block_contains_comment(context, block)
&& attrs.map_or(true, |a| inner_attributes(a).is_empty())
}

View file

@ -1,29 +1,20 @@
// High level formatting functions.
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::{self, Write};
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::rc::Rc;
use std::time::{Duration, Instant};
use rustc_data_structures::sync::{Lrc, Send};
use rustc_errors::emitter::{Emitter, EmitterWriter};
use rustc_errors::{ColorConfig, Diagnostic, DiagnosticBuilder, Handler, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess;
use rustc_span::{
source_map::{FilePathMapping, SourceMap},
Span, DUMMY_SP,
};
use rustc_span::Span;
use syntax::ast;
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::{DirectoryOwnership, Parser, ParserError};
use crate::syntux::session::ParseSess;
use crate::utils::count_newlines;
use crate::visitor::{FmtVisitor, SnippetProvider};
use crate::visitor::FmtVisitor;
use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session};
mod newline_style;
@ -71,54 +62,41 @@ fn format_project<T: FormatHandler>(
let main_file = input.file_name();
let input_is_stdin = main_file == FileName::Stdin;
let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
Ok(set) => Rc::new(set),
Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
};
if config.skip_children() && ignore_path_set.is_match(&main_file) {
let mut parse_session = ParseSess::new(config)?;
if config.skip_children() && parse_session.ignore_file(&main_file) {
return Ok(FormatReport::new());
}
// Parse the crate.
let can_reset_parser_errors = Rc::new(RefCell::new(false));
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let mut parse_session = make_parse_sess(
source_map.clone(),
config,
Rc::clone(&ignore_path_set),
can_reset_parser_errors.clone(),
);
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,
can_reset_parser_errors.clone(),
) {
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 = input_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();
// Suppress error output if we have to do any further parsing.
let silent_emitter = silent_emitter();
parse_session.span_diagnostic = Handler::with_emitter(true, None, silent_emitter);
parse_session.set_silent_emitter();
let mut context = FormatContext::new(&krate, report, parse_session, config, handler);
let files = modules::ModResolver::new(
&context.parse_session,
directory_ownership.unwrap_or(rustc_parse::DirectoryOwnership::UnownedViaMod),
directory_ownership.unwrap_or(DirectoryOwnership::UnownedViaMod),
!(input_is_stdin || config.skip_children()),
)
.visit_crate(&krate)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
for (path, module) in files {
let should_ignore = !input_is_stdin && ignore_path_set.is_match(&path);
let should_ignore = !input_is_stdin && context.ignore_file(&path);
if (config.skip_children() && path != main_file) || should_ignore {
continue;
}
@ -150,6 +128,10 @@ struct FormatContext<'a, T: FormatHandler> {
}
impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
fn ignore_file(&self, path: &FileName) -> bool {
self.parse_session.ignore_file(path)
}
// Formats a single file/module.
fn format_file(
&mut self,
@ -157,15 +139,8 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
module: &ast::Mod,
is_root: bool,
) -> Result<(), ErrorKind> {
let source_file = self
.parse_session
.source_map()
.lookup_char_pos(module.inner.lo())
.file;
let big_snippet = source_file.src.as_ref().unwrap();
let snippet_provider =
SnippetProvider::new(source_file.start_pos, source_file.end_pos, big_snippet);
let mut visitor = FmtVisitor::from_source_map(
let snippet_provider = self.parse_session.snippet_provider(module.inner);
let mut visitor = FmtVisitor::from_parse_sess(
&self.parse_session,
&self.config,
&snippet_provider,
@ -175,16 +150,16 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
// Format inner attributes if available.
if !self.krate.attrs.is_empty() && is_root {
visitor.skip_empty_lines(source_file.end_pos);
visitor.skip_empty_lines(snippet_provider.end_pos());
if visitor.visit_attrs(&self.krate.attrs, ast::AttrStyle::Inner) {
visitor.push_rewrite(module.inner, None);
} else {
visitor.format_separate_mod(module, &*source_file);
visitor.format_separate_mod(module, snippet_provider.end_pos());
}
} else {
visitor.last_pos = source_file.start_pos;
visitor.skip_empty_lines(source_file.end_pos);
visitor.format_separate_mod(module, &*source_file);
visitor.last_pos = snippet_provider.start_pos();
visitor.skip_empty_lines(snippet_provider.end_pos());
visitor.format_separate_mod(module, snippet_provider.end_pos());
};
debug_assert_eq!(
@ -209,7 +184,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
apply_newline_style(
self.config.newline_style(),
&mut visitor.buffer,
&big_snippet,
snippet_provider.entire_snippet(),
);
if visitor.macro_rewrite_failure {
@ -219,7 +194,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
.add_non_formatted_ranges(visitor.skipped_range.borrow().clone());
self.handler.handle_formatted_file(
self.parse_session.source_map(),
&self.parse_session,
path,
visitor.buffer.to_owned(),
&mut self.report,
@ -231,7 +206,7 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> {
trait FormatHandler {
fn handle_formatted_file(
&mut self,
source_map: &SourceMap,
parse_session: &ParseSess,
path: FileName,
result: String,
report: &mut FormatReport,
@ -242,14 +217,14 @@ impl<'b, T: Write + 'b> FormatHandler for Session<'b, T> {
// Called for each formatted file.
fn handle_formatted_file(
&mut self,
source_map: &SourceMap,
parse_session: &ParseSess,
path: FileName,
result: String,
report: &mut FormatReport,
) -> Result<(), ErrorKind> {
if let Some(ref mut out) = self.out {
match source_file::write_file(
Some(source_map),
Some(parse_session),
&path,
&result,
out,
@ -282,23 +257,15 @@ pub(crate) struct FormattingError {
impl FormattingError {
pub(crate) fn from_span(
span: Span,
source_map: &SourceMap,
parse_sess: &ParseSess,
kind: ErrorKind,
) -> FormattingError {
FormattingError {
line: source_map.lookup_char_pos(span.lo()).line,
line: parse_sess.line_of_byte_pos(span.lo()),
is_comment: kind.is_comment(),
kind,
is_string: false,
line_buffer: source_map
.span_to_lines(span)
.ok()
.and_then(|fl| {
fl.file
.get_line(fl.lines[0].line_index)
.map(std::borrow::Cow::into_owned)
})
.unwrap_or_else(String::new),
line_buffer: parse_sess.span_to_first_line_string(span),
}
}
@ -633,358 +600,11 @@ impl<'a> FormatLines<'a> {
}
}
fn parse_crate(
input: Input,
parse_session: &ParseSess,
config: &Config,
report: &mut FormatReport,
directory_ownership: Option<rustc_parse::DirectoryOwnership>,
can_reset_parser_errors: Rc<RefCell<bool>>,
) -> 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 {
rustc_parse::new_sub_parser_from_file(parse_session, file, dir_own, None, DUMMY_SP)
} else {
rustc_parse::new_parser_from_file(parse_session, file)
})
}
Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
parse_session,
rustc_span::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) => {
for diagnostic in diagnostics {
parse_session.span_diagnostic.emit_diagnostic(&diagnostic);
}
report.add_parsing_error();
return Err(ErrorKind::ParseError);
}
};
match result {
Ok(Ok(c)) => {
if !parse_session.span_diagnostic.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 *can_reset_parser_errors.borrow() {
parse_session.span_diagnostic.reset_err_count();
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)
}
struct SilentOnIgnoredFilesEmitter {
ignore_path_set: Rc<IgnorePathSet>,
source_map: Rc<SourceMap>,
emitter: Box<dyn Emitter + Send>,
has_non_ignorable_parser_errors: bool,
can_reset: Rc<RefCell<bool>>,
}
impl SilentOnIgnoredFilesEmitter {
fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
self.has_non_ignorable_parser_errors = true;
*self.can_reset.borrow_mut() = false;
self.emitter.emit_diagnostic(db);
}
}
impl Emitter for SilentOnIgnoredFilesEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, db: &Diagnostic) {
if db.level == DiagnosticLevel::Fatal {
return self.handle_non_ignoreable_error(db);
}
if let Some(primary_span) = &db.span.primary_span() {
let file_name = self.source_map.span_to_filename(*primary_span);
if let rustc_span::FileName::Real(ref path) = file_name {
if self
.ignore_path_set
.is_match(&FileName::Real(path.to_path_buf()))
{
if !self.has_non_ignorable_parser_errors {
*self.can_reset.borrow_mut() = true;
}
return;
}
};
}
self.handle_non_ignoreable_error(db);
}
}
/// Emitter which discards every error.
struct SilentEmitter;
impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
}
fn silent_emitter() -> Box<dyn Emitter + Send> {
Box::new(SilentEmitter {})
}
fn make_parse_sess(
source_map: Rc<SourceMap>,
config: &Config,
ignore_path_set: Rc<IgnorePathSet>,
can_reset: Rc<RefCell<bool>>,
) -> ParseSess {
let supports_color = term::stderr().map_or(false, |term| term.supports_color());
let color_cfg = if supports_color {
ColorConfig::Auto
} else {
ColorConfig::Never
};
let emitter = if config.hide_parse_errors() {
silent_emitter()
} else {
Box::new(EmitterWriter::stderr(
color_cfg,
Some(source_map.clone()),
false,
false,
None,
false,
))
};
let handler = Handler::with_emitter(
true,
None,
Box::new(SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false,
source_map: source_map.clone(),
emitter,
ignore_path_set,
can_reset,
}),
);
ParseSess::with_span_handler(handler, source_map)
}
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();
}
}
#[cfg(test)]
mod tests {
use super::*;
mod emitter {
use super::*;
use crate::config::IgnoreList;
use crate::is_nightly_channel;
use crate::utils::mk_sp;
use rustc_span::{BytePos, FileName as SourceMapFileName, MultiSpan, DUMMY_SP};
use std::path::{Path, PathBuf};
struct TestEmitter {
num_emitted_errors: Rc<RefCell<u32>>,
}
impl Emitter for TestEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, _db: &Diagnostic) {
*self.num_emitted_errors.borrow_mut() += 1;
}
}
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
Diagnostic {
level,
code: None,
message: vec![],
children: vec![],
suggestions: vec![],
span: span.unwrap_or_else(MultiSpan::new),
sort_span: DUMMY_SP,
}
}
fn build_emitter(
num_emitted_errors: Rc<RefCell<u32>>,
can_reset: Rc<RefCell<bool>>,
source_map: Option<Rc<SourceMap>>,
ignore_list: Option<IgnoreList>,
) -> SilentOnIgnoredFilesEmitter {
let emitter_writer = TestEmitter { num_emitted_errors };
let source_map =
source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
let ignore_path_set =
Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false,
source_map,
emitter: Box::new(emitter_writer),
ignore_path_set,
can_reset,
}
}
fn get_ignore_list(config: &str) -> IgnoreList {
Config::from_toml(config, Path::new("")).unwrap().ignore()
}
#[test]
fn handles_fatal_parse_error_in_ignored_file() {
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
emitter.emit_diagnostic(&fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 1);
assert_eq!(*can_reset_errors.borrow(), false);
}
#[test]
fn handles_recoverable_parse_error_in_ignored_file() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
emitter.emit_diagnostic(&non_fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 0);
assert_eq!(*can_reset_errors.borrow(), true);
}
#[test]
fn handles_recoverable_parse_error_in_non_ignored_file() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
None,
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
emitter.emit_diagnostic(&non_fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 1);
assert_eq!(*can_reset_errors.borrow(), false);
}
#[test]
fn handles_mix_of_recoverable_parse_error() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
let fatal_source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map
.new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
source_map
.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source);
source_map.new_source_file(
SourceMapFileName::Real(PathBuf::from("fatal.rs")),
fatal_source,
);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
emitter.emit_diagnostic(&bar_diagnostic);
emitter.emit_diagnostic(&foo_diagnostic);
emitter.emit_diagnostic(&fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 2);
assert_eq!(*can_reset_errors.borrow(), false);
}
}
}

View file

@ -363,17 +363,17 @@ impl<'a> FmtVisitor<'a> {
return None;
}
let source_map = self.get_context().source_map;
let context = self.get_context();
if self.config.empty_item_single_line()
&& is_empty_block(block, None, source_map)
&& is_empty_block(&context, block, None)
&& self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width()
&& !last_line_contains_single_line_comment(fn_str)
{
return Some(format!("{} {{}}", fn_str));
}
if !self.config.fn_single_line() || !is_simple_block_stmt(block, None, source_map) {
if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) {
return None;
}

View file

@ -3,7 +3,6 @@
#[macro_use]
extern crate derive_new;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
@ -20,7 +19,6 @@ use std::rc::Rc;
use failure::Fail;
use ignore;
use rustc_parse::DirectoryOwnership;
use syntax::ast;
use crate::comment::LineClasses;
@ -28,6 +26,7 @@ use crate::emitter::Emitter;
use crate::formatting::{FormatErrorMap, FormattingError, ReportedErrors, SourceFile};
use crate::issues::Issue;
use crate::shape::Indent;
use crate::syntux::parser::DirectoryOwnership;
use crate::utils::indent_next_line;
pub use crate::config::{
@ -75,6 +74,7 @@ pub(crate) mod source_map;
mod spanned;
mod stmt;
mod string;
mod syntux;
#[cfg(test)]
mod test;
mod types;
@ -509,13 +509,6 @@ pub enum Input {
}
impl Input {
fn is_text(&self) -> bool {
match *self {
Input::File(_) => false,
Input::Text(_) => true,
}
}
fn file_name(&self) -> FileName {
match *self {
Input::File(ref file) => FileName::Real(file.clone()),

View file

@ -560,7 +560,7 @@ pub(crate) struct ListItems<'a, I, F1, F2, F3>
where
I: Iterator,
{
snippet_provider: &'a SnippetProvider<'a>,
snippet_provider: &'a SnippetProvider,
inner: Peekable<I>,
get_lo: F1,
get_hi: F2,
@ -777,7 +777,7 @@ where
#[allow(clippy::too_many_arguments)]
// Creates an iterator over a list's items with associated comments.
pub(crate) fn itemize_list<'a, T, I, F1, F2, F3>(
snippet_provider: &'a SnippetProvider<'_>,
snippet_provider: &'a SnippetProvider,
inner: I,
terminator: &'a str,
separator: &'a str,

View file

@ -178,8 +178,8 @@ fn return_macro_parse_failure_fallback(
}
context.skipped_range.borrow_mut().push((
context.source_map.lookup_line(span.lo()).unwrap().line,
context.source_map.lookup_line(span.hi()).unwrap().line,
context.parse_sess.line_of_byte_pos(span.lo()),
context.parse_sess.line_of_byte_pos(span.hi()),
));
// Return the snippet unmodified if the macro is not block-like
@ -286,7 +286,7 @@ fn rewrite_macro_inner(
}
}
let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
let mut arg_vec = Vec::new();
let mut vec_with_semi = false;
let mut trailing_comma = false;
@ -1190,7 +1190,7 @@ pub(crate) fn convert_try_mac(mac: &ast::Mac, context: &RewriteContext<'_>) -> O
let path = &pprust::path_to_string(&mac.path);
if path == "try" || path == "r#try" {
let ts = mac.args.inner_tokens();
let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
Some(ast::Expr {
id: ast::NodeId::root(), // dummy value
@ -1422,7 +1422,7 @@ fn format_lazy_static(
ts: &TokenStream,
) -> Option<String> {
let mut result = String::with_capacity(1024);
let mut parser = new_parser_from_tts(context.parse_session, ts.trees().collect());
let mut parser = new_parser_from_tts(context.parse_sess.inner(), ts.trees().collect());
let nested_shape = shape
.block_indent(context.config.tab_spaces())
.with_max_width(context.config);

View file

@ -276,7 +276,7 @@ fn block_can_be_flattened<'a>(
ast::ExprKind::Block(ref block, _)
if !is_unsafe_block(block)
&& !context.inside_macro()
&& is_simple_block(block, Some(&expr.attrs), context.source_map) =>
&& is_simple_block(context, block, Some(&expr.attrs)) =>
{
Some(&*block)
}
@ -332,10 +332,7 @@ fn rewrite_match_body(
shape.offset_left(extra_offset(pats_str, shape) + 4),
);
let (is_block, is_empty_block) = if let ast::ExprKind::Block(ref block, _) = body.kind {
(
true,
is_empty_block(block, Some(&body.attrs), context.source_map),
)
(true, is_empty_block(context, block, Some(&body.attrs)))
} else {
(false, false)
};

View file

@ -86,9 +86,8 @@ impl<'a> FmtVisitor<'a> {
assert!(
start < end,
"Request to format inverted span: {:?} to {:?}",
self.source_map.lookup_char_pos(start),
self.source_map.lookup_char_pos(end)
"Request to format inverted span: {}",
self.parse_sess.span_to_debug_info(mk_sp(start, end)),
);
self.last_pos = end;
@ -139,26 +138,22 @@ impl<'a> FmtVisitor<'a> {
// Get a snippet from the file start to the span's hi without allocating.
// We need it to determine what precedes the current comment. If the comment
// follows code on the same line, we won't touch it.
let big_span_lo = self.source_map.lookup_char_pos(span.lo()).file.start_pos;
let local_begin = self.source_map.lookup_byte_offset(big_span_lo);
let local_end = self.source_map.lookup_byte_offset(span.hi());
let start_index = local_begin.pos.to_usize();
let end_index = local_end.pos.to_usize();
let big_snippet = &local_begin.sf.src.as_ref().unwrap()[start_index..end_index];
let big_span_lo = self.snippet_provider.start_pos();
let big_snippet = self.snippet_provider.entire_snippet();
let big_diff = (span.lo() - big_span_lo).to_usize();
let snippet = self.snippet(span);
debug!("write_snippet `{}`", snippet);
self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet);
self.write_snippet_inner(big_snippet, snippet, big_diff, span, process_last_snippet);
}
fn write_snippet_inner<F>(
&mut self,
big_snippet: &str,
big_diff: usize,
old_snippet: &str,
big_diff: usize,
span: Span,
process_last_snippet: F,
) where
@ -167,9 +162,9 @@ impl<'a> FmtVisitor<'a> {
// Trim whitespace from the right hand side of each line.
// Annoyingly, the library functions for splitting by lines etc. are not
// quite right, so we must do it ourselves.
let char_pos = self.source_map.lookup_char_pos(span.lo());
let file_name = &char_pos.file.name.clone().into();
let mut status = SnippetStatus::new(char_pos.line);
let line = self.parse_sess.line_of_byte_pos(span.lo());
let file_name = &self.parse_sess.span_to_filename(span);
let mut status = SnippetStatus::new(line);
let snippet = &*transform_missing_snippet(self.config, old_snippet);

View file

@ -2,24 +2,25 @@ use std::borrow::Cow;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use rustc_errors::PResult;
use rustc_parse::{new_sub_parser_from_file, parser, DirectoryOwnership};
use rustc_session::parse::ParseSess;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{source_map, Span, DUMMY_SP};
use syntax::ast;
use syntax::token::TokenKind;
use syntax::visit::Visitor;
use crate::attr::MetaVisitor;
use crate::config::FileName;
use crate::items::is_mod_decl;
use crate::syntux::parser::{Directory, DirectoryOwnership, ModulePathSuccess, Parser};
use crate::syntux::session::ParseSess;
use crate::utils::contains_skip;
mod visitor;
type FileModMap<'ast> = BTreeMap<FileName, Cow<'ast, ast::Mod>>;
lazy_static! {
static ref CFG_IF: Symbol = Symbol::intern("cfg_if");
}
/// Maps each module to the corresponding file.
pub(crate) struct ModResolver<'ast, 'sess> {
parse_sess: &'sess ParseSess,
@ -28,21 +29,6 @@ pub(crate) struct ModResolver<'ast, 'sess> {
recursive: bool,
}
#[derive(Clone)]
struct Directory {
path: PathBuf,
ownership: DirectoryOwnership,
}
impl<'a> Directory {
fn to_syntax_directory(&'a self) -> rustc_parse::Directory {
rustc_parse::Directory {
path: self.path.clone(),
ownership: self.ownership.clone(),
}
}
}
#[derive(Clone)]
enum SubModKind<'a, 'ast> {
/// `mod foo;`
@ -78,12 +64,9 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
mut self,
krate: &'ast ast::Crate,
) -> Result<FileModMap<'ast>, String> {
let root_filename = self.parse_sess.source_map().span_to_filename(krate.span);
let root_filename = self.parse_sess.span_to_filename(krate.span);
self.directory.path = match root_filename {
source_map::FileName::Real(ref path) => path
.parent()
.expect("Parent directory should exists")
.to_path_buf(),
FileName::Real(ref p) => p.parent().unwrap_or(Path::new("")).to_path_buf(),
_ => PathBuf::new(),
};
@ -93,14 +76,13 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
}
self.file_map
.insert(root_filename.into(), Cow::Borrowed(&krate.module));
.insert(root_filename, Cow::Borrowed(&krate.module));
Ok(self.file_map)
}
/// Visit `cfg_if` macro and look for module declarations.
fn visit_cfg_if(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), String> {
let mut visitor =
visitor::CfgIfVisitor::new(self.parse_sess, self.directory.to_syntax_directory());
let mut visitor = visitor::CfgIfVisitor::new(self.parse_sess, &self.directory);
visitor.visit_item(&item);
for module_item in visitor.mods() {
if let ast::ItemKind::Mod(ref sub_mod) = module_item.item.kind {
@ -258,7 +240,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
attrs: &[ast::Attribute],
sub_mod: &Cow<'ast, ast::Mod>,
) -> Result<SubModKind<'c, 'ast>, String> {
if let Some(path) = parser::Parser::submod_path_from_attr(attrs, &self.directory.path) {
if let Some(path) = Parser::submod_path_from_attr(attrs, &self.directory.path) {
return Ok(SubModKind::External(
path,
DirectoryOwnership::Owned { relative: None },
@ -266,23 +248,18 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
}
// Look for nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`.
let mut mods_outside_ast = self
.find_mods_ouside_of_ast(attrs, sub_mod)
.unwrap_or(vec![]);
let mut mods_outside_ast = self.find_mods_outside_of_ast(attrs, sub_mod);
let relative = match self.directory.ownership {
DirectoryOwnership::Owned { relative } => relative,
DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
};
match parser::Parser::default_submod_path(
mod_name,
relative,
&self.directory.path,
self.parse_sess.source_map(),
)
.result
match self
.parse_sess
.default_submod_path(mod_name, relative, &self.directory.path)
.result
{
Ok(parser::ModulePathSuccess {
Ok(ModulePathSuccess {
path,
directory_ownership,
..
@ -323,21 +300,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
}
}
fn find_mods_ouside_of_ast(
&self,
attrs: &[ast::Attribute],
sub_mod: &Cow<'ast, ast::Mod>,
) -> Option<Vec<(PathBuf, DirectoryOwnership, Cow<'ast, ast::Mod>)>> {
use std::panic::{catch_unwind, AssertUnwindSafe};
Some(
catch_unwind(AssertUnwindSafe(|| {
self.find_mods_ouside_of_ast_inner(attrs, sub_mod)
}))
.ok()?,
)
}
fn find_mods_ouside_of_ast_inner(
fn find_mods_outside_of_ast(
&self,
attrs: &[ast::Attribute],
sub_mod: &Cow<'ast, ast::Mod>,
@ -356,14 +319,8 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
if !actual_path.exists() {
continue;
}
let file_name = rustc_span::FileName::Real(actual_path.clone());
if self
.parse_sess
.source_map()
.get_source_file(&file_name)
.is_some()
{
// If the specfied file is already parsed, then we just use that.
if self.parse_sess.is_file_parsed(&actual_path) {
// If the specified file is already parsed, then we just use that.
result.push((
actual_path,
DirectoryOwnership::Owned { relative: None },
@ -371,32 +328,15 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
));
continue;
}
let mut parser = new_sub_parser_from_file(
let m = match Parser::parse_file_as_module(
self.directory.ownership,
self.parse_sess,
&actual_path,
self.directory.ownership,
None,
DUMMY_SP,
);
parser.cfg_mods = false;
let lo = parser.token.span;
// FIXME(topecongiro) Format inner attributes (#3606).
let _mod_attrs = match parse_inner_attributes(&mut parser) {
Ok(attrs) => attrs,
Err(mut e) => {
e.cancel();
parser.sess.span_diagnostic.reset_err_count();
continue;
}
};
let m = match parse_mod_items(&mut parser, lo) {
Ok(m) => m,
Err(mut e) => {
e.cancel();
parser.sess.span_diagnostic.reset_err_count();
continue;
}
) {
Some(m) => m,
None => continue,
};
result.push((
actual_path,
DirectoryOwnership::Owned { relative: None },
@ -422,67 +362,11 @@ fn find_path_value(attrs: &[ast::Attribute]) -> Option<Symbol> {
attrs.iter().flat_map(path_value).next()
}
// FIXME(topecongiro) Use the method from libsyntax[1] once it become public.
//
// [1] https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/attr.rs
fn parse_inner_attributes<'a>(parser: &mut parser::Parser<'a>) -> PResult<'a, Vec<ast::Attribute>> {
let mut attrs: Vec<ast::Attribute> = vec![];
loop {
match parser.token.kind {
TokenKind::Pound => {
// Don't even try to parse if it's not an inner attribute.
if !parser.look_ahead(1, |t| t == &TokenKind::Not) {
break;
}
let attr = parser.parse_attribute(true)?;
assert_eq!(attr.style, ast::AttrStyle::Inner);
attrs.push(attr);
}
TokenKind::DocComment(s) => {
// we need to get the position of this token before we bump.
let attr = syntax::attr::mk_doc_comment(
syntax::util::comments::doc_comment_style(&s.as_str()),
s,
parser.token.span,
);
if attr.style == ast::AttrStyle::Inner {
attrs.push(attr);
parser.bump();
} else {
break;
}
}
_ => break,
}
}
Ok(attrs)
}
fn parse_mod_items<'a>(parser: &mut parser::Parser<'a>, inner_lo: Span) -> PResult<'a, ast::Mod> {
let mut items = vec![];
while let Some(item) = parser.parse_item()? {
items.push(item);
}
let hi = if parser.token.span.is_dummy() {
inner_lo
} else {
parser.prev_token.span
};
Ok(ast::Mod {
inner: inner_lo.to(hi),
items,
inline: false,
})
}
fn is_cfg_if(item: &ast::Item) -> bool {
match item.kind {
ast::ItemKind::Mac(ref mac) => {
if let Some(first_segment) = mac.path.segments.first() {
if first_segment.ident.name == Symbol::intern("cfg_if") {
if first_segment.ident.name == *CFG_IF {
return true;
}
}

View file

@ -1,11 +1,10 @@
use rustc_parse::{stream_to_parser_with_base_dir, Directory};
use rustc_session::parse::ParseSess;
use rustc_span::{symbol::kw, Symbol};
use rustc_span::Symbol;
use syntax::ast;
use syntax::token::{DelimToken, TokenKind};
use syntax::visit::Visitor;
use crate::attr::MetaVisitor;
use crate::syntux::parser::{Directory, Parser};
use crate::syntux::session::ParseSess;
pub(crate) struct ModItem {
pub(crate) item: ast::Item,
@ -15,11 +14,11 @@ pub(crate) struct ModItem {
pub(crate) struct CfgIfVisitor<'a> {
parse_sess: &'a ParseSess,
mods: Vec<ModItem>,
base_dir: Directory,
base_dir: &'a Directory,
}
impl<'a> CfgIfVisitor<'a> {
pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: Directory) -> CfgIfVisitor<'a> {
pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: &'a Directory) -> CfgIfVisitor<'a> {
CfgIfVisitor {
mods: vec![],
parse_sess,
@ -65,59 +64,9 @@ impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
}
};
let ts = mac.args.inner_tokens();
let mut parser =
stream_to_parser_with_base_dir(self.parse_sess, ts.clone(), self.base_dir.clone());
parser.cfg_mods = false;
let mut process_if_cfg = true;
while parser.token.kind != TokenKind::Eof {
if process_if_cfg {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}
parser
.parse_attribute(false)
.map_err(|_| "Failed to parse attributes")?;
}
if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
return Err("Expected an opening brace");
}
while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
&& parser.token.kind != TokenKind::Eof
{
let item = match parser.parse_item() {
Ok(Some(item_ptr)) => item_ptr.into_inner(),
Ok(None) => continue,
Err(mut err) => {
err.cancel();
parser.sess.span_diagnostic.reset_err_count();
return Err(
"Expected item inside cfg_if block, but failed to parse it as an item",
);
}
};
if let ast::ItemKind::Mod(..) = item.kind {
self.mods.push(ModItem { item });
}
}
if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
return Err("Expected a closing brace");
}
if parser.eat(&TokenKind::Eof) {
break;
}
if !parser.eat_keyword(kw::Else) {
return Err("Expected `else`");
}
process_if_cfg = parser.token.is_keyword(kw::If);
}
let items = Parser::parse_cfg_if(self.parse_sess, mac, &self.base_dir)?;
self.mods
.append(&mut items.into_iter().map(|item| ModItem { item }).collect());
Ok(())
}

View file

@ -207,13 +207,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
item_kind: ReorderableItemKind,
in_group: bool,
) -> usize {
let mut last = self.source_map.lookup_line_range(items[0].span());
let mut last = self.parse_sess.lookup_line_range(items[0].span());
let item_length = items
.iter()
.take_while(|ppi| {
item_kind.is_same_item_kind(&***ppi)
&& (!in_group || {
let current = self.source_map.lookup_line_range(ppi.span());
let current = self.parse_sess.lookup_line_range(ppi.span());
let in_same_group = current.lo < last.hi + 2;
last = current;
in_same_group

View file

@ -3,13 +3,13 @@
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use rustc_session::parse::ParseSess;
use rustc_span::{source_map::SourceMap, Span};
use rustc_span::Span;
use syntax::ptr;
use crate::config::{Config, IndentStyle};
use crate::shape::Shape;
use crate::skip::SkipContext;
use crate::syntux::session::ParseSess;
use crate::visitor::SnippetProvider;
use crate::FormatReport;
@ -26,8 +26,7 @@ impl<T: Rewrite> Rewrite for ptr::P<T> {
#[derive(Clone)]
pub(crate) struct RewriteContext<'a> {
pub(crate) parse_session: &'a ParseSess,
pub(crate) source_map: &'a SourceMap,
pub(crate) parse_sess: &'a ParseSess,
pub(crate) config: &'a Config,
pub(crate) inside_macro: Rc<Cell<bool>>,
// Force block indent style even if we are using visual indent style.
@ -37,7 +36,7 @@ pub(crate) struct RewriteContext<'a> {
pub(crate) is_if_else_block: Cell<bool>,
// When rewriting chain, veto going multi line except the last element
pub(crate) force_one_line_chain: Cell<bool>,
pub(crate) snippet_provider: &'a SnippetProvider<'a>,
pub(crate) snippet_provider: &'a SnippetProvider,
// Used for `format_snippet`
pub(crate) macro_rewrite_failure: Cell<bool>,
pub(crate) report: FormatReport,

View file

@ -2,10 +2,9 @@ use std::fs;
use std::io::{self, Write};
use std::path::Path;
use rustc_span::source_map::SourceMap;
use crate::config::FileName;
use crate::emitter::{self, Emitter};
use crate::syntux::session::ParseSess;
use crate::NewlineStyle;
#[cfg(test)]
@ -14,6 +13,7 @@ use crate::config::Config;
use crate::create_emitter;
#[cfg(test)]
use crate::formatting::FileRecord;
use std::rc::Rc;
// Append a newline to the end of each file.
pub(crate) fn append_newline(s: &mut String) {
@ -48,7 +48,7 @@ where
}
pub(crate) fn write_file<T>(
source_map: Option<&SourceMap>,
parse_sess: Option<&ParseSess>,
filename: &FileName,
formatted_text: &str,
out: &mut T,
@ -84,20 +84,17 @@ where
// source map instead of hitting the file system. This also supports getting
// original text for `FileName::Stdin`.
let original_text = if newline_style != NewlineStyle::Auto && *filename != FileName::Stdin {
fs::read_to_string(ensure_real_path(filename))?
Rc::new(fs::read_to_string(ensure_real_path(filename))?)
} else {
match source_map
.and_then(|x| x.get_source_file(&filename.into()))
.and_then(|x| x.src.as_ref().map(ToString::to_string))
{
match parse_sess.and_then(|sess| sess.get_original_snippet(filename)) {
Some(ori) => ori,
None => fs::read_to_string(ensure_real_path(filename))?,
None => Rc::new(fs::read_to_string(ensure_real_path(filename))?),
}
};
let formatted_file = emitter::FormattedFile {
filename,
original_text: &original_text,
original_text: original_text.as_str(),
formatted_text,
};

View file

@ -1,11 +1,10 @@
//! This module contains utilities that work with the `SourceMap` from `libsyntax`/`syntex_syntax`.
//! This includes extension traits and methods for looking up spans and line ranges for AST nodes.
use rustc_span::{source_map::SourceMap, BytePos, Span};
use rustc_span::{BytePos, Span};
use crate::comment::FindUncommented;
use crate::config::file_lines::LineRange;
use crate::utils::starts_with_newline;
use crate::visitor::SnippetProvider;
pub(crate) trait SpanUtils {
@ -26,7 +25,7 @@ pub(crate) trait LineRangeUtils {
fn lookup_line_range(&self, span: Span) -> LineRange;
}
impl<'a> SpanUtils for SnippetProvider<'a> {
impl SpanUtils for SnippetProvider {
fn span_after(&self, original: Span, needle: &str) -> BytePos {
self.opt_span_after(original, needle).unwrap_or_else(|| {
panic!(
@ -81,27 +80,3 @@ impl<'a> SpanUtils for SnippetProvider<'a> {
Some(original.lo() + BytePos(offset as u32))
}
}
impl LineRangeUtils for SourceMap {
fn lookup_line_range(&self, span: Span) -> LineRange {
let snippet = self.span_to_snippet(span).unwrap_or_default();
let lo = self.lookup_line(span.lo()).unwrap();
let hi = self.lookup_line(span.hi()).unwrap();
debug_assert_eq!(
lo.sf.name, hi.sf.name,
"span crossed file boundary: lo: {:?}, hi: {:?}",
lo, hi
);
// in case the span starts with a newline, the line range is off by 1 without the
// adjustment below
let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
// Line numbers start at 1
LineRange {
file: lo.sf.clone(),
lo: lo.line + offset,
hi: hi.line + offset,
}
}
}

4
src/syntux.rs Normal file
View file

@ -0,0 +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;

348
src/syntux/parser.rs Normal file
View file

@ -0,0 +1,348 @@
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::{Path, PathBuf};
use rustc_errors::{Diagnostic, PResult};
use rustc_parse::{new_sub_parser_from_file, parser::Parser as RawParser};
use rustc_span::{symbol::kw, Span, DUMMY_SP};
use syntax::ast;
use syntax::token::{DelimToken, TokenKind};
use crate::syntux::session::ParseSess;
use crate::{Config, Input};
pub(crate) type DirectoryOwnership = rustc_parse::DirectoryOwnership;
pub(crate) type ModulePathSuccess = rustc_parse::parser::ModulePathSuccess;
#[derive(Clone)]
pub(crate) struct Directory {
pub(crate) path: PathBuf,
pub(crate) ownership: DirectoryOwnership,
}
impl<'a> Directory {
fn to_syntax_directory(&'a self) -> rustc_parse::Directory {
rustc_parse::Directory {
path: self.path.clone(),
ownership: self.ownership,
}
}
}
/// A parser for Rust source code.
pub(crate) struct Parser<'a> {
parser: RawParser<'a>,
sess: &'a ParseSess,
}
/// A builder for the `Parser`.
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
config: Option<&'a Config>,
sess: Option<&'a ParseSess>,
input: Option<Input>,
directory_ownership: Option<DirectoryOwnership>,
}
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(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);
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 rustc_session::parse::ParseSess,
input: Input,
directory_ownership: Option<DirectoryOwnership>,
) -> Result<rustc_parse::parser::Parser<'a>, Vec<Diagnostic>> {
match input {
Input::File(ref file) => Ok(if let Some(directory_ownership) = directory_ownership {
rustc_parse::new_sub_parser_from_file(
sess,
file,
directory_ownership,
None,
DUMMY_SP,
)
} else {
rustc_parse::new_parser_from_file(sess, file)
}),
Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
sess,
rustc_span::FileName::Custom("stdin".to_owned()),
text,
)
.map(|mut parser| {
parser.recurse_into_file_modules = false;
parser
}),
}
}
}
#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
NoConfig,
NoParseSess,
NoInput,
ParserCreationError,
ParseError,
ParsePanicError,
}
impl<'a> Parser<'a> {
pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
rustc_parse::parser::Parser::submod_path_from_attr(attrs, path)
}
// FIXME(topecongiro) Use the method from libsyntax[1] once it become public.
//
// [1] https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/attr.rs
fn parse_inner_attrs(parser: &mut RawParser<'a>) -> PResult<'a, Vec<ast::Attribute>> {
let mut attrs: Vec<ast::Attribute> = vec![];
loop {
match parser.token.kind {
TokenKind::Pound => {
// Don't even try to parse if it's not an inner attribute.
if !parser.look_ahead(1, |t| t == &TokenKind::Not) {
break;
}
let attr = parser.parse_attribute(true)?;
assert_eq!(attr.style, ast::AttrStyle::Inner);
attrs.push(attr);
}
TokenKind::DocComment(s) => {
// we need to get the position of this token before we bump.
let attr = syntax::attr::mk_doc_comment(
syntax::util::comments::doc_comment_style(&s.as_str()),
s,
parser.token.span,
);
if attr.style == ast::AttrStyle::Inner {
attrs.push(attr);
parser.bump();
} else {
break;
}
}
_ => break,
}
}
Ok(attrs)
}
fn parse_mod_items(parser: &mut RawParser<'a>, span: Span) -> PResult<'a, ast::Mod> {
let mut items = vec![];
while let Some(item) = parser.parse_item()? {
items.push(item);
}
let hi = if parser.token.span.is_dummy() {
span
} else {
parser.prev_token.span
};
Ok(ast::Mod {
inner: span.to(hi),
items,
inline: false,
})
}
pub(crate) fn parse_file_as_module(
directory_ownership: DirectoryOwnership,
sess: &'a ParseSess,
path: &Path,
) -> Option<ast::Mod> {
let result = catch_unwind(AssertUnwindSafe(|| {
let mut parser =
new_sub_parser_from_file(sess.inner(), &path, directory_ownership, None, DUMMY_SP);
parser.cfg_mods = false;
let lo = parser.token.span;
// FIXME(topecongiro) Format inner attributes (#3606).
match Parser::parse_inner_attrs(&mut parser) {
Ok(_attrs) => (),
Err(mut e) => {
e.cancel();
sess.reset_errors();
return None;
}
}
match Parser::parse_mod_items(&mut parser, lo) {
Ok(m) => Some(m),
Err(mut db) => {
db.cancel();
sess.reset_errors();
None
}
}
}));
match result {
Ok(Some(m)) => Some(m),
_ => None,
}
}
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),
}
}
pub(crate) fn parse_cfg_if(
sess: &'a ParseSess,
mac: &'a ast::Mac,
base_dir: &Directory,
) -> Result<Vec<ast::Item>, &'static str> {
match catch_unwind(AssertUnwindSafe(|| {
Parser::parse_cfg_if_inner(sess, mac, base_dir)
})) {
Ok(Ok(items)) => Ok(items),
Ok(err @ Err(_)) => err,
Err(..) => Err("failed to parse cfg_if!"),
}
}
fn parse_cfg_if_inner(
sess: &'a ParseSess,
mac: &'a ast::Mac,
base_dir: &Directory,
) -> Result<Vec<ast::Item>, &'static str> {
let token_stream = mac.args.inner_tokens();
let mut parser = rustc_parse::stream_to_parser_with_base_dir(
sess.inner(),
token_stream.clone(),
base_dir.to_syntax_directory(),
);
parser.cfg_mods = false;
let mut items = vec![];
let mut process_if_cfg = true;
while parser.token.kind != TokenKind::Eof {
if process_if_cfg {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}
parser
.parse_attribute(false)
.map_err(|_| "Failed to parse attributes")?;
}
if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
return Err("Expected an opening brace");
}
while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
&& parser.token.kind != TokenKind::Eof
{
let item = match parser.parse_item() {
Ok(Some(item_ptr)) => item_ptr.into_inner(),
Ok(None) => continue,
Err(mut err) => {
err.cancel();
parser.sess.span_diagnostic.reset_err_count();
return Err(
"Expected item inside cfg_if block, but failed to parse it as an item",
);
}
};
if let ast::ItemKind::Mod(..) = item.kind {
items.push(item);
}
}
if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
return Err("Expected a closing brace");
}
if parser.eat(&TokenKind::Eof) {
break;
}
if !parser.eat_keyword(kw::Else) {
return Err("Expected `else`");
}
process_if_cfg = parser.token.is_keyword(kw::If);
}
Ok(items)
}
}

440
src/syntux/session.rs Normal file
View file

@ -0,0 +1,440 @@
use std::cell::RefCell;
use std::path::Path;
use std::rc::Rc;
use rustc_data_structures::sync::{Lrc, Send};
use rustc_errors::emitter::{Emitter, EmitterWriter};
use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel};
use rustc_session::parse::ParseSess as RawParseSess;
use rustc_span::{
source_map::{FilePathMapping, SourceMap},
BytePos, Span,
};
use syntax::ast;
use crate::config::file_lines::LineRange;
use crate::ignore_path::IgnorePathSet;
use crate::source_map::LineRangeUtils;
use crate::utils::starts_with_newline;
use crate::visitor::SnippetProvider;
use crate::{Config, ErrorKind, FileName};
/// ParseSess holds structs necessary for constructing a parser.
pub(crate) struct ParseSess {
parse_sess: RawParseSess,
ignore_path_set: Rc<IgnorePathSet>,
can_reset_errors: Rc<RefCell<bool>>,
}
/// Emitter which discards every error.
struct SilentEmitter;
impl Emitter for SilentEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, _db: &Diagnostic) {}
}
fn silent_emitter() -> Box<dyn Emitter + Send> {
Box::new(SilentEmitter {})
}
/// Emit errors against every files expect ones specified in the `ignore_path_set`.
struct SilentOnIgnoredFilesEmitter {
ignore_path_set: Rc<IgnorePathSet>,
source_map: Rc<SourceMap>,
emitter: Box<dyn Emitter + Send>,
has_non_ignorable_parser_errors: bool,
can_reset: Rc<RefCell<bool>>,
}
impl SilentOnIgnoredFilesEmitter {
fn handle_non_ignoreable_error(&mut self, db: &Diagnostic) {
self.has_non_ignorable_parser_errors = true;
*self.can_reset.borrow_mut() = false;
self.emitter.emit_diagnostic(db);
}
}
impl Emitter for SilentOnIgnoredFilesEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, db: &Diagnostic) {
if db.level == DiagnosticLevel::Fatal {
return self.handle_non_ignoreable_error(db);
}
if let Some(primary_span) = &db.span.primary_span() {
let file_name = self.source_map.span_to_filename(*primary_span);
if let rustc_span::FileName::Real(ref path) = file_name {
if self
.ignore_path_set
.is_match(&FileName::Real(path.to_path_buf()))
{
if !self.has_non_ignorable_parser_errors {
*self.can_reset.borrow_mut() = true;
}
return;
}
};
}
self.handle_non_ignoreable_error(db);
}
}
fn default_handler(
source_map: Rc<SourceMap>,
ignore_path_set: Rc<IgnorePathSet>,
can_reset: Rc<RefCell<bool>>,
hide_parse_errors: bool,
) -> Handler {
let supports_color = term::stderr().map_or(false, |term| term.supports_color());
let color_cfg = if supports_color {
ColorConfig::Auto
} else {
ColorConfig::Never
};
let emitter = if hide_parse_errors {
silent_emitter()
} else {
Box::new(EmitterWriter::stderr(
color_cfg,
Some(source_map.clone()),
false,
false,
None,
false,
))
};
Handler::with_emitter(
true,
None,
Box::new(SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false,
source_map,
emitter,
ignore_path_set,
can_reset,
}),
)
}
impl ParseSess {
pub(crate) fn new(config: &Config) -> Result<ParseSess, ErrorKind> {
let ignore_path_set = match IgnorePathSet::from_ignore_list(&config.ignore()) {
Ok(ignore_path_set) => Rc::new(ignore_path_set),
Err(e) => return Err(ErrorKind::InvalidGlobPattern(e)),
};
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let can_reset_errors = Rc::new(RefCell::new(false));
let handler = default_handler(
Rc::clone(&source_map),
Rc::clone(&ignore_path_set),
Rc::clone(&can_reset_errors),
config.hide_parse_errors(),
);
let parse_sess = RawParseSess::with_span_handler(handler, source_map);
Ok(ParseSess {
parse_sess,
ignore_path_set,
can_reset_errors,
})
}
pub(crate) fn default_submod_path(
&self,
id: ast::Ident,
relative: Option<ast::Ident>,
dir_path: &Path,
) -> rustc_parse::parser::ModulePath {
rustc_parse::parser::Parser::default_submod_path(
id,
relative,
dir_path,
self.parse_sess.source_map(),
)
}
pub(crate) fn is_file_parsed(&self, path: &Path) -> bool {
self.parse_sess
.source_map()
.get_source_file(&rustc_span::FileName::Real(path.to_path_buf()))
.is_some()
}
pub(crate) fn ignore_file(&self, path: &FileName) -> bool {
self.ignore_path_set.as_ref().is_match(&path)
}
pub(crate) fn set_silent_emitter(&mut self) {
self.parse_sess.span_diagnostic = Handler::with_emitter(true, None, silent_emitter());
}
pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
self.parse_sess.source_map().span_to_filename(span).into()
}
pub(crate) fn span_to_first_line_string(&self, span: Span) -> String {
let file_lines = self.parse_sess.source_map().span_to_lines(span).ok();
match file_lines {
Some(fl) => fl
.file
.get_line(fl.lines[0].line_index)
.map_or_else(String::new, |s| s.to_string()),
None => String::new(),
}
}
pub(crate) fn line_of_byte_pos(&self, pos: BytePos) -> usize {
self.parse_sess.source_map().lookup_char_pos(pos).line
}
pub(crate) fn span_to_debug_info(&self, span: Span) -> String {
self.parse_sess.source_map().span_to_string(span)
}
pub(crate) fn inner(&self) -> &RawParseSess {
&self.parse_sess
}
pub(crate) fn snippet_provider(&self, span: Span) -> SnippetProvider {
let source_file = self.parse_sess.source_map().lookup_char_pos(span.lo()).file;
SnippetProvider::new(
source_file.start_pos,
source_file.end_pos,
Rc::clone(source_file.src.as_ref().unwrap()),
)
}
pub(crate) fn get_original_snippet(&self, file_name: &FileName) -> Option<Rc<String>> {
self.parse_sess
.source_map()
.get_source_file(&file_name.into())
.and_then(|source_file| source_file.src.clone())
}
}
// Methods that should be restricted within the syntux module.
impl ParseSess {
pub(super) fn emit_diagnostics(&self, diagnostics: Vec<Diagnostic>) {
for diagnostic in diagnostics {
self.parse_sess.span_diagnostic.emit_diagnostic(&diagnostic);
}
}
pub(super) fn can_reset_errors(&self) -> bool {
*self.can_reset_errors.borrow()
}
pub(super) fn has_errors(&self) -> bool {
self.parse_sess.span_diagnostic.has_errors()
}
pub(super) fn reset_errors(&self) {
self.parse_sess.span_diagnostic.reset_err_count();
}
}
impl LineRangeUtils for ParseSess {
fn lookup_line_range(&self, span: Span) -> LineRange {
let snippet = self
.parse_sess
.source_map()
.span_to_snippet(span)
.unwrap_or_default();
let lo = self.parse_sess.source_map().lookup_line(span.lo()).unwrap();
let hi = self.parse_sess.source_map().lookup_line(span.hi()).unwrap();
debug_assert_eq!(
lo.sf.name, hi.sf.name,
"span crossed file boundary: lo: {:?}, hi: {:?}",
lo, hi
);
// in case the span starts with a newline, the line range is off by 1 without the
// adjustment below
let offset = 1 + if starts_with_newline(&snippet) { 1 } else { 0 };
// Line numbers start at 1
LineRange {
file: lo.sf.clone(),
lo: lo.line + offset,
hi: hi.line + offset,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
mod emitter {
use super::*;
use crate::config::IgnoreList;
use crate::is_nightly_channel;
use crate::utils::mk_sp;
use rustc_span::{FileName as SourceMapFileName, MultiSpan, DUMMY_SP};
use std::path::PathBuf;
struct TestEmitter {
num_emitted_errors: Rc<RefCell<u32>>,
}
impl Emitter for TestEmitter {
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
None
}
fn emit_diagnostic(&mut self, _db: &Diagnostic) {
*self.num_emitted_errors.borrow_mut() += 1;
}
}
fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
Diagnostic {
level,
code: None,
message: vec![],
children: vec![],
suggestions: vec![],
span: span.unwrap_or_else(MultiSpan::new),
sort_span: DUMMY_SP,
}
}
fn build_emitter(
num_emitted_errors: Rc<RefCell<u32>>,
can_reset: Rc<RefCell<bool>>,
source_map: Option<Rc<SourceMap>>,
ignore_list: Option<IgnoreList>,
) -> SilentOnIgnoredFilesEmitter {
let emitter_writer = TestEmitter { num_emitted_errors };
let source_map =
source_map.unwrap_or_else(|| Rc::new(SourceMap::new(FilePathMapping::empty())));
let ignore_path_set =
Rc::new(IgnorePathSet::from_ignore_list(&ignore_list.unwrap_or_default()).unwrap());
SilentOnIgnoredFilesEmitter {
has_non_ignorable_parser_errors: false,
source_map,
emitter: Box::new(emitter_writer),
ignore_path_set,
can_reset,
}
}
fn get_ignore_list(config: &str) -> IgnoreList {
Config::from_toml(config, Path::new("")).unwrap().ignore()
}
#[test]
fn handles_fatal_parse_error_in_ignored_file() {
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
emitter.emit_diagnostic(&fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 1);
assert_eq!(*can_reset_errors.borrow(), false);
}
#[test]
fn handles_recoverable_parse_error_in_ignored_file() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
emitter.emit_diagnostic(&non_fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 0);
assert_eq!(*can_reset_errors.borrow(), true);
}
#[test]
fn handles_recoverable_parse_error_in_non_ignored_file() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let source = String::from(r#"pub fn bar() { 1x; }"#);
source_map.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), source);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
None,
);
let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
emitter.emit_diagnostic(&non_fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 1);
assert_eq!(*can_reset_errors.borrow(), false);
}
#[test]
fn handles_mix_of_recoverable_parse_error() {
if !is_nightly_channel!() {
return;
}
let num_emitted_errors = Rc::new(RefCell::new(0));
let can_reset_errors = Rc::new(RefCell::new(false));
let source_map = Rc::new(SourceMap::new(FilePathMapping::empty()));
let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#);
let bar_source = String::from(r#"pub fn bar() { 1x; }"#);
let foo_source = String::from(r#"pub fn foo() { 1x; }"#);
let fatal_source =
String::from(r#"extern "system" fn jni_symbol!( funcName ) ( ... ) -> {} "#);
source_map
.new_source_file(SourceMapFileName::Real(PathBuf::from("bar.rs")), bar_source);
source_map
.new_source_file(SourceMapFileName::Real(PathBuf::from("foo.rs")), foo_source);
source_map.new_source_file(
SourceMapFileName::Real(PathBuf::from("fatal.rs")),
fatal_source,
);
let mut emitter = build_emitter(
Rc::clone(&num_emitted_errors),
Rc::clone(&can_reset_errors),
Some(Rc::clone(&source_map)),
Some(ignore_list),
);
let bar_span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
let foo_span = MultiSpan::from_span(mk_sp(BytePos(21), BytePos(22)));
let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
emitter.emit_diagnostic(&bar_diagnostic);
emitter.emit_diagnostic(&foo_diagnostic);
emitter.emit_diagnostic(&fatal_diagnostic);
assert_eq!(*num_emitted_errors.borrow(), 2);
assert_eq!(*can_reset_errors.borrow(), false);
}
}
}

View file

@ -355,7 +355,7 @@ macro_rules! out_of_file_lines_range {
&& !$self
.config
.file_lines()
.intersects(&$self.source_map.lookup_line_range($span))
.intersects(&$self.parse_sess.lookup_line_range($span))
};
}

View file

@ -1,11 +1,7 @@
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use rustc_session::parse::ParseSess;
use rustc_span::{
source_map::{self, SourceMap},
BytePos, Pos, Span,
};
use rustc_span::{BytePos, Pos, Span};
use syntax::token::DelimToken;
use syntax::{ast, visit};
@ -27,6 +23,7 @@ use crate::skip::{is_skip_attr, SkipContext};
use crate::source_map::{LineRangeUtils, SpanUtils};
use crate::spanned::Spanned;
use crate::stmt::Stmt;
use crate::syntux::session::ParseSess;
use crate::utils::{
self, contains_skip, count_newlines, depr_skip_annotation, inner_attributes, last_line_width,
mk_sp, ptr_vec_to_ref_vec, rewrite_ident, stmt_expr,
@ -34,23 +31,23 @@ use crate::utils::{
use crate::{ErrorKind, FormatReport, FormattingError};
/// Creates a string slice corresponding to the specified span.
pub(crate) struct SnippetProvider<'a> {
pub(crate) struct SnippetProvider {
/// A pointer to the content of the file we are formatting.
big_snippet: &'a str,
big_snippet: Rc<String>,
/// A position of the start of `big_snippet`, used as an offset.
start_pos: usize,
/// A end position of the file that this snippet lives.
end_pos: usize,
}
impl<'a> SnippetProvider<'a> {
impl SnippetProvider {
pub(crate) fn span_to_snippet(&self, span: Span) -> Option<&str> {
let start_index = span.lo().to_usize().checked_sub(self.start_pos)?;
let end_index = span.hi().to_usize().checked_sub(self.start_pos)?;
Some(&self.big_snippet[start_index..end_index])
}
pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: &'a str) -> Self {
pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Rc<String>) -> Self {
let start_pos = start_pos.to_usize();
let end_pos = end_pos.to_usize();
SnippetProvider {
@ -60,6 +57,14 @@ impl<'a> SnippetProvider<'a> {
}
}
pub(crate) fn entire_snippet(&self) -> &str {
self.big_snippet.as_str()
}
pub(crate) fn start_pos(&self) -> BytePos {
BytePos::from_usize(self.start_pos)
}
pub(crate) fn end_pos(&self) -> BytePos {
BytePos::from_usize(self.end_pos)
}
@ -67,15 +72,14 @@ impl<'a> SnippetProvider<'a> {
pub(crate) struct FmtVisitor<'a> {
parent_context: Option<&'a RewriteContext<'a>>,
pub(crate) parse_session: &'a ParseSess,
pub(crate) source_map: &'a SourceMap,
pub(crate) parse_sess: &'a ParseSess,
pub(crate) buffer: String,
pub(crate) last_pos: BytePos,
// FIXME: use an RAII util or closure for indenting
pub(crate) block_indent: Indent,
pub(crate) config: &'a Config,
pub(crate) is_if_else_block: bool,
pub(crate) snippet_provider: &'a SnippetProvider<'a>,
pub(crate) snippet_provider: &'a SnippetProvider,
pub(crate) line_number: usize,
/// List of 1-based line ranges which were annotated with skip
/// Both bounds are inclusifs.
@ -110,10 +114,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
fn visit_stmt(&mut self, stmt: &Stmt<'_>) {
debug!(
"visit_stmt: {:?} {:?} `{}`",
self.source_map.lookup_char_pos(stmt.span().lo()),
self.source_map.lookup_char_pos(stmt.span().hi()),
self.snippet(stmt.span()),
"visit_stmt: {}",
self.parse_sess.span_to_debug_info(stmt.span())
);
// https://github.com/rust-lang/rust/issues/63679.
@ -201,9 +203,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
has_braces: bool,
) {
debug!(
"visit_block: {:?} {:?}",
self.source_map.lookup_char_pos(b.span.lo()),
self.source_map.lookup_char_pos(b.span.hi())
"visit_block: {}",
self.parse_sess.span_to_debug_info(b.span),
);
// Check if this block has braces.
@ -744,10 +745,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
// do not take into account the lines with attributes as part of the skipped range
let attrs_end = attrs
.iter()
.map(|attr| self.source_map.lookup_char_pos(attr.span.hi()).line)
.map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi()))
.max()
.unwrap_or(1);
let first_line = self.source_map.lookup_char_pos(main_span.lo()).line;
let first_line = self.parse_sess.line_of_byte_pos(main_span.lo());
// Statement can start after some newlines and/or spaces
// or it can be on the same line as the last attribute.
// So here we need to take a minimum between the two.
@ -758,8 +759,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
}
pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> {
let mut visitor = FmtVisitor::from_source_map(
ctx.parse_session,
let mut visitor = FmtVisitor::from_parse_sess(
ctx.parse_sess,
ctx.config,
ctx.snippet_provider,
ctx.report.clone(),
@ -769,16 +770,15 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
visitor
}
pub(crate) fn from_source_map(
pub(crate) fn from_parse_sess(
parse_session: &'a ParseSess,
config: &'a Config,
snippet_provider: &'a SnippetProvider<'_>,
snippet_provider: &'a SnippetProvider,
report: FormatReport,
) -> FmtVisitor<'a> {
FmtVisitor {
parent_context: None,
parse_session,
source_map: parse_session.source_map(),
parse_sess: parse_session,
buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2),
last_pos: BytePos(0),
block_indent: Indent::empty(),
@ -805,12 +805,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool {
for attr in attrs {
if attr.check_name(depr_skip_annotation()) {
let file_name = self.source_map.span_to_filename(attr.span).into();
let file_name = self.parse_sess.span_to_filename(attr.span);
self.report.append(
file_name,
vec![FormattingError::from_span(
attr.span,
&self.source_map,
self.parse_sess,
ErrorKind::DeprecatedAttr,
)],
);
@ -819,12 +819,12 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
ast::AttrKind::Normal(ref attribute_item)
if self.is_unknown_rustfmt_attr(&attribute_item.path.segments) =>
{
let file_name = self.source_map.span_to_filename(attr.span).into();
let file_name = self.parse_sess.span_to_filename(attr.span);
self.report.append(
file_name,
vec![FormattingError::from_span(
attr.span,
self.source_map,
self.parse_sess,
ErrorKind::BadAttr,
)],
);
@ -932,14 +932,10 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
}
}
pub(crate) fn format_separate_mod(
&mut self,
m: &ast::Mod,
source_file: &source_map::SourceFile,
) {
pub(crate) fn format_separate_mod(&mut self, m: &ast::Mod, end_pos: BytePos) {
self.block_indent = Indent::empty();
self.walk_mod_items(m);
self.format_missing_with_indent(source_file.end_pos);
self.format_missing_with_indent(end_pos);
}
pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) {
@ -970,8 +966,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
pub(crate) fn get_context(&self) -> RewriteContext<'_> {
RewriteContext {
parse_session: self.parse_session,
source_map: self.source_map,
parse_sess: self.parse_sess,
config: self.config,
inside_macro: Rc::new(Cell::new(false)),
use_block: Cell::new(false),