Skip to content

Commit 618f5a0

Browse files
committed
Auto merge of #57617 - mark-i-m:multiple-matcher-bindings, r=petrochenkov
Error on duplicate matcher bindings fix #57593 This should not be merged without a crater run and maybe an FCP. Discussion is ongoing at #57593. TODO: - [x] write tests - [x] crater run - [x] ~maybe need edition gating?~ not for 1 regression /centril r? @petrochenkov
2 parents 312f382 + c25d6b8 commit 618f5a0

File tree

10 files changed

+171
-38
lines changed

10 files changed

+171
-38
lines changed

src/librustc/lint/builtin.rs

+6
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,12 @@ declare_lint! {
352352
"outlives requirements can be inferred"
353353
}
354354

355+
declare_lint! {
356+
pub DUPLICATE_MATCHER_BINDING_NAME,
357+
Warn,
358+
"duplicate macro matcher binding name"
359+
}
360+
355361
/// Some lints that are buffered from `libsyntax`. See `syntax::early_buffered_lints`.
356362
pub mod parser {
357363
declare_lint! {

src/librustc/lint/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::errors::{DiagnosticBuilder, DiagnosticId};
2727
use crate::hir::def_id::{CrateNum, LOCAL_CRATE};
2828
use crate::hir::intravisit;
2929
use crate::hir;
30-
use crate::lint::builtin::BuiltinLintDiagnostics;
30+
use crate::lint::builtin::{BuiltinLintDiagnostics, DUPLICATE_MATCHER_BINDING_NAME};
3131
use crate::lint::builtin::parser::{QUESTION_MARK_MACRO_SEP, ILL_FORMED_ATTRIBUTE_INPUT};
3232
use crate::session::{Session, DiagnosticMessageId};
3333
use std::{hash, ptr};
@@ -82,6 +82,7 @@ impl Lint {
8282
match lint_id {
8383
BufferedEarlyLintId::QuestionMarkMacroSep => QUESTION_MARK_MACRO_SEP,
8484
BufferedEarlyLintId::IllFormedAttributeInput => ILL_FORMED_ATTRIBUTE_INPUT,
85+
BufferedEarlyLintId::DuplicateMacroMatcherBindingName => DUPLICATE_MATCHER_BINDING_NAME,
8586
}
8687
}
8788

src/librustc_lint/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
348348
reference: "issue #57644 <https://github.com/rust-lang/rust/issues/57644>",
349349
edition: None,
350350
},
351+
FutureIncompatibleInfo {
352+
id: LintId::of(DUPLICATE_MATCHER_BINDING_NAME),
353+
reference: "issue #57593 <https://github.com/rust-lang/rust/issues/57593>",
354+
edition: None,
355+
},
351356
]);
352357

353358
// Register renamed and removed lints.

src/libsyntax/early_buffered_lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub enum BufferedEarlyLintId {
1212
/// Usage of `?` as a macro separator is deprecated.
1313
QuestionMarkMacroSep,
1414
IllFormedAttributeInput,
15+
/// Usage of a duplicate macro matcher binding name.
16+
DuplicateMacroMatcherBindingName,
1517
}
1618

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

src/libsyntax/ext/tt/macro_rules.rs

+62-5
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use crate::parse::token::Token::*;
1717
use crate::symbol::Symbol;
1818
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
1919

20-
use syntax_pos::{Span, DUMMY_SP};
20+
use syntax_pos::{Span, DUMMY_SP, symbol::Ident};
2121
use log::debug;
2222

23-
use rustc_data_structures::fx::FxHashMap;
23+
use rustc_data_structures::fx::{FxHashMap};
2424
use std::borrow::Cow;
2525
use std::collections::hash_map::Entry;
2626

@@ -246,8 +246,12 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
246246
// Holy self-referential!
247247

248248
/// Converts a `macro_rules!` invocation into a syntax extension.
249-
pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: Edition)
250-
-> SyntaxExtension {
249+
pub fn compile(
250+
sess: &ParseSess,
251+
features: &Features,
252+
def: &ast::Item,
253+
edition: Edition
254+
) -> SyntaxExtension {
251255
let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
252256
let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
253257

@@ -355,7 +359,13 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
355359

356360
// don't abort iteration early, so that errors for multiple lhses can be reported
357361
for lhs in &lhses {
358-
valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()])
362+
valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()]);
363+
valid &= check_lhs_duplicate_matcher_bindings(
364+
sess,
365+
&[lhs.clone()],
366+
&mut FxHashMap::default(),
367+
def.id
368+
);
359369
}
360370

361371
let expander: Box<_> = Box::new(MacroRulesMacroExpander {
@@ -456,6 +466,53 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
456466
true
457467
}
458468

469+
/// Check that the LHS contains no duplicate matcher bindings. e.g. `$a:expr, $a:expr` would be
470+
/// illegal, since it would be ambiguous which `$a` to use if we ever needed to.
471+
fn check_lhs_duplicate_matcher_bindings(
472+
sess: &ParseSess,
473+
tts: &[quoted::TokenTree],
474+
metavar_names: &mut FxHashMap<Ident, Span>,
475+
node_id: ast::NodeId,
476+
) -> bool {
477+
use self::quoted::TokenTree;
478+
use crate::early_buffered_lints::BufferedEarlyLintId;
479+
for tt in tts {
480+
match *tt {
481+
TokenTree::MetaVarDecl(span, name, _kind) => {
482+
if let Some(&prev_span) = metavar_names.get(&name) {
483+
// FIXME(mark-i-m): in a few cycles, make this a hard error.
484+
// sess.span_diagnostic
485+
// .struct_span_err(span, "duplicate matcher binding")
486+
// .span_note(prev_span, "previous declaration was here")
487+
// .emit();
488+
sess.buffer_lint(
489+
BufferedEarlyLintId::DuplicateMacroMatcherBindingName,
490+
crate::source_map::MultiSpan::from(vec![prev_span, span]),
491+
node_id,
492+
"duplicate matcher binding"
493+
);
494+
return false;
495+
} else {
496+
metavar_names.insert(name, span);
497+
}
498+
}
499+
TokenTree::Delimited(_, ref del) => {
500+
if !check_lhs_duplicate_matcher_bindings(sess, &del.tts, metavar_names, node_id) {
501+
return false;
502+
}
503+
},
504+
TokenTree::Sequence(_, ref seq) => {
505+
if !check_lhs_duplicate_matcher_bindings(sess, &seq.tts, metavar_names, node_id) {
506+
return false;
507+
}
508+
}
509+
_ => {}
510+
}
511+
}
512+
513+
true
514+
}
515+
459516
fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
460517
match *rhs {
461518
quoted::TokenTree::Delimited(..) => return true,

src/test/run-pass/macros/macro-follow.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ macro_rules! follow_block {
7373
($b:block $t:ty) => {};
7474
($b:block $s:stmt) => {};
7575
($b:block $p:path) => {};
76-
($b:block $b:block) => {};
76+
($b:block $c:block) => {};
7777
($b:block $i:ident) => {};
7878
($b:block $t:tt) => {};
7979
($b:block $i:item) => {};
@@ -99,9 +99,9 @@ macro_rules! follow_ident {
9999
($i:ident $s:stmt) => {};
100100
($i:ident $p:path) => {};
101101
($i:ident $b:block) => {};
102-
($i:ident $i:ident) => {};
102+
($i:ident $j:ident) => {};
103103
($i:ident $t:tt) => {};
104-
($i:ident $i:item) => {};
104+
($i:ident $j:item) => {};
105105
($i:ident $m:meta) => {};
106106
}
107107
// FOLLOW(tt) = any token
@@ -120,12 +120,12 @@ macro_rules! follow_tt {
120120
($t:tt ident) => {};
121121
($t:tt $p:pat) => {};
122122
($t:tt $e:expr) => {};
123-
($t:tt $t:ty) => {};
123+
($t:tt $v:ty) => {};
124124
($t:tt $s:stmt) => {};
125125
($t:tt $p:path) => {};
126126
($t:tt $b:block) => {};
127127
($t:tt $i:ident) => {};
128-
($t:tt $t:tt) => {};
128+
($t:tt $v:tt) => {};
129129
($t:tt $i:item) => {};
130130
($t:tt $m:meta) => {};
131131
}
@@ -149,9 +149,9 @@ macro_rules! follow_item {
149149
($i:item $s:stmt) => {};
150150
($i:item $p:path) => {};
151151
($i:item $b:block) => {};
152-
($i:item $i:ident) => {};
152+
($i:item $j:ident) => {};
153153
($i:item $t:tt) => {};
154-
($i:item $i:item) => {};
154+
($i:item $j:item) => {};
155155
($i:item $m:meta) => {};
156156
}
157157
// FOLLOW(meta) = any token
@@ -177,7 +177,7 @@ macro_rules! follow_meta {
177177
($m:meta $i:ident) => {};
178178
($m:meta $t:tt) => {};
179179
($m:meta $i:item) => {};
180-
($m:meta $m:meta) => {};
180+
($m:meta $n:meta) => {};
181181
}
182182

183183
fn main() {}

src/test/ui/macros/macro-follow.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ macro_rules! follow_pat {
1212
($p:pat >) => {}; //~ERROR `$p:pat` is followed by `>`
1313
($p:pat +) => {}; //~ERROR `$p:pat` is followed by `+`
1414
($p:pat ident) => {}; //~ERROR `$p:pat` is followed by `ident`
15-
($p:pat $p:pat) => {}; //~ERROR `$p:pat` is followed by `$p:pat`
15+
($p:pat $q:pat) => {}; //~ERROR `$p:pat` is followed by `$q:pat`
1616
($p:pat $e:expr) => {}; //~ERROR `$p:pat` is followed by `$e:expr`
1717
($p:pat $t:ty) => {}; //~ERROR `$p:pat` is followed by `$t:ty`
1818
($p:pat $s:stmt) => {}; //~ERROR `$p:pat` is followed by `$s:stmt`
19-
($p:pat $p:path) => {}; //~ERROR `$p:pat` is followed by `$p:path`
19+
($p:pat $q:path) => {}; //~ERROR `$p:pat` is followed by `$q:path`
2020
($p:pat $b:block) => {}; //~ERROR `$p:pat` is followed by `$b:block`
2121
($p:pat $i:ident) => {}; //~ERROR `$p:pat` is followed by `$i:ident`
2222
($p:pat $t:tt) => {}; //~ERROR `$p:pat` is followed by `$t:tt`
@@ -37,7 +37,7 @@ macro_rules! follow_expr {
3737
($e:expr if) => {}; //~ERROR `$e:expr` is followed by `if`
3838
($e:expr in) => {}; //~ERROR `$e:expr` is followed by `in`
3939
($e:expr $p:pat) => {}; //~ERROR `$e:expr` is followed by `$p:pat`
40-
($e:expr $e:expr) => {}; //~ERROR `$e:expr` is followed by `$e:expr`
40+
($e:expr $f:expr) => {}; //~ERROR `$e:expr` is followed by `$f:expr`
4141
($e:expr $t:ty) => {}; //~ERROR `$e:expr` is followed by `$t:ty`
4242
($e:expr $s:stmt) => {}; //~ERROR `$e:expr` is followed by `$s:stmt`
4343
($e:expr $p:path) => {}; //~ERROR `$e:expr` is followed by `$p:path`
@@ -57,12 +57,12 @@ macro_rules! follow_ty {
5757
($t:ty if) => {}; //~ERROR `$t:ty` is followed by `if`
5858
($t:ty $p:pat) => {}; //~ERROR `$t:ty` is followed by `$p:pat`
5959
($t:ty $e:expr) => {}; //~ERROR `$t:ty` is followed by `$e:expr`
60-
($t:ty $t:ty) => {}; //~ERROR `$t:ty` is followed by `$t:ty`
60+
($t:ty $r:ty) => {}; //~ERROR `$t:ty` is followed by `$r:ty`
6161
($t:ty $s:stmt) => {}; //~ERROR `$t:ty` is followed by `$s:stmt`
6262
($t:ty $p:path) => {}; //~ERROR `$t:ty` is followed by `$p:path`
6363
($t:ty $b:block) => {}; // ok (RFC 1494)
6464
($t:ty $i:ident) => {}; //~ERROR `$t:ty` is followed by `$i:ident`
65-
($t:ty $t:tt) => {}; //~ERROR `$t:ty` is followed by `$t:tt`
65+
($t:ty $r:tt) => {}; //~ERROR `$t:ty` is followed by `$r:tt`
6666
($t:ty $i:item) => {}; //~ERROR `$t:ty` is followed by `$i:item`
6767
($t:ty $m:meta) => {}; //~ERROR `$t:ty` is followed by `$m:meta`
6868
}
@@ -82,7 +82,7 @@ macro_rules! follow_stmt {
8282
($s:stmt $p:pat) => {}; //~ERROR `$s:stmt` is followed by `$p:pat`
8383
($s:stmt $e:expr) => {}; //~ERROR `$s:stmt` is followed by `$e:expr`
8484
($s:stmt $t:ty) => {}; //~ERROR `$s:stmt` is followed by `$t:ty`
85-
($s:stmt $s:stmt) => {}; //~ERROR `$s:stmt` is followed by `$s:stmt`
85+
($s:stmt $t:stmt) => {}; //~ERROR `$s:stmt` is followed by `$t:stmt`
8686
($s:stmt $p:path) => {}; //~ERROR `$s:stmt` is followed by `$p:path`
8787
($s:stmt $b:block) => {}; //~ERROR `$s:stmt` is followed by `$b:block`
8888
($s:stmt $i:ident) => {}; //~ERROR `$s:stmt` is followed by `$i:ident`
@@ -97,11 +97,11 @@ macro_rules! follow_path {
9797
($p:path +) => {}; //~ERROR `$p:path` is followed by `+`
9898
($p:path ident) => {}; //~ERROR `$p:path` is followed by `ident`
9999
($p:path if) => {}; //~ERROR `$p:path` is followed by `if`
100-
($p:path $p:pat) => {}; //~ERROR `$p:path` is followed by `$p:pat`
100+
($p:path $q:pat) => {}; //~ERROR `$p:path` is followed by `$q:pat`
101101
($p:path $e:expr) => {}; //~ERROR `$p:path` is followed by `$e:expr`
102102
($p:path $t:ty) => {}; //~ERROR `$p:path` is followed by `$t:ty`
103103
($p:path $s:stmt) => {}; //~ERROR `$p:path` is followed by `$s:stmt`
104-
($p:path $p:path) => {}; //~ERROR `$p:path` is followed by `$p:path`
104+
($p:path $q:path) => {}; //~ERROR `$p:path` is followed by `$q:path`
105105
($p:path $b:block) => {}; // ok (RFC 1494)
106106
($p:path $i:ident) => {}; //~ERROR `$p:path` is followed by `$i:ident`
107107
($p:path $t:tt) => {}; //~ERROR `$p:path` is followed by `$t:tt`

src/test/ui/macros/macro-follow.stderr

+16-16
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ LL | ($p:pat ident) => {}; //~ERROR `$p:pat` is followed by `ident`
5454
|
5555
= note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
5656

57-
error: `$p:pat` is followed by `$p:pat`, which is not allowed for `pat` fragments
57+
error: `$p:pat` is followed by `$q:pat`, which is not allowed for `pat` fragments
5858
--> $DIR/macro-follow.rs:15:13
5959
|
60-
LL | ($p:pat $p:pat) => {}; //~ERROR `$p:pat` is followed by `$p:pat`
60+
LL | ($p:pat $q:pat) => {}; //~ERROR `$p:pat` is followed by `$q:pat`
6161
| ^^^^^^ not allowed after `pat` fragments
6262
|
6363
= note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
@@ -86,10 +86,10 @@ LL | ($p:pat $s:stmt) => {}; //~ERROR `$p:pat` is followed by `$s:stmt`
8686
|
8787
= note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
8888

89-
error: `$p:pat` is followed by `$p:path`, which is not allowed for `pat` fragments
89+
error: `$p:pat` is followed by `$q:path`, which is not allowed for `pat` fragments
9090
--> $DIR/macro-follow.rs:19:13
9191
|
92-
LL | ($p:pat $p:path) => {}; //~ERROR `$p:pat` is followed by `$p:path`
92+
LL | ($p:pat $q:path) => {}; //~ERROR `$p:pat` is followed by `$q:path`
9393
| ^^^^^^^ not allowed after `pat` fragments
9494
|
9595
= note: allowed there are: `=>`, `,`, `=`, `|`, `if` or `in`
@@ -230,10 +230,10 @@ LL | ($e:expr $p:pat) => {}; //~ERROR `$e:expr` is followed by `$p:pat`
230230
|
231231
= note: allowed there are: `=>`, `,` or `;`
232232

233-
error: `$e:expr` is followed by `$e:expr`, which is not allowed for `expr` fragments
233+
error: `$e:expr` is followed by `$f:expr`, which is not allowed for `expr` fragments
234234
--> $DIR/macro-follow.rs:40:14
235235
|
236-
LL | ($e:expr $e:expr) => {}; //~ERROR `$e:expr` is followed by `$e:expr`
236+
LL | ($e:expr $f:expr) => {}; //~ERROR `$e:expr` is followed by `$f:expr`
237237
| ^^^^^^^ not allowed after `expr` fragments
238238
|
239239
= note: allowed there are: `=>`, `,` or `;`
@@ -350,10 +350,10 @@ LL | ($t:ty $e:expr) => {}; //~ERROR `$t:ty` is followed by `$e:expr`
350350
|
351351
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
352352

353-
error: `$t:ty` is followed by `$t:ty`, which is not allowed for `ty` fragments
353+
error: `$t:ty` is followed by `$r:ty`, which is not allowed for `ty` fragments
354354
--> $DIR/macro-follow.rs:60:12
355355
|
356-
LL | ($t:ty $t:ty) => {}; //~ERROR `$t:ty` is followed by `$t:ty`
356+
LL | ($t:ty $r:ty) => {}; //~ERROR `$t:ty` is followed by `$r:ty`
357357
| ^^^^^ not allowed after `ty` fragments
358358
|
359359
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
@@ -382,10 +382,10 @@ LL | ($t:ty $i:ident) => {}; //~ERROR `$t:ty` is followed by `$i:ident`
382382
|
383383
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
384384

385-
error: `$t:ty` is followed by `$t:tt`, which is not allowed for `ty` fragments
385+
error: `$t:ty` is followed by `$r:tt`, which is not allowed for `ty` fragments
386386
--> $DIR/macro-follow.rs:65:12
387387
|
388-
LL | ($t:ty $t:tt) => {}; //~ERROR `$t:ty` is followed by `$t:tt`
388+
LL | ($t:ty $r:tt) => {}; //~ERROR `$t:ty` is followed by `$r:tt`
389389
| ^^^^^ not allowed after `ty` fragments
390390
|
391391
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
@@ -518,10 +518,10 @@ LL | ($s:stmt $t:ty) => {}; //~ERROR `$s:stmt` is followed by `$t:ty`
518518
|
519519
= note: allowed there are: `=>`, `,` or `;`
520520

521-
error: `$s:stmt` is followed by `$s:stmt`, which is not allowed for `stmt` fragments
521+
error: `$s:stmt` is followed by `$t:stmt`, which is not allowed for `stmt` fragments
522522
--> $DIR/macro-follow.rs:85:14
523523
|
524-
LL | ($s:stmt $s:stmt) => {}; //~ERROR `$s:stmt` is followed by `$s:stmt`
524+
LL | ($s:stmt $t:stmt) => {}; //~ERROR `$s:stmt` is followed by `$t:stmt`
525525
| ^^^^^^^ not allowed after `stmt` fragments
526526
|
527527
= note: allowed there are: `=>`, `,` or `;`
@@ -606,10 +606,10 @@ LL | ($p:path if) => {}; //~ERROR `$p:path` is followed by `if`
606606
|
607607
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
608608

609-
error: `$p:path` is followed by `$p:pat`, which is not allowed for `path` fragments
609+
error: `$p:path` is followed by `$q:pat`, which is not allowed for `path` fragments
610610
--> $DIR/macro-follow.rs:100:14
611611
|
612-
LL | ($p:path $p:pat) => {}; //~ERROR `$p:path` is followed by `$p:pat`
612+
LL | ($p:path $q:pat) => {}; //~ERROR `$p:path` is followed by `$q:pat`
613613
| ^^^^^^ not allowed after `path` fragments
614614
|
615615
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
@@ -638,10 +638,10 @@ LL | ($p:path $s:stmt) => {}; //~ERROR `$p:path` is followed by `$s:stmt`
638638
|
639639
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
640640

641-
error: `$p:path` is followed by `$p:path`, which is not allowed for `path` fragments
641+
error: `$p:path` is followed by `$q:path`, which is not allowed for `path` fragments
642642
--> $DIR/macro-follow.rs:104:14
643643
|
644-
LL | ($p:path $p:path) => {}; //~ERROR `$p:path` is followed by `$p:path`
644+
LL | ($p:path $q:path) => {}; //~ERROR `$p:path` is followed by `$q:path`
645645
| ^^^^^^^ not allowed after `path` fragments
646646
|
647647
= note: allowed there are: `{`, `[`, `=>`, `,`, `>`, `=`, `:`, `;`, `|`, `as` or `where`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Test that duplicate matcher binding names are caught at declaration time, rather than at macro
2+
// invocation time.
3+
//
4+
// FIXME(mark-i-m): Update this when it becomes a hard error.
5+
6+
// compile-pass
7+
8+
#![allow(unused_macros)]
9+
10+
macro_rules! foo1 {
11+
($a:ident, $a:ident) => {}; //~WARNING duplicate matcher binding
12+
($a:ident, $a:path) => {}; //~WARNING duplicate matcher binding
13+
}
14+
15+
macro_rules! foo2 {
16+
($a:ident) => {}; // OK
17+
($a:path) => {}; // OK
18+
}
19+
20+
macro_rules! foo3 {
21+
($a:ident, $($a:ident),*) => {}; //~WARNING duplicate matcher binding
22+
($($a:ident)+ # $($($a:path),+);*) => {}; //~WARNING duplicate matcher binding
23+
}
24+
25+
fn main() {}

0 commit comments

Comments
 (0)