Skip to content

Commit 61a9a14

Browse files
committed
Add warning cycle.
1 parent 7f822c8 commit 61a9a14

File tree

11 files changed

+71
-13
lines changed

11 files changed

+71
-13
lines changed

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ declare_lint! {
236236
"detects use of struct constructors that would be invisible with new visibility rules"
237237
}
238238

239+
declare_lint! {
240+
pub MISSING_FRAGMENT_SPECIFIER,
241+
Warn,
242+
"detects missing fragment specifiers in unused `macro_rules!` patterns"
243+
}
244+
239245
declare_lint! {
240246
pub DEPRECATED,
241247
Warn,
@@ -286,6 +292,7 @@ impl LintPass for HardwiredLints {
286292
LEGACY_DIRECTORY_OWNERSHIP,
287293
LEGACY_IMPORTS,
288294
LEGACY_CONSTRUCTOR_VISIBILITY,
295+
MISSING_FRAGMENT_SPECIFIER,
289296
DEPRECATED
290297
)
291298
}

src/librustc_driver/driver.rs

+8
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,14 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
688688

689689
let krate = ecx.monotonic_expander().expand_crate(krate);
690690

691+
let mut missing_fragment_specifiers: Vec<_> =
692+
ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect();
693+
missing_fragment_specifiers.sort();
694+
for span in missing_fragment_specifiers {
695+
let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER;
696+
let msg = "missing fragment specifier".to_string();
697+
sess.add_lint(lint, ast::CRATE_NODE_ID, span, msg);
698+
}
691699
if ecx.parse_sess.span_diagnostic.err_count() - ecx.resolve_err_count > err_count {
692700
ecx.parse_sess.span_diagnostic.abort_if_errors();
693701
}

src/librustc_lint/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
247247
id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY),
248248
reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>",
249249
},
250+
FutureIncompatibleInfo {
251+
id: LintId::of(MISSING_FRAGMENT_SPECIFIER),
252+
reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
253+
},
250254
]);
251255

252256
// Register renamed and removed lints

src/libsyntax/ext/tt/macro_parser.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ use parse::{Directory, ParseSess};
8787
use parse::parser::{PathStyle, Parser};
8888
use parse::token::{self, DocComment, Token, Nonterminal};
8989
use print::pprust;
90+
use symbol::keywords;
9091
use tokenstream::TokenTree;
9192
use util::small_vector::SmallVector;
9293

@@ -201,22 +202,27 @@ pub enum NamedMatch {
201202
MatchedNonterminal(Rc<Nonterminal>)
202203
}
203204

204-
fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(ms: &[quoted::TokenTree], mut res: I)
205+
fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(sess: &ParseSess, ms: &[quoted::TokenTree], mut res: I)
205206
-> NamedParseResult {
206207
use self::quoted::TokenTree;
207208

208-
fn n_rec<I: Iterator<Item=Rc<NamedMatch>>>(m: &TokenTree, mut res: &mut I,
209+
fn n_rec<I: Iterator<Item=Rc<NamedMatch>>>(sess: &ParseSess, m: &TokenTree, mut res: &mut I,
209210
ret_val: &mut HashMap<Ident, Rc<NamedMatch>>)
210211
-> Result<(), (syntax_pos::Span, String)> {
211212
match *m {
212213
TokenTree::Sequence(_, ref seq) => {
213214
for next_m in &seq.tts {
214-
n_rec(next_m, res.by_ref(), ret_val)?
215+
n_rec(sess, next_m, res.by_ref(), ret_val)?
215216
}
216217
}
217218
TokenTree::Delimited(_, ref delim) => {
218219
for next_m in &delim.tts {
219-
n_rec(next_m, res.by_ref(), ret_val)?;
220+
n_rec(sess, next_m, res.by_ref(), ret_val)?;
221+
}
222+
}
223+
TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => {
224+
if sess.missing_fragment_specifiers.borrow_mut().remove(&span) {
225+
return Err((span, "missing fragment specifier".to_string()));
220226
}
221227
}
222228
TokenTree::MetaVarDecl(sp, bind_name, _) => {
@@ -237,7 +243,7 @@ fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(ms: &[quoted::TokenTree], mut res:
237243

238244
let mut ret_val = HashMap::new();
239245
for m in ms {
240-
match n_rec(m, res.by_ref(), &mut ret_val) {
246+
match n_rec(sess, m, res.by_ref(), &mut ret_val) {
241247
Ok(_) => {},
242248
Err((sp, msg)) => return Error(sp, msg),
243249
}
@@ -277,11 +283,13 @@ fn create_matches(len: usize) -> Vec<Vec<Rc<NamedMatch>>> {
277283
(0..len).into_iter().map(|_| Vec::new()).collect()
278284
}
279285

280-
fn inner_parse_loop(cur_eis: &mut SmallVector<Box<MatcherPos>>,
286+
fn inner_parse_loop(sess: &ParseSess,
287+
cur_eis: &mut SmallVector<Box<MatcherPos>>,
281288
next_eis: &mut Vec<Box<MatcherPos>>,
282289
eof_eis: &mut SmallVector<Box<MatcherPos>>,
283290
bb_eis: &mut SmallVector<Box<MatcherPos>>,
284-
token: &Token, span: &syntax_pos::Span) -> ParseResult<()> {
291+
token: &Token,
292+
span: &syntax_pos::Span) -> ParseResult<()> {
285293
use self::quoted::TokenTree;
286294

287295
while let Some(mut ei) = cur_eis.pop() {
@@ -375,6 +383,11 @@ fn inner_parse_loop(cur_eis: &mut SmallVector<Box<MatcherPos>>,
375383
top_elts: Tt(TokenTree::Sequence(sp, seq)),
376384
}));
377385
}
386+
TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => {
387+
if sess.missing_fragment_specifiers.borrow_mut().remove(&span) {
388+
return Error(span, "missing fragment specifier".to_string());
389+
}
390+
}
378391
TokenTree::MetaVarDecl(..) => {
379392
// Built-in nonterminals never start with these tokens,
380393
// so we can eliminate them from consideration.
@@ -422,7 +435,7 @@ pub fn parse(sess: &ParseSess,
422435
let mut eof_eis = SmallVector::new();
423436
assert!(next_eis.is_empty());
424437

425-
match inner_parse_loop(&mut cur_eis, &mut next_eis, &mut eof_eis, &mut bb_eis,
438+
match inner_parse_loop(sess, &mut cur_eis, &mut next_eis, &mut eof_eis, &mut bb_eis,
426439
&parser.token, &parser.span) {
427440
Success(_) => {},
428441
Failure(sp, tok) => return Failure(sp, tok),
@@ -435,7 +448,8 @@ pub fn parse(sess: &ParseSess,
435448
/* error messages here could be improved with links to orig. rules */
436449
if token_name_eq(&parser.token, &token::Eof) {
437450
if eof_eis.len() == 1 {
438-
return nameize(ms, eof_eis[0].matches.iter_mut().map(|mut dv| dv.pop().unwrap()));
451+
let matches = eof_eis[0].matches.iter_mut().map(|mut dv| dv.pop().unwrap());
452+
return nameize(sess, ms, matches);
439453
} else if eof_eis.len() > 1 {
440454
return Error(parser.span, "ambiguity: multiple successful parses".to_string());
441455
} else {

src/libsyntax/ext/tt/macro_rules.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
788788
// harmless
789789
Ok(true)
790790
},
791+
"" => Ok(true), // keywords::Invalid
791792
_ => Err((format!("invalid fragment specifier `{}`", frag),
792793
"valid fragment specifiers are `ident`, `block`, \
793794
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
@@ -810,7 +811,7 @@ fn has_legal_fragment_specifier(tok: &quoted::TokenTree) -> Result<(), String> {
810811
fn is_legal_fragment_specifier(frag: &str) -> bool {
811812
match frag {
812813
"item" | "block" | "stmt" | "expr" | "pat" |
813-
"path" | "ty" | "ident" | "meta" | "tt" => true,
814+
"path" | "ty" | "ident" | "meta" | "tt" | "" => true,
814815
_ => false,
815816
}
816817
}

src/libsyntax/ext/tt/quoted.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ pub fn parse(input: &[tokenstream::TokenTree], expect_matchers: bool, sess: &Par
143143
},
144144
tree @ _ => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp),
145145
};
146-
sess.span_diagnostic.span_err(span, "missing fragment specifier");
146+
sess.missing_fragment_specifiers.borrow_mut().insert(span);
147+
result.push(TokenTree::MetaVarDecl(span, ident, keywords::Invalid.ident()));
147148
}
148149
_ => result.push(tree),
149150
}

src/libsyntax/parse/lexer/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,7 @@ mod tests {
16931693
use feature_gate::UnstableFeatures;
16941694
use parse::token;
16951695
use std::cell::RefCell;
1696+
use std::collections::HashSet;
16961697
use std::io;
16971698
use std::rc::Rc;
16981699

@@ -1704,6 +1705,7 @@ mod tests {
17041705
config: CrateConfig::new(),
17051706
included_mod_stack: RefCell::new(Vec::new()),
17061707
code_map: cm,
1708+
missing_fragment_specifiers: RefCell::new(HashSet::new()),
17071709
}
17081710
}
17091711

src/libsyntax/parse/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct ParseSess {
4646
pub span_diagnostic: Handler,
4747
pub unstable_features: UnstableFeatures,
4848
pub config: CrateConfig,
49+
pub missing_fragment_specifiers: RefCell<HashSet<Span>>,
4950
/// Used to determine and report recursive mod inclusions
5051
included_mod_stack: RefCell<Vec<PathBuf>>,
5152
code_map: Rc<CodeMap>,
@@ -66,6 +67,7 @@ impl ParseSess {
6667
span_diagnostic: handler,
6768
unstable_features: UnstableFeatures::from_environment(),
6869
config: HashSet::new(),
70+
missing_fragment_specifiers: RefCell::new(HashSet::new()),
6971
included_mod_stack: RefCell::new(vec![]),
7072
code_map: code_map
7173
}

src/test/compile-fail/issue-39404.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
#![deny(missing_fragment_specifier)] //~ NOTE lint level defined here
12+
13+
macro_rules! m { ($i) => {} }
14+
//~^ ERROR missing fragment specifier
15+
//~| WARN previously accepted
16+
//~| NOTE issue #40107
17+
18+
fn main() {}

src/test/compile-fail/macro-match-nonterminal.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
macro_rules! test { ($a, //~ ERROR missing fragment
12-
$b) => (()); } //~ ERROR missing fragment
11+
macro_rules! test { ($a, $b) => (()); } //~ ERROR missing fragment
1312

1413
fn main() {
1514
test!()

src/test/parse-fail/issue-33569.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ macro_rules! foo {
1616
$(x)(y) //~ ERROR expected `*` or `+`
1717
}
1818
}
19+
20+
foo!();

0 commit comments

Comments
 (0)