From fdadba578655ed35f99395b66cd36d8f6d6fdaf3 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Sat, 14 Nov 2015 19:11:40 +0900 Subject: [PATCH 1/3] Store TokenTree in MacroRulesMacroExpander --- src/libsyntax/ext/tt/macro_rules.rs | 51 +++++++++++++---------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 35853256651db..898d4d80b997a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -13,7 +13,7 @@ use codemap::{Span, DUMMY_SP}; use ext::base::{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; @@ -129,15 +129,15 @@ impl<'a> MacResult for ParserAnyMacro<'a> { struct MacroRulesMacroExpander { name: ast::Ident, imported_from: Option, - lhses: Vec>, - rhses: Vec>, + lhses: Vec, + rhses: Vec, } impl TTMacroExpander for MacroRulesMacroExpander { fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, sp: Span, - arg: &[ast::TokenTree]) + arg: &[TokenTree]) -> Box { generic_extension(cx, sp, @@ -154,9 +154,9 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, sp: Span, name: ast::Ident, imported_from: Option, - arg: &[ast::TokenTree], - lhses: &[Rc], - rhses: &[Rc]) + arg: &[TokenTree], + lhses: &[TokenTree], + rhses: &[TokenTree]) -> Box { if cx.trace_macros() { println!("{}! {{ {} }}", @@ -169,25 +169,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 { + let lhs_tt = match *lhs { 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 { + let rhs = match rhses[i] { // 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") }; // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic, @@ -216,9 +208,6 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, panic!(cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..])) } } - } - _ => cx.bug("non-matcher found in parsed lhses") - } } panic!(cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..])); @@ -284,7 +273,12 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt, // 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") }; @@ -293,7 +287,12 @@ 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") }; @@ -307,11 +306,10 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt, 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); }, @@ -320,9 +318,6 @@ fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) { }, _ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \ in balanced delimiters or a repetition indicator") - }, - _ => cx.span_bug(sp, "wrong-structured lhs for follow check (didn't find a \ - MatchedNonterminal)") }; // 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. From 289b1b400a01f9cc8a6b75b3083341c1a2170831 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Sat, 14 Nov 2015 19:18:32 +0900 Subject: [PATCH 2/3] Reindent code --- src/libsyntax/ext/tt/macro_rules.rs | 44 ++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 898d4d80b997a..c5cbb95aeebff 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -169,17 +169,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 - let lhs_tt = match *lhs { - TokenTree::Delimited(_, ref delim) => &delim.tts[..], - _ => panic!(cx.span_fatal(sp, "malformed macro lhs")) - }; + let lhs_tt = match *lhs { + TokenTree::Delimited(_, ref delim) => &delim.tts[..], + _ => panic!(cx.span_fatal(sp, "malformed macro lhs")) + }; - match TokenTree::parse(cx, lhs_tt, arg) { - Success(named_matches) => { + match TokenTree::parse(cx, lhs_tt, arg) { + Success(named_matches) => { let rhs = match rhses[i] { - // ignore delimiters - TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), - _ => panic!(cx.span_fatal(sp, "macro rhs must be delimited")), + // ignore delimiters + TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), + _ => panic!(cx.span_fatal(sp, "macro rhs must be delimited")), }; // rhs has holes ( `$id` and `$(...)` that need filled) let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic, @@ -199,15 +199,15 @@ 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[..])) - } } + } } panic!(cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..])); @@ -310,14 +310,14 @@ 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 { - &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); + }, + 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. From cce7b8bd25484a882a2be5472d6a2aadd84e9539 Mon Sep 17 00:00:00 2001 From: Seo Sanghyeon Date: Sat, 14 Nov 2015 19:50:46 +0900 Subject: [PATCH 3/3] Check macro definition and do not expand invalid macros --- src/libsyntax/ext/tt/macro_rules.rs | 25 ++++++++++++++++++++--- src/test/compile-fail/macro-error.rs | 19 +++++++++++++++++ src/test/compile-fail/type-macros-fail.rs | 2 +- 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/macro-error.rs diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index c5cbb95aeebff..323cc53ecefea 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -10,7 +10,7 @@ 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::{MatchedSeq, MatchedNonterminal}; @@ -131,6 +131,7 @@ struct MacroRulesMacroExpander { imported_from: Option, lhses: Vec, rhses: Vec, + valid: bool, } impl TTMacroExpander for MacroRulesMacroExpander { @@ -139,6 +140,9 @@ impl TTMacroExpander for MacroRulesMacroExpander { sp: Span, arg: &[TokenTree]) -> Box { + if !self.valid { + return DummyResult::any(sp); + } generic_extension(cx, sp, self.name, @@ -171,7 +175,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers let lhs_tt = match *lhs { TokenTree::Delimited(_, ref delim) => &delim.tts[..], - _ => panic!(cx.span_fatal(sp, "malformed macro lhs")) + _ => cx.span_bug(sp, "malformed macro lhs") }; match TokenTree::parse(cx, lhs_tt, arg) { @@ -179,7 +183,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, let rhs = match rhses[i] { // ignore delimiters TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), - _ => panic!(cx.span_fatal(sp, "macro rhs must be delimited")), + _ => 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, @@ -271,6 +275,8 @@ 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, _) => { @@ -296,11 +302,16 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt, _ => 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) @@ -323,6 +334,14 @@ fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) { // 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 { diff --git a/src/test/compile-fail/macro-error.rs b/src/test/compile-fail/macro-error.rs new file mode 100644 index 0000000000000..817b675aedfe8 --- /dev/null +++ b/src/test/compile-fail/macro-error.rs @@ -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 or the MIT license +// , 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); +} diff --git a/src/test/compile-fail/type-macros-fail.rs b/src/test/compile-fail/type-macros-fail.rs index d51176a925d07..756f5d4547a16 100644 --- a/src/test/compile-fail/type-macros-fail.rs +++ b/src/test/compile-fail/type-macros-fail.rs @@ -9,7 +9,7 @@ // except according to those terms. macro_rules! Id { - { $T:tt } => $T + ($T:tt) => ($T); } struct Foo {