Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8f81d0b

Browse files
committedJul 2, 2019
Auto merge of #62008 - ia0:issues_61053, r=<try>
Add meta-variable checks in macro definitions This is an implementation of #61053. It is not sound (some errors are not reported) and not complete (reports may not be actual errors). This is due to the possibility to define macros in macros in indirect ways. See module documentation of `macro_check` for more details. What remains to be done: - [x] Migrate from an error to an allow-by-default lint. - [x] Add more comments in particular for the handling of nested macros. - [x] Add more tests if needed. - [x] Try to avoid cloning too much (one idea is to use lists on the stack). - [ ] Run crater with deny-by-default lint (measure rate of false positives). - [ ] Remove extra commit for deny-by-default lint - [x] Create a PR to remove the old `question_mark_macro_sep` lint #62160
2 parents 848e0a2 + b3b12b0 commit 8f81d0b

24 files changed

+962
-74
lines changed
 

‎src/libcore/tests/ascii.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ macro_rules! assert_none {
151151
stringify!($what), b);
152152
}
153153
}
154-
)*
154+
)+
155155
}};
156156
($what:ident, $($str:tt),+,) => (assert_none!($what,$($str),+))
157157
}

‎src/libcore/tests/pattern.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::str::pattern::*;
55
macro_rules! search_asserts {
66
($haystack:expr, $needle:expr, $testname:expr, [$($func:ident),*], $result:expr) => {
77
let mut searcher = $needle.into_searcher($haystack);
8-
let arr = [$( Step::from(searcher.$func()) ),+];
8+
let arr = [$( Step::from(searcher.$func()) ),*];
99
assert_eq!(&arr[..], &$result, $testname);
1010
}
1111
}

‎src/librustc/lint/builtin.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,12 @@ pub mod parser {
353353
Warn,
354354
"ill-formed attribute inputs that were previously accepted and used in practice"
355355
}
356+
357+
declare_lint! {
358+
pub META_VARIABLE_MISUSE,
359+
Deny,
360+
"possible meta-variable misuse at macro definition"
361+
}
356362
}
357363

358364
declare_lint! {
@@ -439,6 +445,7 @@ declare_lint_pass! {
439445
MACRO_USE_EXTERN_CRATE,
440446
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
441447
parser::ILL_FORMED_ATTRIBUTE_INPUT,
448+
parser::META_VARIABLE_MISUSE,
442449
DEPRECATED_IN_FUTURE,
443450
AMBIGUOUS_ASSOCIATED_ITEMS,
444451
NESTED_IMPL_TRAIT,

‎src/librustc/lint/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::hir::def_id::{CrateNum, LOCAL_CRATE};
2727
use crate::hir::intravisit;
2828
use crate::hir;
2929
use crate::lint::builtin::BuiltinLintDiagnostics;
30-
use crate::lint::builtin::parser::ILL_FORMED_ATTRIBUTE_INPUT;
30+
use crate::lint::builtin::parser::{ILL_FORMED_ATTRIBUTE_INPUT, META_VARIABLE_MISUSE};
3131
use crate::session::{Session, DiagnosticMessageId};
3232
use crate::ty::TyCtxt;
3333
use crate::ty::query::Providers;
@@ -81,6 +81,7 @@ impl Lint {
8181
pub fn from_parser_lint_id(lint_id: BufferedEarlyLintId) -> &'static Self {
8282
match lint_id {
8383
BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT,
84+
BufferedEarlyLintId::MetaVariableMisuse => META_VARIABLE_MISUSE,
8485
}
8586
}
8687

‎src/libsyntax/early_buffered_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use syntax_pos::MultiSpan;
1010
/// passed to `rustc::lint::Lint::from_parser_lint_id` to get a `rustc::lint::Lint`.
1111
pub enum BufferedEarlyLintId {
1212
IllFormedAttributeInput,
13+
MetaVariableMisuse,
1314
}
1415

1516
/// Stores buffered lint info which can later be passed to `librustc`.

‎src/libsyntax/ext/tt/macro_check.rs

Lines changed: 620 additions & 0 deletions
Large diffs are not rendered by default.

‎src/libsyntax/ext/tt/macro_parser.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,8 @@ fn inner_parse_loop<'root, 'tt>(
557557
// implicitly disallowing OneOrMore from having 0 matches here. Thus, that will
558558
// result in a "no rules expected token" error by virtue of this matcher not
559559
// working.
560-
if seq.op == quoted::KleeneOp::ZeroOrMore
561-
|| seq.op == quoted::KleeneOp::ZeroOrOne
560+
if seq.kleene.op == quoted::KleeneOp::ZeroOrMore
561+
|| seq.kleene.op == quoted::KleeneOp::ZeroOrOne
562562
{
563563
let mut new_item = item.clone();
564564
new_item.match_cur += seq.num_captures;
@@ -573,7 +573,7 @@ fn inner_parse_loop<'root, 'tt>(
573573
cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos {
574574
stack: smallvec![],
575575
sep: seq.separator.clone(),
576-
seq_op: Some(seq.op),
576+
seq_op: Some(seq.kleene.op),
577577
idx: 0,
578578
matches,
579579
match_lo: item.match_cur,

‎src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::ext::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
33
use crate::ext::base::{SyntaxExtension, SyntaxExtensionKind};
44
use crate::ext::expand::{AstFragment, AstFragmentKind};
55
use crate::ext::hygiene::Transparency;
6+
use crate::ext::tt::macro_check;
67
use crate::ext::tt::macro_parser::{parse, parse_failure_msg};
78
use crate::ext::tt::macro_parser::{Error, Failure, Success};
89
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq};
@@ -19,7 +20,7 @@ use crate::{ast, attr};
1920

2021
use errors::FatalError;
2122
use log::debug;
22-
use syntax_pos::{symbol::Ident, Span};
23+
use syntax_pos::Span;
2324

2425
use rustc_data_structures::fx::FxHashMap;
2526
use std::borrow::Cow;
@@ -276,7 +277,7 @@ pub fn compile(
276277
if body.legacy { token::Semi } else { token::Comma },
277278
def.span,
278279
)),
279-
op: quoted::KleeneOp::OneOrMore,
280+
kleene: quoted::KleeneToken::new(quoted::KleeneOp::OneOrMore, def.span),
280281
num_captures: 2,
281282
}),
282283
),
@@ -286,7 +287,7 @@ pub fn compile(
286287
Lrc::new(quoted::SequenceRepetition {
287288
tts: vec![quoted::TokenTree::token(token::Semi, def.span)],
288289
separator: None,
289-
op: quoted::KleeneOp::ZeroOrMore,
290+
kleene: quoted::KleeneToken::new(quoted::KleeneOp::ZeroOrMore, def.span),
290291
num_captures: 0,
291292
}),
292293
),
@@ -369,14 +370,12 @@ pub fn compile(
369370
// don't abort iteration early, so that errors for multiple lhses can be reported
370371
for lhs in &lhses {
371372
valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
372-
valid &= check_lhs_duplicate_matcher_bindings(
373-
sess,
374-
slice::from_ref(lhs),
375-
&mut FxHashMap::default(),
376-
def.id,
377-
);
378373
}
379374

375+
// We use CRATE_NODE_ID instead of `def.id` otherwise we may emit buffered lints for a node id
376+
// that is not lint-checked and trigger the "failed to process buffered lint here" bug.
377+
valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
378+
380379
let expander: Box<_> =
381380
Box::new(MacroRulesMacroExpander { name: def.ident, lhses, rhses, valid });
382381

@@ -484,8 +483,8 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
484483
&& seq.tts.iter().all(|seq_tt| match *seq_tt {
485484
TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis,
486485
TokenTree::Sequence(_, ref sub_seq) => {
487-
sub_seq.op == quoted::KleeneOp::ZeroOrMore
488-
|| sub_seq.op == quoted::KleeneOp::ZeroOrOne
486+
sub_seq.kleene.op == quoted::KleeneOp::ZeroOrMore
487+
|| sub_seq.kleene.op == quoted::KleeneOp::ZeroOrOne
489488
}
490489
_ => false,
491490
})
@@ -504,45 +503,6 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
504503
true
505504
}
506505

507-
/// Check that the LHS contains no duplicate matcher bindings. e.g. `$a:expr, $a:expr` would be
508-
/// illegal, since it would be ambiguous which `$a` to use if we ever needed to.
509-
fn check_lhs_duplicate_matcher_bindings(
510-
sess: &ParseSess,
511-
tts: &[quoted::TokenTree],
512-
metavar_names: &mut FxHashMap<Ident, Span>,
513-
node_id: ast::NodeId,
514-
) -> bool {
515-
use self::quoted::TokenTree;
516-
for tt in tts {
517-
match *tt {
518-
TokenTree::MetaVarDecl(span, name, _kind) => {
519-
if let Some(&prev_span) = metavar_names.get(&name) {
520-
sess.span_diagnostic
521-
.struct_span_err(span, "duplicate matcher binding")
522-
.span_note(prev_span, "previous declaration was here")
523-
.emit();
524-
return false;
525-
} else {
526-
metavar_names.insert(name, span);
527-
}
528-
}
529-
TokenTree::Delimited(_, ref del) => {
530-
if !check_lhs_duplicate_matcher_bindings(sess, &del.tts, metavar_names, node_id) {
531-
return false;
532-
}
533-
}
534-
TokenTree::Sequence(_, ref seq) => {
535-
if !check_lhs_duplicate_matcher_bindings(sess, &seq.tts, metavar_names, node_id) {
536-
return false;
537-
}
538-
}
539-
_ => {}
540-
}
541-
}
542-
543-
true
544-
}
545-
546506
fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
547507
match *rhs {
548508
quoted::TokenTree::Delimited(..) => return true,
@@ -635,8 +595,8 @@ impl FirstSets {
635595

636596
// Reverse scan: Sequence comes before `first`.
637597
if subfirst.maybe_empty
638-
|| seq_rep.op == quoted::KleeneOp::ZeroOrMore
639-
|| seq_rep.op == quoted::KleeneOp::ZeroOrOne
598+
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
599+
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
640600
{
641601
// If sequence is potentially empty, then
642602
// union them (preserving first emptiness).
@@ -684,8 +644,8 @@ impl FirstSets {
684644
assert!(first.maybe_empty);
685645
first.add_all(subfirst);
686646
if subfirst.maybe_empty
687-
|| seq_rep.op == quoted::KleeneOp::ZeroOrMore
688-
|| seq_rep.op == quoted::KleeneOp::ZeroOrOne
647+
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrMore
648+
|| seq_rep.kleene.op == quoted::KleeneOp::ZeroOrOne
689649
{
690650
// continue scanning for more first
691651
// tokens, but also make sure we

‎src/libsyntax/ext/tt/quoted.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,23 @@ pub struct SequenceRepetition {
5050
/// The optional separator
5151
pub separator: Option<Token>,
5252
/// Whether the sequence can be repeated zero (*), or one or more times (+)
53-
pub op: KleeneOp,
53+
pub kleene: KleeneToken,
5454
/// The number of `Match`s that appear in the sequence (and subsequences)
5555
pub num_captures: usize,
5656
}
5757

58+
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Copy)]
59+
pub struct KleeneToken {
60+
pub span: Span,
61+
pub op: KleeneOp,
62+
}
63+
64+
impl KleeneToken {
65+
pub fn new(op: KleeneOp, span: Span) -> KleeneToken {
66+
KleeneToken { span, op }
67+
}
68+
}
69+
5870
/// A Kleene-style [repetition operator](http://en.wikipedia.org/wiki/Kleene_star)
5971
/// for token sequences.
6072
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
@@ -111,6 +123,22 @@ impl TokenTree {
111123
}
112124
}
113125

126+
/// Returns `true` if the given token tree is delimited.
127+
pub fn is_delimited(&self) -> bool {
128+
match *self {
129+
TokenTree::Delimited(..) => true,
130+
_ => false,
131+
}
132+
}
133+
134+
/// Returns `true` if the given token tree is a token of the given kind.
135+
pub fn is_token(&self, expected_kind: &TokenKind) -> bool {
136+
match self {
137+
TokenTree::Token(Token { kind: actual_kind, .. }) => actual_kind == expected_kind,
138+
_ => false,
139+
}
140+
}
141+
114142
/// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences.
115143
pub fn get_tt(&self, index: usize) -> TokenTree {
116144
match (self, index) {
@@ -273,15 +301,15 @@ fn parse_tree(
273301
macro_node_id,
274302
);
275303
// Get the Kleene operator and optional separator
276-
let (separator, op) = parse_sep_and_kleene_op(trees, span.entire(), sess);
304+
let (separator, kleene) = parse_sep_and_kleene_op(trees, span.entire(), sess);
277305
// Count the number of captured "names" (i.e., named metavars)
278306
let name_captures = macro_parser::count_names(&sequence);
279307
TokenTree::Sequence(
280308
span,
281309
Lrc::new(SequenceRepetition {
282310
tts: sequence,
283311
separator,
284-
op,
312+
kleene,
285313
num_captures: name_captures,
286314
}),
287315
)
@@ -379,28 +407,28 @@ fn parse_sep_and_kleene_op(
379407
input: &mut Peekable<impl Iterator<Item = tokenstream::TokenTree>>,
380408
span: Span,
381409
sess: &ParseSess,
382-
) -> (Option<Token>, KleeneOp) {
410+
) -> (Option<Token>, KleeneToken) {
383411
// We basically look at two token trees here, denoted as #1 and #2 below
384412
let span = match parse_kleene_op(input, span) {
385413
// #1 is a `?`, `+`, or `*` KleeneOp
386-
Ok(Ok((op, _))) => return (None, op),
414+
Ok(Ok((op, span))) => return (None, KleeneToken::new(op, span)),
387415

388416
// #1 is a separator followed by #2, a KleeneOp
389417
Ok(Err(token)) => match parse_kleene_op(input, token.span) {
390418
// #2 is the `?` Kleene op, which does not take a separator (error)
391-
Ok(Ok((KleeneOp::ZeroOrOne, _))) => {
419+
Ok(Ok((KleeneOp::ZeroOrOne, span))) => {
392420
// Error!
393421
sess.span_diagnostic.span_err(
394422
token.span,
395423
"the `?` macro repetition operator does not take a separator",
396424
);
397425

398426
// Return a dummy
399-
return (None, KleeneOp::ZeroOrMore);
427+
return (None, KleeneToken::new(KleeneOp::ZeroOrMore, span));
400428
}
401429

402430
// #2 is a KleeneOp :D
403-
Ok(Ok((op, _))) => return (Some(token), op),
431+
Ok(Ok((op, span))) => return (Some(token), KleeneToken::new(op, span)),
404432

405433
// #2 is a random token or not a token at all :(
406434
Ok(Err(Token { span, .. })) | Err(span) => span,
@@ -414,5 +442,5 @@ fn parse_sep_and_kleene_op(
414442
sess.span_diagnostic.span_err(span, "expected one of: `*`, `+`, or `?`");
415443

416444
// Return a dummy
417-
(None, KleeneOp::ZeroOrMore)
445+
(None, KleeneToken::new(KleeneOp::ZeroOrMore, span))
418446
}

‎src/libsyntax/ext/tt/transcribe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ pub fn transcribe(
183183

184184
// Is the repetition empty?
185185
if len == 0 {
186-
if seq.op == quoted::KleeneOp::OneOrMore {
186+
if seq.kleene.op == quoted::KleeneOp::OneOrMore {
187187
// FIXME: this really ought to be caught at macro definition
188188
// time... It happens when the Kleene operator in the matcher and
189189
// the body for the same meta-variable do not match.

‎src/libsyntax/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ pub mod ext {
174174

175175
pub mod tt {
176176
pub mod transcribe;
177+
pub mod macro_check;
177178
pub mod macro_parser;
178179
pub mod macro_rules;
179180
pub mod quoted;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// run-pass
2+
#![deny(meta_variable_misuse)]
3+
4+
macro_rules! foo {
5+
($($m:ident $($f:ident $v:tt)+),*) => {
6+
$($(macro_rules! $f { () => { $v } })+)*
7+
$(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })*
8+
}
9+
}
10+
11+
foo!(m a 1 b 2, n c 3);
12+
m!();
13+
n!();
14+
15+
macro_rules! no_shadow {
16+
($x:tt) => { macro_rules! bar { ($x:tt) => { 42 }; } };
17+
}
18+
no_shadow!(z);
19+
20+
macro_rules! make_plus {
21+
($n: ident $x:expr) => { macro_rules! $n { ($y:expr) => { $x + $y }; } };
22+
}
23+
make_plus!(add3 3);
24+
25+
fn main() {
26+
assert_eq!(a!(), 1);
27+
assert_eq!(b!(), 2);
28+
assert_eq!(c!(), 3);
29+
assert_eq!(a(), 1);
30+
assert_eq!(b(), 2);
31+
assert_eq!(c(), 3);
32+
assert_eq!(bar!(z:tt), 42);
33+
assert_eq!(add3!(9), 12);
34+
}

‎src/test/ui/issues/issue-6596-1.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
macro_rules! e {
22
($inp:ident) => (
3-
$nonexistent
3+
$nonexistent //~ ERROR unknown macro variable `nonexistent`
44
//~^ ERROR unknown macro variable `nonexistent`
55
);
66
}

‎src/test/ui/issues/issue-6596-1.stderr

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,13 @@ LL | $nonexistent
77
LL | e!(foo);
88
| -------- in this macro invocation
99

10-
error: aborting due to previous error
10+
error: unknown macro variable `nonexistent`
11+
--> $DIR/issue-6596-1.rs:3:9
12+
|
13+
LL | $nonexistent
14+
| ^^^^^^^^^^^^
15+
|
16+
= note: #[deny(meta_variable_misuse)] on by default
17+
18+
error: aborting due to 2 previous errors
1119

‎src/test/ui/issues/issue-6596-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
macro_rules! g {
44
($inp:ident) => (
5-
{ $inp $nonexistent }
5+
{ $inp $nonexistent } //~ ERROR unknown macro variable `nonexistent`
66
//~^ ERROR unknown macro variable `nonexistent`
77
);
88
}

‎src/test/ui/issues/issue-6596-2.stderr

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,13 @@ LL | { $inp $nonexistent }
77
LL | g!(foo);
88
| -------- in this macro invocation
99

10-
error: aborting due to previous error
10+
error: unknown macro variable `nonexistent`
11+
--> $DIR/issue-6596-2.rs:5:16
12+
|
13+
LL | { $inp $nonexistent }
14+
| ^^^^^^^^^^^^
15+
|
16+
= note: #[deny(meta_variable_misuse)] on by default
17+
18+
error: aborting due to 2 previous errors
1119

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![deny(meta_variable_misuse)]
2+
3+
macro_rules! foo {
4+
() => {};
5+
( $( $i:ident = $($j:ident),+ );* ) => { $( $( $i = $j; )* )* };
6+
//~^ ERROR meta-variable repeats with
7+
( $( $($j:ident),+ );* ) => { $( $( $j; )+ )+ }; //~ERROR meta-variable repeats with
8+
}
9+
10+
macro_rules! bar {
11+
() => {};
12+
(test) => {
13+
macro_rules! nested {
14+
() => {};
15+
( $( $i:ident = $($j:ident),+ );* ) => { $( $( $i = $j; )* )* };
16+
//~^ ERROR meta-variable repeats with
17+
( $( $($j:ident),+ );* ) => { $( $( $j; )+ )+ }; //~ERROR meta-variable repeats with
18+
}
19+
};
20+
( $( $i:ident = $($j:ident),+ );* ) => {
21+
$(macro_rules! $i {
22+
() => { 0 $( + $j )* }; //~ ERROR meta-variable repeats with
23+
})*
24+
};
25+
}
26+
27+
fn main() {
28+
foo!();
29+
bar!();
30+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
error: meta-variable repeats with different Kleene operator
2+
--> $DIR/issue-61053-different-kleene.rs:5:57
3+
|
4+
LL | ( $( $i:ident = $($j:ident),+ );* ) => { $( $( $i = $j; )* )* };
5+
| - expected repetition ^^ - conflicting repetition
6+
|
7+
note: lint level defined here
8+
--> $DIR/issue-61053-different-kleene.rs:1:9
9+
|
10+
LL | #![deny(meta_variable_misuse)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: meta-variable repeats with different Kleene operator
14+
--> $DIR/issue-61053-different-kleene.rs:7:41
15+
|
16+
LL | ( $( $($j:ident),+ );* ) => { $( $( $j; )+ )+ };
17+
| - ^^ - conflicting repetition
18+
| |
19+
| expected repetition
20+
21+
error: meta-variable repeats with different Kleene operator
22+
--> $DIR/issue-61053-different-kleene.rs:15:65
23+
|
24+
LL | ( $( $i:ident = $($j:ident),+ );* ) => { $( $( $i = $j; )* )* };
25+
| - expected repetition ^^ - conflicting repetition
26+
27+
error: meta-variable repeats with different Kleene operator
28+
--> $DIR/issue-61053-different-kleene.rs:17:49
29+
|
30+
LL | ( $( $($j:ident),+ );* ) => { $( $( $j; )+ )+ };
31+
| - ^^ - conflicting repetition
32+
| |
33+
| expected repetition
34+
35+
error: meta-variable repeats with different Kleene operator
36+
--> $DIR/issue-61053-different-kleene.rs:22:28
37+
|
38+
LL | ( $( $i:ident = $($j:ident),+ );* ) => {
39+
| - expected repetition
40+
LL | $(macro_rules! $i {
41+
LL | () => { 0 $( + $j )* };
42+
| ^^ - conflicting repetition
43+
44+
error: aborting due to 5 previous errors
45+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![deny(meta_variable_misuse)]
2+
3+
macro_rules! foo {
4+
() => {};
5+
(error) => {
6+
macro_rules! bar {
7+
($x:tt $x:tt) => { $x }; //~ ERROR duplicate matcher binding
8+
}
9+
};
10+
}
11+
12+
fn main() {
13+
foo!();
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: duplicate matcher binding
2+
--> $DIR/issue-61053-duplicate-binder.rs:7:20
3+
|
4+
LL | ($x:tt $x:tt) => { $x };
5+
| -- ^^
6+
| |
7+
| previous declaration
8+
|
9+
note: lint level defined here
10+
--> $DIR/issue-61053-duplicate-binder.rs:1:9
11+
|
12+
LL | #![deny(meta_variable_misuse)]
13+
| ^^^^^^^^^^^^^^^^^^^^
14+
15+
error: aborting due to previous error
16+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![deny(meta_variable_misuse)]
2+
3+
macro_rules! foo {
4+
() => {};
5+
($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* };
6+
//~^ ERROR variable 'j' is still repeating
7+
}
8+
9+
macro_rules! bar {
10+
() => {};
11+
(test) => {
12+
macro_rules! nested {
13+
() => {};
14+
($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* };
15+
//~^ ERROR variable 'j' is still repeating
16+
}
17+
};
18+
( $( $i:ident = $($j:ident),+ );* ) => {
19+
$(macro_rules! $i {
20+
() => { $j }; //~ ERROR variable 'j' is still repeating
21+
})*
22+
};
23+
}
24+
25+
fn main() {
26+
foo!();
27+
bar!();
28+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: variable 'j' is still repeating at this depth
2+
--> $DIR/issue-61053-missing-repetition.rs:5:52
3+
|
4+
LL | ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* };
5+
| - ^^
6+
| |
7+
| expected repetition
8+
|
9+
note: lint level defined here
10+
--> $DIR/issue-61053-missing-repetition.rs:1:9
11+
|
12+
LL | #![deny(meta_variable_misuse)]
13+
| ^^^^^^^^^^^^^^^^^^^^
14+
15+
error: variable 'j' is still repeating at this depth
16+
--> $DIR/issue-61053-missing-repetition.rs:14:60
17+
|
18+
LL | ($( $i:ident = $($j:ident),+ );*) => { $( $i = $j; )* };
19+
| - ^^
20+
| |
21+
| expected repetition
22+
23+
error: variable 'j' is still repeating at this depth
24+
--> $DIR/issue-61053-missing-repetition.rs:20:21
25+
|
26+
LL | ( $( $i:ident = $($j:ident),+ );* ) => {
27+
| - expected repetition
28+
LL | $(macro_rules! $i {
29+
LL | () => { $j };
30+
| ^^
31+
32+
error: aborting due to 3 previous errors
33+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![deny(meta_variable_misuse)]
2+
3+
macro_rules! foo {
4+
() => {};
5+
($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* };
6+
//~^ ERROR unknown macro variable
7+
}
8+
9+
macro_rules! bar {
10+
() => {};
11+
(test) => {
12+
macro_rules! nested {
13+
() => {};
14+
($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* };
15+
//~^ ERROR unknown macro variable
16+
}
17+
};
18+
( $( $i:ident = $($j:ident),+ );* ) => {
19+
$(macro_rules! $i {
20+
() => { $( $i = $k)+ }; //~ ERROR unknown macro variable
21+
})*
22+
};
23+
}
24+
25+
fn main() {
26+
foo!();
27+
bar!();
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: unknown macro variable `k`
2+
--> $DIR/issue-61053-unbound.rs:5:55
3+
|
4+
LL | ($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* };
5+
| ^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/issue-61053-unbound.rs:1:9
9+
|
10+
LL | #![deny(meta_variable_misuse)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: unknown macro variable `k`
14+
--> $DIR/issue-61053-unbound.rs:14:63
15+
|
16+
LL | ($( $i:ident = $($j:ident),+ );*) => { $( $( $i = $k; )+ )* };
17+
| ^^
18+
19+
error: unknown macro variable `k`
20+
--> $DIR/issue-61053-unbound.rs:20:29
21+
|
22+
LL | () => { $( $i = $k)+ };
23+
| ^^
24+
25+
error: aborting due to 3 previous errors
26+

0 commit comments

Comments
 (0)
Please sign in to comment.