refactor: backport syntux mod
This commit is contained in:
parent
9699c96cf1
commit
bd5dff4012
22 changed files with 959 additions and 766 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)],
|
||||
);
|
||||
|
|
|
|||
27
src/expr.rs
27
src/expr.rs
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
11
src/lib.rs
11
src/lib.rs
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
170
src/modules.rs
170
src/modules.rs
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
4
src/syntux.rs
Normal 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
348
src/syntux/parser.rs
Normal 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
440
src/syntux/session.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue