Skip to content

Commit

Permalink
Auto merge of #29828 - sanxiyn:check-macro, r=nrc
Browse files Browse the repository at this point in the history
Fix #27409.
  • Loading branch information
bors committed Nov 16, 2015
2 parents 531b053 + cce7b8b commit bf78389
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 50 deletions.
112 changes: 63 additions & 49 deletions src/libsyntax/ext/tt/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

use ast::{self, TokenTree};
use codemap::{Span, DUMMY_SP};
use ext::base::{ExtCtxt, MacResult, SyntaxExtension};
use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
use ext::base::{NormalTT, TTMacroExpander};
use ext::tt::macro_parser::{Success, Error, Failure};
use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
use ext::tt::macro_parser::parse;
use parse::lexer::new_tt_reader;
use parse::parser::Parser;
Expand Down Expand Up @@ -129,16 +129,20 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
struct MacroRulesMacroExpander {
name: ast::Ident,
imported_from: Option<ast::Ident>,
lhses: Vec<Rc<NamedMatch>>,
rhses: Vec<Rc<NamedMatch>>,
lhses: Vec<TokenTree>,
rhses: Vec<TokenTree>,
valid: bool,
}

impl TTMacroExpander for MacroRulesMacroExpander {
fn expand<'cx>(&self,
cx: &'cx mut ExtCtxt,
sp: Span,
arg: &[ast::TokenTree])
arg: &[TokenTree])
-> Box<MacResult+'cx> {
if !self.valid {
return DummyResult::any(sp);
}
generic_extension(cx,
sp,
self.name,
Expand All @@ -154,9 +158,9 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
sp: Span,
name: ast::Ident,
imported_from: Option<ast::Ident>,
arg: &[ast::TokenTree],
lhses: &[Rc<NamedMatch>],
rhses: &[Rc<NamedMatch>])
arg: &[TokenTree],
lhses: &[TokenTree],
rhses: &[TokenTree])
-> Box<MacResult+'cx> {
if cx.trace_macros() {
println!("{}! {{ {} }}",
Expand All @@ -169,25 +173,17 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
let mut best_fail_msg = "internal error: ran no matchers".to_string();

for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
match **lhs {
MatchedNonterminal(NtTT(ref lhs_tt)) => {
let lhs_tt = match **lhs_tt {
TokenTree::Delimited(_, ref delim) => &delim.tts[..],
_ => panic!(cx.span_fatal(sp, "malformed macro lhs"))
};

match TokenTree::parse(cx, lhs_tt, arg) {
Success(named_matches) => {
let rhs = match *rhses[i] {
// okay, what's your transcriber?
MatchedNonterminal(NtTT(ref tt)) => {
match **tt {
// ignore delimiters
TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
_ => panic!(cx.span_fatal(sp, "macro rhs must be delimited")),
}
},
_ => cx.span_bug(sp, "bad thing in rhs")
let lhs_tt = match *lhs {
TokenTree::Delimited(_, ref delim) => &delim.tts[..],
_ => cx.span_bug(sp, "malformed macro lhs")
};

match TokenTree::parse(cx, lhs_tt, arg) {
Success(named_matches) => {
let rhs = match rhses[i] {
// ignore delimiters
TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
_ => cx.span_bug(sp, "malformed macro rhs"),
};
// rhs has holes ( `$id` and `$(...)` that need filled)
let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
Expand All @@ -207,17 +203,14 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
site_span: sp,
macro_ident: name
})
}
Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
}
Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
best_fail_spot = sp;
best_fail_msg = (*msg).clone();
},
Error(err_sp, ref msg) => {
},
Error(err_sp, ref msg) => {
panic!(cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]))
}
}
}
_ => cx.bug("non-matcher found in parsed lhses")
}
}

Expand Down Expand Up @@ -282,9 +275,16 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
}
};

let mut valid = true;

// Extract the arguments:
let lhses = match **argument_map.get(&lhs_nm.name).unwrap() {
MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
MatchedSeq(ref s, _) => {
s.iter().map(|m| match **m {
MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),
_ => cx.span_bug(def.span, "wrong-structured lhs")
}).collect()
}
_ => cx.span_bug(def.span, "wrong-structured lhs")
};

Expand All @@ -293,41 +293,55 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
}

let rhses = match **argument_map.get(&rhs_nm.name).unwrap() {
MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
MatchedSeq(ref s, _) => {
s.iter().map(|m| match **m {
MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),
_ => cx.span_bug(def.span, "wrong-structured rhs")
}).collect()
}
_ => cx.span_bug(def.span, "wrong-structured rhs")
};

for rhs in &rhses {
valid &= check_rhs(cx, rhs);
}

let exp: Box<_> = Box::new(MacroRulesMacroExpander {
name: def.ident,
imported_from: def.imported_from,
lhses: lhses,
rhses: rhses,
valid: valid,
});

NormalTT(exp, Some(def.span), def.allow_internal_unstable)
}

fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {
// lhs is going to be like MatchedNonterminal(NtTT(TokenTree::Delimited(...))), where the
fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
match lhs {
&MatchedNonterminal(NtTT(ref inner)) => match &**inner {
&TokenTree::Delimited(_, ref tts) => {
check_matcher(cx, tts.tts.iter(), &Eof);
},
tt @ &TokenTree::Sequence(..) => {
check_matcher(cx, Some(tt).into_iter(), &Eof);
},
_ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \
in balanced delimiters or a repetition indicator")
&TokenTree::Delimited(_, ref tts) => {
check_matcher(cx, tts.tts.iter(), &Eof);
},
_ => cx.span_bug(sp, "wrong-structured lhs for follow check (didn't find a \
MatchedNonterminal)")
tt @ &TokenTree::Sequence(..) => {
check_matcher(cx, Some(tt).into_iter(), &Eof);
},
_ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \
in balanced delimiters or a repetition indicator")
};
// we don't abort on errors on rejection, the driver will do that for us
// after parsing/expansion. we can report every error in every macro this way.
}

fn check_rhs(cx: &mut ExtCtxt, rhs: &TokenTree) -> bool {
match *rhs {
TokenTree::Delimited(..) => return true,
_ => cx.span_err(rhs.get_span(), "macro rhs must be delimited")
}
false
}

// returns the last token that was checked, for TokenTree::Sequence. this gets used later on.
fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
-> Option<(Span, Token)> where I: Iterator<Item=&'a TokenTree> {
Expand Down
19 changes: 19 additions & 0 deletions src/test/compile-fail/macro-error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Check that we report errors at macro definition, not expansion.

macro_rules! foo {
($a:expr) => $a; //~ ERROR macro rhs must be delimited
}

fn main() {
foo!(0);
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/type-macros-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// except according to those terms.

macro_rules! Id {
{ $T:tt } => $T
($T:tt) => ($T);
}

struct Foo<T> {
Expand Down

0 comments on commit bf78389

Please sign in to comment.