Skip to content

Commit 914b6f1

Browse files
authored
Rollup merge of #41012 - durka:vis-matcher, r=petrochenkov
:vis matcher for macro_rules Resurrection of @DanielKeep's implementation posted with [RFC 1575](rust-lang/rfcs#1575). @jseyfried was of the opinion that this doesn't need an RFC. Needed before merge: - [x] sign-off from @DanielKeep since I stole his code - [x] feature gate - [x] docs
2 parents 5997806 + cfa51f2 commit 914b6f1

File tree

13 files changed

+237
-32
lines changed

13 files changed

+237
-32
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
- [lookup_host](lookup-host.md)
115115
- [loop_break_value](loop-break-value.md)
116116
- [macro_reexport](macro-reexport.md)
117+
- [macro_vis_matcher](macro-vis-matcher.md)
117118
- [main](main.md)
118119
- [manually_drop](manually-drop.md)
119120
- [map_entry_recover_keys](map-entry-recover-keys.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# `macro_vis_matcher`
2+
3+
The tracking issue for this feature is: [#41022]
4+
5+
With this feature gate enabled, the [list of fragment specifiers][frags] gains one more entry:
6+
7+
* `vis`: a visibility qualifier. Examples: nothing (default visibility); `pub`; `pub(crate)`.
8+
9+
A `vis` variable may be followed by a comma, ident, type, or path.
10+
11+
[#41022]: https://github.com/rust-lang/rust/issues/41022
12+
[frags]: ../book/first-edition/macros.html#syntactic-requirements
13+
14+
------------------------

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_parser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
529529
token::NtPath(panictry!(p.parse_path(PathStyle::Type)))
530530
},
531531
"meta" => token::NtMeta(panictry!(p.parse_meta_item())),
532+
"vis" => token::NtVis(panictry!(p.parse_visibility(true))),
532533
// this is not supposed to happen, since it has been checked
533534
// when compiling the macro.
534535
_ => p.span_bug(sp, "invalid fragment specifier")

src/libsyntax/ext/tt/macro_rules.rs

+58-21
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 {
@@ -790,30 +796,61 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
790796
// harmless
791797
Ok(true)
792798
},
799+
"vis" => {
800+
// Explicitly disallow `priv`, on the off chance it comes back.
801+
match *tok {
802+
TokenTree::Token(_, ref tok) => match *tok {
803+
Comma => Ok(true),
804+
Ident(i) if i.name != "priv" => Ok(true),
805+
ref tok => Ok(tok.can_begin_type())
806+
},
807+
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "ident"
808+
|| frag.name == "ty"
809+
|| frag.name == "path" => Ok(true),
810+
_ => Ok(false)
811+
}
812+
},
793813
"" => Ok(true), // keywords::Invalid
794814
_ => Err((format!("invalid fragment specifier `{}`", frag),
795815
"valid fragment specifiers are `ident`, `block`, \
796-
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
797-
and `item`"))
816+
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, \
817+
`item` and `vis`"))
798818
}
799819
}
800820
}
801821

802-
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> {
803825
debug!("has_legal_fragment_specifier({:?})", tok);
804-
if let quoted::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
805-
let s = &frag_spec.name.as_str();
806-
if !is_legal_fragment_specifier(s) {
807-
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());
808831
}
809832
}
810833
Ok(())
811834
}
812835

813-
fn is_legal_fragment_specifier(frag: &str) -> bool {
814-
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 {
815841
"item" | "block" | "stmt" | "expr" | "pat" |
816842
"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+
},
817854
_ => false,
818855
}
819856
}

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

src/libsyntax/fold.rs

+1
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
636636
token::NtWhereClause(where_clause) =>
637637
token::NtWhereClause(fld.fold_where_clause(where_clause)),
638638
token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)),
639+
token::NtVis(vis) => token::NtVis(fld.fold_vis(vis)),
639640
}
640641
}
641642

src/libsyntax/parse/parser.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5056,7 +5056,9 @@ impl<'a> Parser<'a> {
50565056
/// and `pub(super)` for `pub(in super)`. If the following element can't be a tuple (i.e. it's
50575057
/// a function definition, it's not a tuple struct field) and the contents within the parens
50585058
/// isn't valid, emit a proper diagnostic.
5059-
fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
5059+
pub fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
5060+
maybe_whole!(self, NtVis, |x| x);
5061+
50605062
if !self.eat_keyword(keywords::Pub) {
50615063
return Ok(Visibility::Inherited)
50625064
}

src/libsyntax/parse/token.rs

+2
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ pub enum Nonterminal {
363363
/// Stuff inside brackets for attributes
364364
NtMeta(ast::MetaItem),
365365
NtPath(ast::Path),
366+
NtVis(ast::Visibility),
366367
NtTT(TokenTree),
367368
// These are not exposed to macros, but are used by quasiquote.
368369
NtArm(ast::Arm),
@@ -392,6 +393,7 @@ impl fmt::Debug for Nonterminal {
392393
NtGenerics(..) => f.pad("NtGenerics(..)"),
393394
NtWhereClause(..) => f.pad("NtWhereClause(..)"),
394395
NtArg(..) => f.pad("NtArg(..)"),
396+
NtVis(..) => f.pad("NtVis(..)"),
395397
}
396398
}
397399
}

src/libsyntax/print/pprust.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ pub fn token_to_string(tok: &Token) -> String {
293293
token::NtGenerics(ref e) => generics_to_string(&e),
294294
token::NtWhereClause(ref e) => where_clause_to_string(&e),
295295
token::NtArg(ref e) => arg_to_string(&e),
296+
token::NtVis(ref e) => vis_to_string(&e),
296297
}
297298
}
298299
}
@@ -373,6 +374,10 @@ pub fn ident_to_string(id: ast::Ident) -> String {
373374
to_string(|s| s.print_ident(id))
374375
}
375376

377+
pub fn vis_to_string(v: &ast::Visibility) -> String {
378+
to_string(|s| s.print_visibility(v))
379+
}
380+
376381
pub fn fun_to_string(decl: &ast::FnDecl,
377382
unsafety: ast::Unsafety,
378383
constness: ast::Constness,
@@ -427,13 +432,7 @@ pub fn mac_to_string(arg: &ast::Mac) -> String {
427432
}
428433

429434
pub fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
430-
match *vis {
431-
ast::Visibility::Public => format!("pub {}", s),
432-
ast::Visibility::Crate(_) => format!("pub(crate) {}", s),
433-
ast::Visibility::Restricted { ref path, .. } =>
434-
format!("pub({}) {}", to_string(|s| s.print_path(path, false, 0, true)), s),
435-
ast::Visibility::Inherited => s.to_string()
436-
}
435+
format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
437436
}
438437

439438
fn needs_parentheses(expr: &ast::Expr) -> bool {
@@ -1468,7 +1467,11 @@ impl<'a> State<'a> {
14681467
ast::Visibility::Crate(_) => self.word_nbsp("pub(crate)"),
14691468
ast::Visibility::Restricted { ref path, .. } => {
14701469
let path = to_string(|s| s.print_path(path, false, 0, true));
1471-
self.word_nbsp(&format!("pub({})", path))
1470+
if path == "self" || path == "super" {
1471+
self.word_nbsp(&format!("pub({})", path))
1472+
} else {
1473+
self.word_nbsp(&format!("pub(in {})", path))
1474+
}
14721475
}
14731476
ast::Visibility::Inherited => Ok(())
14741477
}
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+
}

0 commit comments

Comments
 (0)