Skip to content

Commit e0cd766

Browse files
committed
feature gate :vis matcher
1 parent 1d46805 commit e0cd766

File tree

6 files changed

+77
-24
lines changed

6 files changed

+77
-24
lines changed

src/librustc_resolve/build_reduced_graph.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,9 @@ impl<'a> Resolver<'a> {
521521
LoadedMacro::ProcMacro(ext) => return ext,
522522
};
523523

524-
let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, &macro_def));
524+
let ext = Rc::new(macro_rules::compile(&self.session.parse_sess,
525+
&self.session.features,
526+
&macro_def));
525527
self.macro_map.insert(def_id, ext.clone());
526528
ext
527529
}

src/librustc_resolve/macros.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,9 @@ impl<'a> Resolver<'a> {
671671
}
672672

673673
let def_id = self.definitions.local_def_id(item.id);
674-
let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, item));
674+
let ext = Rc::new(macro_rules::compile(&self.session.parse_sess,
675+
&self.session.features,
676+
item));
675677
self.macro_map.insert(def_id, ext);
676678
*legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding {
677679
parent: Cell::new(*legacy_scope), name: ident.name, def_id: def_id, span: item.span,

src/libsyntax/ext/tt/macro_rules.rs

+45-22
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
1818
use ext::tt::macro_parser::{parse, parse_failure_msg};
1919
use ext::tt::quoted;
2020
use ext::tt::transcribe::transcribe;
21+
use feature_gate::{self, emit_feature_err, Features, GateIssue};
2122
use parse::{Directory, ParseSess};
2223
use parse::parser::Parser;
2324
use parse::token::{self, NtTT};
2425
use parse::token::Token::*;
2526
use symbol::Symbol;
2627
use tokenstream::{TokenStream, TokenTree};
2728

29+
use std::cell::RefCell;
2830
use std::collections::{HashMap};
2931
use std::collections::hash_map::{Entry};
3032
use std::rc::Rc;
@@ -154,7 +156,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
154156
// Holy self-referential!
155157

156158
/// Converts a `macro_rules!` invocation into a syntax extension.
157-
pub fn compile(sess: &ParseSess, def: &ast::Item) -> SyntaxExtension {
159+
pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item) -> SyntaxExtension {
158160
let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
159161
let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
160162

@@ -208,7 +210,7 @@ pub fn compile(sess: &ParseSess, def: &ast::Item) -> SyntaxExtension {
208210
if let MatchedNonterminal(ref nt) = **m {
209211
if let NtTT(ref tt) = **nt {
210212
let tt = quoted::parse(tt.clone().into(), true, sess).pop().unwrap();
211-
valid &= check_lhs_nt_follows(sess, &tt);
213+
valid &= check_lhs_nt_follows(sess, features, &tt);
212214
return tt;
213215
}
214216
}
@@ -251,11 +253,13 @@ pub fn compile(sess: &ParseSess, def: &ast::Item) -> SyntaxExtension {
251253
NormalTT(exp, Some(def.span), attr::contains_name(&def.attrs, "allow_internal_unstable"))
252254
}
253255

254-
fn check_lhs_nt_follows(sess: &ParseSess, lhs: &quoted::TokenTree) -> bool {
256+
fn check_lhs_nt_follows(sess: &ParseSess,
257+
features: &RefCell<Features>,
258+
lhs: &quoted::TokenTree) -> bool {
255259
// lhs is going to be like TokenTree::Delimited(...), where the
256260
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
257261
match lhs {
258-
&quoted::TokenTree::Delimited(_, ref tts) => check_matcher(sess, &tts.tts),
262+
&quoted::TokenTree::Delimited(_, ref tts) => check_matcher(sess, features, &tts.tts),
259263
_ => {
260264
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
261265
sess.span_diagnostic.span_err(lhs.span(), msg);
@@ -307,11 +311,13 @@ fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
307311
false
308312
}
309313

310-
fn check_matcher(sess: &ParseSess, matcher: &[quoted::TokenTree]) -> bool {
314+
fn check_matcher(sess: &ParseSess,
315+
features: &RefCell<Features>,
316+
matcher: &[quoted::TokenTree]) -> bool {
311317
let first_sets = FirstSets::new(matcher);
312318
let empty_suffix = TokenSet::empty();
313319
let err = sess.span_diagnostic.err_count();
314-
check_matcher_core(sess, &first_sets, matcher, &empty_suffix);
320+
check_matcher_core(sess, features, &first_sets, matcher, &empty_suffix);
315321
err == sess.span_diagnostic.err_count()
316322
}
317323

@@ -553,6 +559,7 @@ impl TokenSet {
553559
// Requires that `first_sets` is pre-computed for `matcher`;
554560
// see `FirstSets::new`.
555561
fn check_matcher_core(sess: &ParseSess,
562+
features: &RefCell<Features>,
556563
first_sets: &FirstSets,
557564
matcher: &[quoted::TokenTree],
558565
follow: &TokenSet) -> TokenSet {
@@ -583,12 +590,11 @@ fn check_matcher_core(sess: &ParseSess,
583590
match *token {
584591
TokenTree::Token(..) | TokenTree::MetaVarDecl(..) => {
585592
let can_be_followed_by_any;
586-
if let Err(bad_frag) = has_legal_fragment_specifier(token) {
593+
if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, token) {
587594
let msg = format!("invalid fragment specifier `{}`", bad_frag);
588595
sess.span_diagnostic.struct_span_err(token.span(), &msg)
589-
.help("valid fragment specifiers are `ident`, `block`, \
590-
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
591-
and `item`")
596+
.help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \
597+
`pat`, `ty`, `path`, `meta`, `tt`, `item` and `vis`")
592598
.emit();
593599
// (This eliminates false positives and duplicates
594600
// from error messages.)
@@ -610,7 +616,7 @@ fn check_matcher_core(sess: &ParseSess,
610616
}
611617
TokenTree::Delimited(span, ref d) => {
612618
let my_suffix = TokenSet::singleton(d.close_tt(span));
613-
check_matcher_core(sess, first_sets, &d.tts, &my_suffix);
619+
check_matcher_core(sess, features, first_sets, &d.tts, &my_suffix);
614620
// don't track non NT tokens
615621
last.replace_with_irrelevant();
616622

@@ -642,7 +648,7 @@ fn check_matcher_core(sess: &ParseSess,
642648
// At this point, `suffix_first` is built, and
643649
// `my_suffix` is some TokenSet that we can use
644650
// for checking the interior of `seq_rep`.
645-
let next = check_matcher_core(sess, first_sets, &seq_rep.tts, my_suffix);
651+
let next = check_matcher_core(sess, features, first_sets, &seq_rep.tts, my_suffix);
646652
if next.maybe_empty {
647653
last.add_all(&next);
648654
} else {
@@ -807,27 +813,44 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
807813
"" => Ok(true), // keywords::Invalid
808814
_ => Err((format!("invalid fragment specifier `{}`", frag),
809815
"valid fragment specifiers are `ident`, `block`, \
810-
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
811-
and `item`"))
816+
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, \
817+
`item` and `vis`"))
812818
}
813819
}
814820
}
815821

816-
fn has_legal_fragment_specifier(tok: &quoted::TokenTree) -> Result<(), String> {
822+
fn has_legal_fragment_specifier(sess: &ParseSess,
823+
features: &RefCell<Features>,
824+
tok: &quoted::TokenTree) -> Result<(), String> {
817825
debug!("has_legal_fragment_specifier({:?})", tok);
818-
if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
819-
let s = &frag_spec.name.as_str();
820-
if !is_legal_fragment_specifier(s) {
821-
return Err(s.to_string());
826+
if let quoted::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
827+
let frag_name = frag_spec.name.as_str();
828+
let frag_span = tok.span();
829+
if !is_legal_fragment_specifier(sess, features, &frag_name, frag_span) {
830+
return Err(frag_name.to_string());
822831
}
823832
}
824833
Ok(())
825834
}
826835

827-
fn is_legal_fragment_specifier(frag: &str) -> bool {
828-
match frag {
836+
fn is_legal_fragment_specifier(sess: &ParseSess,
837+
features: &RefCell<Features>,
838+
frag_name: &str,
839+
frag_span: Span) -> bool {
840+
match frag_name {
829841
"item" | "block" | "stmt" | "expr" | "pat" |
830-
"path" | "ty" | "ident" | "meta" | "tt" | "vis" | "" => true,
842+
"path" | "ty" | "ident" | "meta" | "tt" | "" => true,
843+
"vis" => {
844+
if !features.borrow().macro_vis_matcher {
845+
let explain = feature_gate::EXPLAIN_VIS_MATCHER;
846+
emit_feature_err(sess,
847+
"macro_vis_matcher",
848+
frag_span,
849+
GateIssue::Language,
850+
explain);
851+
}
852+
true
853+
},
831854
_ => false,
832855
}
833856
}

src/libsyntax/feature_gate.rs

+6
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ declare_features! (
352352

353353
// Allows overlapping impls of marker traits
354354
(active, overlapping_marker_traits, "1.18.0", Some(29864)),
355+
356+
// Allows use of the :vis macro fragment specifier
357+
(active, macro_vis_matcher, "1.18.0", Some(41022)),
355358
);
356359

357360
declare_features! (
@@ -1012,6 +1015,9 @@ pub const EXPLAIN_DEPR_CUSTOM_DERIVE: &'static str =
10121015
pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str =
10131016
"attributes of the form `#[derive_*]` are reserved for the compiler";
10141017

1018+
pub const EXPLAIN_VIS_MATCHER: &'static str =
1019+
":vis fragment specifier is experimental and subject to change";
1020+
10151021
pub const EXPLAIN_PLACEMENT_IN: &'static str =
10161022
"placement-in expression syntax is experimental and subject to change.";
10171023

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 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+
// Test that the MSP430 interrupt ABI cannot be used when msp430_interrupt
12+
// feature gate is not used.
13+
14+
macro_rules! m { ($v:vis) => {} }
15+
//~^ ERROR :vis fragment specifier is experimental and subject to change
16+
17+
fn main() {
18+
m!(pub);
19+
}

src/test/run-pass/macro-pub-matcher.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#![allow(dead_code, unused_imports)]
12+
#![feature(macro_vis_matcher)]
1213

1314
/**
1415
Ensure that `:vis` matches can be captured in existing positions, and passed

0 commit comments

Comments
 (0)