mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-12 20:16:49 +00:00
Fix crash speculatively parsing macro arguments as expressions.
The problem is essentially that if we try to parse a token tree using a CodeMap different from the one the tree was originally parsed with, spans become nonsense. Since CodeMaps can't be cloned, we're basically forced to use the original ParseSess for additional parsing. Ideally, rustfmt would be a bit more clever and figure out how to parse macro arguments based on the definition of the macro itself, rather than just guessing that a particular token sequence looks like an expression, but this is good enough for now. Fixes #538.
This commit is contained in:
parent
100058f2de
commit
1c235de97d
@ -422,7 +422,7 @@ impl Rewrite for ast::Block {
|
||||
return Some(user_str);
|
||||
}
|
||||
|
||||
let mut visitor = FmtVisitor::from_codemap(context.codemap, context.config, None);
|
||||
let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config, None);
|
||||
visitor.block_indent = context.block_indent;
|
||||
|
||||
let prefix = match self.rules {
|
||||
@ -833,7 +833,9 @@ impl Rewrite for ast::Arm {
|
||||
let attr_str = if !attrs.is_empty() {
|
||||
// We only use this visitor for the attributes, should we use it for
|
||||
// more?
|
||||
let mut attr_visitor = FmtVisitor::from_codemap(context.codemap, context.config, None);
|
||||
let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session,
|
||||
context.config,
|
||||
None);
|
||||
attr_visitor.block_indent = context.block_indent;
|
||||
attr_visitor.last_pos = attrs[0].span.lo;
|
||||
if attr_visitor.visit_attrs(attrs) {
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -26,7 +26,8 @@ extern crate diff;
|
||||
extern crate term;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{CodeMap, Span};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::diagnostic::{EmitterWriter, Handler};
|
||||
use syntax::parse::{self, ParseSess};
|
||||
|
||||
use std::ops::{Add, Sub};
|
||||
@ -288,11 +289,15 @@ impl fmt::Display for FormatReport {
|
||||
}
|
||||
|
||||
// Formatting which depends on the AST.
|
||||
fn fmt_ast(krate: &ast::Crate, codemap: &CodeMap, config: &Config, mode: WriteMode) -> FileMap {
|
||||
fn fmt_ast(krate: &ast::Crate,
|
||||
parse_session: &ParseSess,
|
||||
config: &Config,
|
||||
mode: WriteMode)
|
||||
-> FileMap {
|
||||
let mut file_map = FileMap::new();
|
||||
for (path, module) in modules::list_files(krate, codemap) {
|
||||
for (path, module) in modules::list_files(krate, parse_session.codemap()) {
|
||||
let path = path.to_str().unwrap();
|
||||
let mut visitor = FmtVisitor::from_codemap(codemap, config, Some(mode));
|
||||
let mut visitor = FmtVisitor::from_codemap(parse_session, config, Some(mode));
|
||||
visitor.format_separate_mod(module, path);
|
||||
file_map.insert(path.to_owned(), visitor.buffer);
|
||||
}
|
||||
@ -382,9 +387,14 @@ pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
|
||||
}
|
||||
|
||||
pub fn format(file: &Path, config: &Config, mode: WriteMode) -> FileMap {
|
||||
let parse_session = ParseSess::new();
|
||||
let mut parse_session = ParseSess::new();
|
||||
let krate = parse::parse_crate_from_file(file, Vec::new(), &parse_session);
|
||||
let mut file_map = fmt_ast(&krate, parse_session.codemap(), config, mode);
|
||||
|
||||
// Suppress error output after parsing.
|
||||
let emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None));
|
||||
parse_session.span_diagnostic.handler = Handler::with_emitter(false, emitter);
|
||||
|
||||
let mut file_map = fmt_ast(&krate, &parse_session, config, mode);
|
||||
|
||||
// For some reason, the codemap does not include terminating
|
||||
// newlines so we must add one on for each file. This is sad.
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::parse::token::{Eof, Comma, Token};
|
||||
use syntax::parse::{ParseSess, tts_to_parser};
|
||||
use syntax::parse::tts_to_parser;
|
||||
use syntax::codemap::{mk_sp, BytePos};
|
||||
|
||||
use Indent;
|
||||
@ -73,8 +73,7 @@ pub fn rewrite_macro(mac: &ast::Mac,
|
||||
};
|
||||
}
|
||||
|
||||
let parse_session = ParseSess::new();
|
||||
let mut parser = tts_to_parser(&parse_session, mac.node.tts.clone(), Vec::new());
|
||||
let mut parser = tts_to_parser(context.parse_session, mac.node.tts.clone(), Vec::new());
|
||||
let mut expr_vec = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -11,6 +11,7 @@
|
||||
// A generic trait to abstract the rewriting of an element (of the AST).
|
||||
|
||||
use syntax::codemap::{CodeMap, Span};
|
||||
use syntax::parse::ParseSess;
|
||||
|
||||
use Indent;
|
||||
use config::Config;
|
||||
@ -27,6 +28,7 @@ pub trait Rewrite {
|
||||
}
|
||||
|
||||
pub struct RewriteContext<'a> {
|
||||
pub parse_session: &'a ParseSess,
|
||||
pub codemap: &'a CodeMap,
|
||||
pub config: &'a Config,
|
||||
// Indentation due to nesting of blocks.
|
||||
@ -36,6 +38,7 @@ pub struct RewriteContext<'a> {
|
||||
impl<'a> RewriteContext<'a> {
|
||||
pub fn nested_context(&self) -> RewriteContext<'a> {
|
||||
RewriteContext {
|
||||
parse_session: self.parse_session,
|
||||
codemap: self.codemap,
|
||||
config: self.config,
|
||||
block_indent: self.block_indent.block_indent(self.config),
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{self, CodeMap, Span, BytePos};
|
||||
use syntax::parse::ParseSess;
|
||||
use syntax::visit;
|
||||
|
||||
use strings::string_buffer::StringBuffer;
|
||||
@ -23,6 +24,7 @@ use macros::rewrite_macro;
|
||||
use items::rewrite_static;
|
||||
|
||||
pub struct FmtVisitor<'a> {
|
||||
pub parse_session: &'a ParseSess,
|
||||
pub codemap: &'a CodeMap,
|
||||
pub buffer: StringBuffer,
|
||||
pub last_pos: BytePos,
|
||||
@ -363,12 +365,13 @@ impl<'a> FmtVisitor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_codemap(codemap: &'a CodeMap,
|
||||
pub fn from_codemap(parse_session: &'a ParseSess,
|
||||
config: &'a Config,
|
||||
mode: Option<WriteMode>)
|
||||
-> FmtVisitor<'a> {
|
||||
FmtVisitor {
|
||||
codemap: codemap,
|
||||
parse_session: parse_session,
|
||||
codemap: parse_session.codemap(),
|
||||
buffer: StringBuffer::new(),
|
||||
last_pos: BytePos(0),
|
||||
block_indent: Indent {
|
||||
@ -461,13 +464,10 @@ impl<'a> FmtVisitor<'a> {
|
||||
let vis = utils::format_visibility(vis);
|
||||
let mut offset = self.block_indent;
|
||||
offset.alignment += vis.len() + "use ".len();
|
||||
let context = RewriteContext {
|
||||
codemap: self.codemap,
|
||||
config: self.config,
|
||||
block_indent: self.block_indent,
|
||||
};
|
||||
// 1 = ";"
|
||||
match vp.rewrite(&context, self.config.max_width - offset.width() - 1, offset) {
|
||||
match vp.rewrite(&self.get_context(),
|
||||
self.config.max_width - offset.width() - 1,
|
||||
offset) {
|
||||
Some(ref s) if s.is_empty() => {
|
||||
// Format up to last newline
|
||||
let prev_span = codemap::mk_sp(self.last_pos, span.lo);
|
||||
@ -493,6 +493,7 @@ impl<'a> FmtVisitor<'a> {
|
||||
|
||||
pub fn get_context(&self) -> RewriteContext {
|
||||
RewriteContext {
|
||||
parse_session: self.parse_session,
|
||||
codemap: self.codemap,
|
||||
config: self.config,
|
||||
block_indent: self.block_indent,
|
||||
|
@ -27,4 +27,6 @@ fn main() {
|
||||
hamkaas!{ () };
|
||||
|
||||
macrowithbraces! {dont, format, me}
|
||||
|
||||
x!(fn);
|
||||
}
|
||||
|
@ -30,4 +30,6 @@ fn main() {
|
||||
hamkaas!{ () };
|
||||
|
||||
macrowithbraces! {dont, format, me}
|
||||
|
||||
x!(fn);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user