]> git.lizzy.rs Git - rust.git/commitdiff
Add feature gate + tests
authorMark Mansi <markm@cs.wisc.edu>
Fri, 26 Jan 2018 22:16:43 +0000 (16:16 -0600)
committerMark Mansi <markm@cs.wisc.edu>
Tue, 30 Jan 2018 18:42:51 +0000 (12:42 -0600)
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/ext/tt/quoted.rs
src/libsyntax/feature_gate.rs
src/test/ui/feature-gate-macro_at_most_once_rep.rs [new file with mode: 0644]
src/test/ui/feature-gate-macro_at_most_once_rep.stderr [new file with mode: 0644]

index 9efb4faa63535725bc198d261465afe694120063..5254c751e6b621b500e1620f320aebda91eb7020 100644 (file)
@@ -237,7 +237,8 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
             s.iter().map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
-                        let tt = quoted::parse(tt.clone().into(), true, sess).pop().unwrap();
+                        let tt = quoted::parse(tt.clone().into(), true, sess, features, &def.attrs)
+                            .pop().unwrap();
                         valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt);
                         return tt;
                     }
@@ -253,7 +254,8 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
             s.iter().map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
-                        return quoted::parse(tt.clone().into(), false, sess).pop().unwrap();
+                        return quoted::parse(tt.clone().into(), false, sess, features, &def.attrs)
+                            .pop().unwrap();
                     }
                 }
                 sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
index 670d3614604b76dfd35bb22a66165e27789d3aff..8e05a6ccc470411db1ec64d494431b4b933c86a2 100644 (file)
@@ -8,14 +8,16 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use ast;
+use {ast, attr};
 use ext::tt::macro_parser;
+use feature_gate::{self, emit_feature_err, Features, GateIssue};
 use parse::{token, ParseSess};
 use print::pprust;
 use symbol::keywords;
 use syntax_pos::{BytePos, Span, DUMMY_SP};
 use tokenstream;
 
+use std::cell::RefCell;
 use std::iter::Peekable;
 use std::rc::Rc;
 
@@ -179,6 +181,8 @@ pub fn parse(
     input: tokenstream::TokenStream,
     expect_matchers: bool,
     sess: &ParseSess,
+    features: &RefCell<Features>,
+    attrs: &[ast::Attribute],
 ) -> Vec<TokenTree> {
     // Will contain the final collection of `self::TokenTree`
     let mut result = Vec::new();
@@ -187,10 +191,9 @@ pub fn parse(
     // additional trees if need be.
     let mut trees = input.trees().peekable();
     while let Some(tree) = trees.next() {
-        let tree = parse_tree(tree, &mut trees, expect_matchers, sess);
-
         // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
         // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`).
+        let tree = parse_tree(tree, &mut trees, expect_matchers, sess, features, attrs);
         match tree {
             TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
                 let span = match trees.next() {
@@ -244,6 +247,8 @@ fn parse_tree<I>(
     trees: &mut Peekable<I>,
     expect_matchers: bool,
     sess: &ParseSess,
+    features: &RefCell<Features>,
+    attrs: &[ast::Attribute],
 ) -> TokenTree
 where
     I: Iterator<Item = tokenstream::TokenTree>,
@@ -262,9 +267,9 @@ fn parse_tree<I>(
                     sess.span_diagnostic.span_err(span, &msg);
                 }
                 // Parse the contents of the sequence itself
-                let sequence = parse(delimited.tts.into(), expect_matchers, sess);
+                let sequence = parse(delimited.tts.into(), expect_matchers, sess, features, attrs);
                 // Get the Kleene operator and optional separator
-                let (separator, op) = parse_sep_and_kleene_op(trees, span, sess);
+                let (separator, op) = parse_sep_and_kleene_op(trees, span, sess, features, attrs);
                 // Count the number of captured "names" (i.e. named metavars)
                 let name_captures = macro_parser::count_names(&sequence);
                 TokenTree::Sequence(
@@ -317,7 +322,7 @@ fn parse_tree<I>(
             span,
             Rc::new(Delimited {
                 delim: delimited.delim,
-                tts: parse(delimited.tts.into(), expect_matchers, sess),
+                tts: parse(delimited.tts.into(), expect_matchers, sess, features, attrs),
             }),
         ),
     }
@@ -373,6 +378,8 @@ fn parse_sep_and_kleene_op<I>(
     input: &mut Peekable<I>,
     span: Span,
     sess: &ParseSess,
+    features: &RefCell<Features>,
+    attrs: &[ast::Attribute],
 ) -> (Option<token::Token>, KleeneOp)
 where
     I: Iterator<Item = tokenstream::TokenTree>,
@@ -401,6 +408,21 @@ fn parse_sep_and_kleene_op<I>(
                 // (N.B. We need to advance the input iterator.)
                 match parse_kleene_op(input, span) {
                     // #2 is a KleeneOp (this is the only valid option) :)
+                    Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
+                        if !features.borrow().macro_at_most_once_rep
+                            && !attr::contains_name(attrs, "allow_internal_unstable")
+                        {
+                            let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
+                            emit_feature_err(
+                                sess,
+                                "macro_at_most_once_rep",
+                                span,
+                                GateIssue::Language,
+                                explain,
+                            );
+                        }
+                        return (Some(token::Question), op);
+                    }
                     Ok(Ok(op)) => return (Some(token::Question), op),
 
                     // #2 is a random token (this is an error) :(
@@ -410,6 +432,19 @@ fn parse_sep_and_kleene_op<I>(
                     Err(span) => span,
                 }
             } else {
+                if !features.borrow().macro_at_most_once_rep
+                    && !attr::contains_name(attrs, "allow_internal_unstable")
+                {
+                    let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
+                    emit_feature_err(
+                        sess,
+                        "macro_at_most_once_rep",
+                        span,
+                        GateIssue::Language,
+                        explain,
+                    );
+                }
+
                 // #2 is a random tree and #1 is KleeneOp::ZeroOrOne
                 return (None, op);
             }
@@ -418,6 +453,21 @@ fn parse_sep_and_kleene_op<I>(
         // #1 is a separator followed by #2, a KleeneOp
         Ok(Err((tok, span))) => match parse_kleene_op(input, span) {
             // #2 is a KleeneOp :D
+            Ok(Ok(op)) if op == KleeneOp::ZeroOrOne => {
+                if !features.borrow().macro_at_most_once_rep
+                    && !attr::contains_name(attrs, "allow_internal_unstable")
+                {
+                    let explain = feature_gate::EXPLAIN_MACRO_AT_MOST_ONCE_REP;
+                    emit_feature_err(
+                        sess,
+                        "macro_at_most_once_rep",
+                        span,
+                        GateIssue::Language,
+                        explain,
+                    );
+                }
+                return (Some(tok), op);
+            }
             Ok(Ok(op)) => return (Some(tok), op),
 
             // #2 is a random token :(
@@ -431,7 +481,13 @@ fn parse_sep_and_kleene_op<I>(
         Err(span) => span,
     };
 
-    sess.span_diagnostic
-        .span_err(span, "expected one of: `*`, `+`, or `?`");
+    if !features.borrow().macro_at_most_once_rep
+        && !attr::contains_name(attrs, "allow_internal_unstable")
+    {
+        sess.span_diagnostic
+            .span_err(span, "expected one of: `*`, `+`, or `?`");
+    } else {
+        sess.span_diagnostic.span_err(span, "expected `*` or `+`");
+    }
     (None, KleeneOp::ZeroOrMore)
 }
index 9a2560b04583da01b3c68ddcec65ebe9842e4afc..9358511018a93778ca50f9ec3fc6bad10238e4d2 100644 (file)
@@ -452,6 +452,11 @@ pub fn new() -> Features {
 
     // Allows `#[repr(transparent)]` attribute on newtype structs
     (active, repr_transparent, "1.25.0", Some(43036)),
+
+    // Use `?` as the Kleene "at most one" operator
+    // FIXME(mark-i-m): make sure we use the correct issue number when there is
+    //   a tracking issue...
+    (active, macro_at_most_once_rep, "1.25.0", None),
 );
 
 declare_features! (
@@ -1250,6 +1255,9 @@ fn leveled_feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue
 pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str =
     "Unsized tuple coercion is not stable enough for use and is subject to change";
 
+pub const EXPLAIN_MACRO_AT_MOST_ONCE_REP: &'static str =
+    "Using the `?` macro Kleene operator for \"at most one\" repetition is unstable";
+
 struct PostExpansionVisitor<'a> {
     context: &'a Context<'a>,
 }
diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.rs b/src/test/ui/feature-gate-macro_at_most_once_rep.rs
new file mode 100644 (file)
index 0000000..13b2a8e
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 2017 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.
+
+// Test that the MSP430 interrupt ABI cannot be used when msp430_interrupt
+// feature gate is not used.
+
+macro_rules! m { ($(a)?) => {} }
+//~^ ERROR Using the `?` macro Kleene operator for "at most one" repetition is unstable
+
+fn main() {
+    m!();
+}
diff --git a/src/test/ui/feature-gate-macro_at_most_once_rep.stderr b/src/test/ui/feature-gate-macro_at_most_once_rep.stderr
new file mode 100644 (file)
index 0000000..f470399
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0658]: Using the `?` macro Kleene operator for "at most one" repetition is unstable
+  --> $DIR/feature-gate-macro_at_most_once_rep.rs:14:19
+   |
+14 | macro_rules! m { ($(a)?) => {} }
+   |                   ^^^^^
+   |
+   = help: add #![feature(macro_at_most_once_rep)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+