Skip to content

Commit be53b20

Browse files
committed
[WIP] Remove panic! in macro parsing.
1 parent 97839c0 commit be53b20

File tree

7 files changed

+118
-57
lines changed

7 files changed

+118
-57
lines changed

src/libsyntax/ast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1276,8 +1276,8 @@ impl TokenTree {
12761276
}
12771277

12781278
/// Use this token tree as a matcher to parse given tts.
1279-
pub fn parse(cx: &base::ExtCtxt, mtch: &[TokenTree], tts: &[TokenTree])
1280-
-> macro_parser::NamedParseResult {
1279+
pub fn parse<'a>(cx: &'a base::ExtCtxt, mtch: &[TokenTree], tts: &[TokenTree])
1280+
-> macro_parser::NamedParseResult<'a> {
12811281
// `None` is because we're not interpolating
12821282
let arg_rdr = lexer::new_tt_reader_with_doc_flag(&cx.parse_sess().span_diagnostic,
12831283
None,

src/libsyntax/codemap.rs

+4
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,10 @@ impl MultiSpan {
330330
&self.primary_spans
331331
}
332332

333+
pub fn primary_spans_mut(&mut self) -> &mut [Span] {
334+
&mut self.primary_spans
335+
}
336+
333337
/// Returns the strings to highlight. We always ensure that there
334338
/// is an entry for each of the primary spans -- for each primary
335339
/// span P, if there is at least one label with span P, we return

src/libsyntax/errors/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ impl<'a> DiagnosticBuilder<'a> {
312312
self
313313
}
314314

315+
pub fn span(&self) -> &MultiSpan {
316+
&self.span
317+
}
318+
315319
pub fn message(&self) -> &str {
316320
&self.message
317321
}

src/libsyntax/ext/tt/macro_parser.rs

+43-45
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ use ast;
8282
use ast::{TokenTree, Name, Ident};
8383
use codemap::{BytePos, mk_sp, Span, Spanned};
8484
use codemap;
85-
use errors::FatalError;
85+
use errors::DiagnosticBuilder;
8686
use parse::lexer::*; //resolve bug?
87-
use parse::ParseSess;
87+
use parse::{ParseSess, PResult};
8888
use parse::parser::{PathStyle, Parser};
8989
use parse::token::{DocComment, MatchNt, SubstNt};
9090
use parse::token::{Token, Nonterminal};
@@ -200,8 +200,8 @@ pub enum NamedMatch {
200200
MatchedNonterminal(Nonterminal)
201201
}
202202

203-
pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
204-
-> ParseResult<HashMap<Name, Rc<NamedMatch>>> {
203+
pub fn nameize<'a>(p_s: &'a ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
204+
-> ParseResult<'a, HashMap<Name, Rc<NamedMatch>>> {
205205
fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc<NamedMatch>],
206206
ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize)
207207
-> Result<(), (codemap::Span, String)> {
@@ -248,16 +248,16 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
248248
Success(ret_val)
249249
}
250250

251-
pub enum ParseResult<T> {
251+
pub enum ParseResult<'a, T> {
252252
Success(T),
253253
/// Arm failed to match
254-
Failure(codemap::Span, String),
254+
Failure(DiagnosticBuilder<'a>),
255255
/// Fatal error (malformed macro?). Abort compilation.
256256
Error(codemap::Span, String)
257257
}
258258

259-
pub type NamedParseResult = ParseResult<HashMap<Name, Rc<NamedMatch>>>;
260-
pub type PositionalParseResult = ParseResult<Vec<Rc<NamedMatch>>>;
259+
pub type NamedParseResult<'a> = ParseResult<'a, HashMap<Name, Rc<NamedMatch>>>;
260+
pub type PositionalParseResult<'a> = ParseResult<'a, Vec<Rc<NamedMatch>>>;
261261

262262
/// Perform a token equality check, ignoring syntax context (that is, an
263263
/// unhygienic comparison)
@@ -270,11 +270,11 @@ pub fn token_name_eq(t1 : &Token, t2 : &Token) -> bool {
270270
}
271271
}
272272

273-
pub fn parse(sess: &ParseSess,
274-
cfg: ast::CrateConfig,
275-
mut rdr: TtReader,
276-
ms: &[TokenTree])
277-
-> NamedParseResult {
273+
pub fn parse<'a>(sess: &'a ParseSess,
274+
cfg: ast::CrateConfig,
275+
mut rdr: TtReader<'a>,
276+
ms: &[TokenTree])
277+
-> NamedParseResult<'a> {
278278
let mut cur_eis = Vec::new();
279279
cur_eis.push(initial_matcher_pos(Rc::new(ms.iter()
280280
.cloned()
@@ -445,7 +445,9 @@ pub fn parse(sess: &ParseSess,
445445
} else if eof_eis.len() > 1 {
446446
return Error(sp, "ambiguity: multiple successful parses".to_string());
447447
} else {
448-
return Failure(sp, "unexpected end of macro invocation".to_string());
448+
return Failure(sess.span_diagnostic.struct_span_err(
449+
sp, "unexpected end of macro invocation"
450+
));
449451
}
450452
} else {
451453
if (!bb_eis.is_empty() && !next_eis.is_empty())
@@ -466,8 +468,10 @@ pub fn parse(sess: &ParseSess,
466468
}
467469
))
468470
} else if bb_eis.is_empty() && next_eis.is_empty() {
469-
return Failure(sp, format!("no rules expected the token `{}`",
470-
pprust::token_to_string(&tok)));
471+
return Failure(sess.span_diagnostic.struct_span_err(
472+
sp, &format!("no rules expected the token `{}`",
473+
pprust::token_to_string(&tok))
474+
));
471475
} else if !next_eis.is_empty() {
472476
/* Now process the next token */
473477
while !next_eis.is_empty() {
@@ -481,8 +485,12 @@ pub fn parse(sess: &ParseSess,
481485
match ei.top_elts.get_tt(ei.idx) {
482486
TokenTree::Token(span, MatchNt(_, ident)) => {
483487
let match_cur = ei.match_cur;
484-
(&mut ei.matches[match_cur]).push(Rc::new(MatchedNonterminal(
485-
parse_nt(&mut rust_parser, span, &ident.name.as_str()))));
488+
let nt = match parse_nt(&mut rust_parser, span,
489+
&ident.name.as_str()) {
490+
Ok(nt) => Rc::new(MatchedNonterminal(nt)),
491+
Err(diag) => return Failure(diag)
492+
};
493+
(&mut ei.matches[match_cur]).push(nt);
486494
ei.idx += 1;
487495
ei.match_cur += 1;
488496
}
@@ -500,55 +508,45 @@ pub fn parse(sess: &ParseSess,
500508
}
501509
}
502510

503-
pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
511+
pub fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> PResult<'a, Nonterminal> {
504512
match name {
505513
"tt" => {
506514
p.quote_depth += 1; //but in theory, non-quoted tts might be useful
507515
let res: ::parse::PResult<'a, _> = p.parse_token_tree();
508-
let res = token::NtTT(P(panictry!(res)));
516+
let res = token::NtTT(P(try!(res)));
509517
p.quote_depth -= 1;
510-
return res;
518+
return Ok(res);
511519
}
512520
_ => {}
513521
}
514522
// check at the beginning and the parser checks after each bump
515523
p.check_unknown_macro_variable();
516524
match name {
517-
"item" => match panictry!(p.parse_item()) {
518-
Some(i) => token::NtItem(i),
519-
None => {
520-
p.fatal("expected an item keyword").emit();
521-
panic!(FatalError);
522-
}
525+
"item" => match try!(p.parse_item()) {
526+
Some(i) => Ok(token::NtItem(i)),
527+
None => Err(p.fatal("expected an item keyword"))
523528
},
524-
"block" => token::NtBlock(panictry!(p.parse_block())),
525-
"stmt" => match panictry!(p.parse_stmt()) {
526-
Some(s) => token::NtStmt(P(s)),
527-
None => {
528-
p.fatal("expected a statement").emit();
529-
panic!(FatalError);
530-
}
529+
"block" => Ok(token::NtBlock(try!(p.parse_block()))),
530+
"stmt" => match try!(p.parse_stmt()) {
531+
Some(s) => Ok(token::NtStmt(P(s))),
532+
None => Err(p.fatal("expected a statement"))
531533
},
532-
"pat" => token::NtPat(panictry!(p.parse_pat())),
533-
"expr" => token::NtExpr(panictry!(p.parse_expr())),
534-
"ty" => token::NtTy(panictry!(p.parse_ty())),
534+
"pat" => Ok(token::NtPat(try!(p.parse_pat()))),
535+
"expr" => Ok(token::NtExpr(try!(p.parse_expr()))),
536+
"ty" => Ok(token::NtTy(try!(p.parse_ty()))),
535537
// this could be handled like a token, since it is one
536538
"ident" => match p.token {
537539
token::Ident(sn) => {
538540
p.bump();
539-
token::NtIdent(Box::new(Spanned::<Ident>{node: sn, span: p.span}))
541+
Ok(token::NtIdent(Box::new(Spanned::<Ident>{node: sn, span: p.span})))
540542
}
541543
_ => {
542544
let token_str = pprust::token_to_string(&p.token);
543-
p.fatal(&format!("expected ident, found {}",
544-
&token_str[..])).emit();
545-
panic!(FatalError)
545+
Err(p.fatal(&format!("expected ident, found {}", &token_str[..])))
546546
}
547547
},
548-
"path" => {
549-
token::NtPath(Box::new(panictry!(p.parse_path(PathStyle::Type))))
550-
},
551-
"meta" => token::NtMeta(panictry!(p.parse_meta_item())),
548+
"path" => Ok(token::NtPath(Box::new(try!(p.parse_path(PathStyle::Type))))),
549+
"meta" => Ok(token::NtMeta(try!(p.parse_meta_item()))),
552550
// this is not supposed to happen, since it has been checked
553551
// when compiling the macro.
554552
_ => p.span_bug(sp, "invalid fragment specifier")

src/libsyntax/ext/tt/macro_rules.rs

+39-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use ast::{self, TokenTree};
1212
use codemap::{Span, DUMMY_SP};
13+
use errors::FatalError;
1314
use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
1415
use ext::base::{NormalTT, TTMacroExpander};
1516
use ext::tt::macro_parser::{Success, Error, Failure};
@@ -158,7 +159,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
158159
}
159160

160161
/// Given `lhses` and `rhses`, this is the new macro we create
161-
fn generic_extension<'cx>(cx: &'cx ExtCtxt,
162+
fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
162163
sp: Span,
163164
name: ast::Ident,
164165
imported_from: Option<ast::Ident>,
@@ -174,7 +175,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
174175

175176
// Which arm's failure should we report? (the one furthest along)
176177
let mut best_fail_spot = DUMMY_SP;
177-
let mut best_fail_msg = "internal error: ran no matchers".to_string();
178+
let mut best_fail_diag = None;
178179

179180
for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
180181
let lhs_tt = match *lhs {
@@ -184,6 +185,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
184185

185186
match TokenTree::parse(cx, lhs_tt, arg) {
186187
Success(named_matches) => {
188+
best_fail_diag.map(|mut d| cx.parse_sess.span_diagnostic.cancel(&mut d));
189+
187190
let rhs = match rhses[i] {
188191
// ignore delimiters
189192
TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
@@ -214,17 +217,36 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
214217
macro_ident: name
215218
})
216219
}
217-
Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
218-
best_fail_spot = sp;
219-
best_fail_msg = (*msg).clone();
220-
},
220+
Failure(diag) => {
221+
let sp = diag.span().primary_span();
222+
let mut new_diag = Some(diag);
223+
if let Some(sp) = sp {
224+
if sp.lo >= best_fail_spot.lo {
225+
best_fail_spot = sp;
226+
::std::mem::swap(&mut best_fail_diag, &mut new_diag);
227+
}
228+
}
229+
// remove the previous diag if we swapped, of the new one if we didn't.
230+
new_diag.map(|mut diag| cx.parse_sess.span_diagnostic.cancel(&mut diag));
231+
}
221232
Error(err_sp, ref msg) => {
222233
cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..])
223234
}
224235
}
225236
}
226237

227-
cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..]);
238+
match best_fail_diag {
239+
None => cx.span_bug(sp, "internal error: ran no matchers"),
240+
Some(mut diag) => {
241+
let mut span = diag.span().clone();
242+
for span in span.primary_spans_mut() {
243+
*span = span.substitute_dummy(sp);
244+
}
245+
diag.set_span(span);
246+
diag.emit();
247+
panic!(FatalError);
248+
}
249+
}
228250
}
229251

230252
// Note that macro-by-example's input is also matched against a token tree:
@@ -279,7 +301,16 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
279301
arg_reader,
280302
&argument_gram) {
281303
Success(m) => m,
282-
Failure(sp, str) | Error(sp, str) => {
304+
Failure(mut diag) => {
305+
let mut span = diag.span().clone();
306+
for span in span.primary_spans_mut() {
307+
*span = span.substitute_dummy(def.span);
308+
}
309+
diag.set_span(span);
310+
diag.emit();
311+
panic!(FatalError);
312+
}
313+
Error(sp, str) => {
283314
panic!(cx.parse_sess().span_diagnostic
284315
.span_fatal(sp.substitute_dummy(def.span), &str[..]));
285316
}

src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,12 @@ fn expand_mbe_matches(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
5656
_ => unreachable!()
5757
}
5858
}
59-
Failure(_, s) | Error(_, s) => {
60-
panic!("expected Success, but got Error/Failure: {}", s);
59+
Failure(diag) => {
60+
diag.span_help(sp, "expected Success, but got Failure");
61+
panic!(diag);
62+
}
63+
Error(_, s) => {
64+
panic!("expected Success, but got Error: {}", s);
6165
}
6266
};
6367

src/test/run-pass/issue-27832.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! m {
12+
( $i:ident ) => ();
13+
( $t:tt $j:tt ) => ();
14+
}
15+
16+
fn main() {
17+
m!(c);
18+
m!(t 9);
19+
m!(0 9);
20+
}

0 commit comments

Comments
 (0)