Add warning cycle.
This commit is contained in:
parent
7f822c800d
commit
61a9a14d29
|
@ -236,6 +236,12 @@ declare_lint! {
|
||||||
"detects use of struct constructors that would be invisible with new visibility rules"
|
"detects use of struct constructors that would be invisible with new visibility rules"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
pub MISSING_FRAGMENT_SPECIFIER,
|
||||||
|
Warn,
|
||||||
|
"detects missing fragment specifiers in unused `macro_rules!` patterns"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub DEPRECATED,
|
pub DEPRECATED,
|
||||||
Warn,
|
Warn,
|
||||||
|
@ -286,6 +292,7 @@ impl LintPass for HardwiredLints {
|
||||||
LEGACY_DIRECTORY_OWNERSHIP,
|
LEGACY_DIRECTORY_OWNERSHIP,
|
||||||
LEGACY_IMPORTS,
|
LEGACY_IMPORTS,
|
||||||
LEGACY_CONSTRUCTOR_VISIBILITY,
|
LEGACY_CONSTRUCTOR_VISIBILITY,
|
||||||
|
MISSING_FRAGMENT_SPECIFIER,
|
||||||
DEPRECATED
|
DEPRECATED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -688,6 +688,14 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
|
||||||
|
|
||||||
let krate = ecx.monotonic_expander().expand_crate(krate);
|
let krate = ecx.monotonic_expander().expand_crate(krate);
|
||||||
|
|
||||||
|
let mut missing_fragment_specifiers: Vec<_> =
|
||||||
|
ecx.parse_sess.missing_fragment_specifiers.borrow().iter().cloned().collect();
|
||||||
|
missing_fragment_specifiers.sort();
|
||||||
|
for span in missing_fragment_specifiers {
|
||||||
|
let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER;
|
||||||
|
let msg = "missing fragment specifier".to_string();
|
||||||
|
sess.add_lint(lint, ast::CRATE_NODE_ID, span, msg);
|
||||||
|
}
|
||||||
if ecx.parse_sess.span_diagnostic.err_count() - ecx.resolve_err_count > err_count {
|
if ecx.parse_sess.span_diagnostic.err_count() - ecx.resolve_err_count > err_count {
|
||||||
ecx.parse_sess.span_diagnostic.abort_if_errors();
|
ecx.parse_sess.span_diagnostic.abort_if_errors();
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,6 +247,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
||||||
id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY),
|
id: LintId::of(LEGACY_CONSTRUCTOR_VISIBILITY),
|
||||||
reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>",
|
reference: "issue #39207 <https://github.com/rust-lang/rust/issues/39207>",
|
||||||
},
|
},
|
||||||
|
FutureIncompatibleInfo {
|
||||||
|
id: LintId::of(MISSING_FRAGMENT_SPECIFIER),
|
||||||
|
reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Register renamed and removed lints
|
// Register renamed and removed lints
|
||||||
|
|
|
@ -87,6 +87,7 @@ use parse::{Directory, ParseSess};
|
||||||
use parse::parser::{PathStyle, Parser};
|
use parse::parser::{PathStyle, Parser};
|
||||||
use parse::token::{self, DocComment, Token, Nonterminal};
|
use parse::token::{self, DocComment, Token, Nonterminal};
|
||||||
use print::pprust;
|
use print::pprust;
|
||||||
|
use symbol::keywords;
|
||||||
use tokenstream::TokenTree;
|
use tokenstream::TokenTree;
|
||||||
use util::small_vector::SmallVector;
|
use util::small_vector::SmallVector;
|
||||||
|
|
||||||
|
@ -201,22 +202,27 @@ pub enum NamedMatch {
|
||||||
MatchedNonterminal(Rc<Nonterminal>)
|
MatchedNonterminal(Rc<Nonterminal>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(ms: &[quoted::TokenTree], mut res: I)
|
fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(sess: &ParseSess, ms: &[quoted::TokenTree], mut res: I)
|
||||||
-> NamedParseResult {
|
-> NamedParseResult {
|
||||||
use self::quoted::TokenTree;
|
use self::quoted::TokenTree;
|
||||||
|
|
||||||
fn n_rec<I: Iterator<Item=Rc<NamedMatch>>>(m: &TokenTree, mut res: &mut I,
|
fn n_rec<I: Iterator<Item=Rc<NamedMatch>>>(sess: &ParseSess, m: &TokenTree, mut res: &mut I,
|
||||||
ret_val: &mut HashMap<Ident, Rc<NamedMatch>>)
|
ret_val: &mut HashMap<Ident, Rc<NamedMatch>>)
|
||||||
-> Result<(), (syntax_pos::Span, String)> {
|
-> Result<(), (syntax_pos::Span, String)> {
|
||||||
match *m {
|
match *m {
|
||||||
TokenTree::Sequence(_, ref seq) => {
|
TokenTree::Sequence(_, ref seq) => {
|
||||||
for next_m in &seq.tts {
|
for next_m in &seq.tts {
|
||||||
n_rec(next_m, res.by_ref(), ret_val)?
|
n_rec(sess, next_m, res.by_ref(), ret_val)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenTree::Delimited(_, ref delim) => {
|
TokenTree::Delimited(_, ref delim) => {
|
||||||
for next_m in &delim.tts {
|
for next_m in &delim.tts {
|
||||||
n_rec(next_m, res.by_ref(), ret_val)?;
|
n_rec(sess, next_m, res.by_ref(), ret_val)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => {
|
||||||
|
if sess.missing_fragment_specifiers.borrow_mut().remove(&span) {
|
||||||
|
return Err((span, "missing fragment specifier".to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TokenTree::MetaVarDecl(sp, bind_name, _) => {
|
TokenTree::MetaVarDecl(sp, bind_name, _) => {
|
||||||
|
@ -237,7 +243,7 @@ fn nameize<I: Iterator<Item=Rc<NamedMatch>>>(ms: &[quoted::TokenTree], mut res:
|
||||||
|
|
||||||
let mut ret_val = HashMap::new();
|
let mut ret_val = HashMap::new();
|
||||||
for m in ms {
|
for m in ms {
|
||||||
match n_rec(m, res.by_ref(), &mut ret_val) {
|
match n_rec(sess, m, res.by_ref(), &mut ret_val) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err((sp, msg)) => return Error(sp, msg),
|
Err((sp, msg)) => return Error(sp, msg),
|
||||||
}
|
}
|
||||||
|
@ -277,11 +283,13 @@ fn create_matches(len: usize) -> Vec<Vec<Rc<NamedMatch>>> {
|
||||||
(0..len).into_iter().map(|_| Vec::new()).collect()
|
(0..len).into_iter().map(|_| Vec::new()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner_parse_loop(cur_eis: &mut SmallVector<Box<MatcherPos>>,
|
fn inner_parse_loop(sess: &ParseSess,
|
||||||
|
cur_eis: &mut SmallVector<Box<MatcherPos>>,
|
||||||
next_eis: &mut Vec<Box<MatcherPos>>,
|
next_eis: &mut Vec<Box<MatcherPos>>,
|
||||||
eof_eis: &mut SmallVector<Box<MatcherPos>>,
|
eof_eis: &mut SmallVector<Box<MatcherPos>>,
|
||||||
bb_eis: &mut SmallVector<Box<MatcherPos>>,
|
bb_eis: &mut SmallVector<Box<MatcherPos>>,
|
||||||
token: &Token, span: &syntax_pos::Span) -> ParseResult<()> {
|
token: &Token,
|
||||||
|
span: &syntax_pos::Span) -> ParseResult<()> {
|
||||||
use self::quoted::TokenTree;
|
use self::quoted::TokenTree;
|
||||||
|
|
||||||
while let Some(mut ei) = cur_eis.pop() {
|
while let Some(mut ei) = cur_eis.pop() {
|
||||||
|
@ -375,6 +383,11 @@ fn inner_parse_loop(cur_eis: &mut SmallVector<Box<MatcherPos>>,
|
||||||
top_elts: Tt(TokenTree::Sequence(sp, seq)),
|
top_elts: Tt(TokenTree::Sequence(sp, seq)),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
TokenTree::MetaVarDecl(span, _, id) if id.name == keywords::Invalid.name() => {
|
||||||
|
if sess.missing_fragment_specifiers.borrow_mut().remove(&span) {
|
||||||
|
return Error(span, "missing fragment specifier".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
TokenTree::MetaVarDecl(..) => {
|
TokenTree::MetaVarDecl(..) => {
|
||||||
// Built-in nonterminals never start with these tokens,
|
// Built-in nonterminals never start with these tokens,
|
||||||
// so we can eliminate them from consideration.
|
// so we can eliminate them from consideration.
|
||||||
|
@ -422,7 +435,7 @@ pub fn parse(sess: &ParseSess,
|
||||||
let mut eof_eis = SmallVector::new();
|
let mut eof_eis = SmallVector::new();
|
||||||
assert!(next_eis.is_empty());
|
assert!(next_eis.is_empty());
|
||||||
|
|
||||||
match inner_parse_loop(&mut cur_eis, &mut next_eis, &mut eof_eis, &mut bb_eis,
|
match inner_parse_loop(sess, &mut cur_eis, &mut next_eis, &mut eof_eis, &mut bb_eis,
|
||||||
&parser.token, &parser.span) {
|
&parser.token, &parser.span) {
|
||||||
Success(_) => {},
|
Success(_) => {},
|
||||||
Failure(sp, tok) => return Failure(sp, tok),
|
Failure(sp, tok) => return Failure(sp, tok),
|
||||||
|
@ -435,7 +448,8 @@ pub fn parse(sess: &ParseSess,
|
||||||
/* error messages here could be improved with links to orig. rules */
|
/* error messages here could be improved with links to orig. rules */
|
||||||
if token_name_eq(&parser.token, &token::Eof) {
|
if token_name_eq(&parser.token, &token::Eof) {
|
||||||
if eof_eis.len() == 1 {
|
if eof_eis.len() == 1 {
|
||||||
return nameize(ms, eof_eis[0].matches.iter_mut().map(|mut dv| dv.pop().unwrap()));
|
let matches = eof_eis[0].matches.iter_mut().map(|mut dv| dv.pop().unwrap());
|
||||||
|
return nameize(sess, ms, matches);
|
||||||
} else if eof_eis.len() > 1 {
|
} else if eof_eis.len() > 1 {
|
||||||
return Error(parser.span, "ambiguity: multiple successful parses".to_string());
|
return Error(parser.span, "ambiguity: multiple successful parses".to_string());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -788,6 +788,7 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result<bool, (String, &'
|
||||||
// harmless
|
// harmless
|
||||||
Ok(true)
|
Ok(true)
|
||||||
},
|
},
|
||||||
|
"" => Ok(true), // keywords::Invalid
|
||||||
_ => Err((format!("invalid fragment specifier `{}`", frag),
|
_ => Err((format!("invalid fragment specifier `{}`", frag),
|
||||||
"valid fragment specifiers are `ident`, `block`, \
|
"valid fragment specifiers are `ident`, `block`, \
|
||||||
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
|
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \
|
||||||
|
@ -810,7 +811,7 @@ fn has_legal_fragment_specifier(tok: "ed::TokenTree) -> Result<(), String> {
|
||||||
fn is_legal_fragment_specifier(frag: &str) -> bool {
|
fn is_legal_fragment_specifier(frag: &str) -> bool {
|
||||||
match frag {
|
match frag {
|
||||||
"item" | "block" | "stmt" | "expr" | "pat" |
|
"item" | "block" | "stmt" | "expr" | "pat" |
|
||||||
"path" | "ty" | "ident" | "meta" | "tt" => true,
|
"path" | "ty" | "ident" | "meta" | "tt" | "" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,8 @@ pub fn parse(input: &[tokenstream::TokenTree], expect_matchers: bool, sess: &Par
|
||||||
},
|
},
|
||||||
tree @ _ => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp),
|
tree @ _ => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp),
|
||||||
};
|
};
|
||||||
sess.span_diagnostic.span_err(span, "missing fragment specifier");
|
sess.missing_fragment_specifiers.borrow_mut().insert(span);
|
||||||
|
result.push(TokenTree::MetaVarDecl(span, ident, keywords::Invalid.ident()));
|
||||||
}
|
}
|
||||||
_ => result.push(tree),
|
_ => result.push(tree),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1693,6 +1693,7 @@ mod tests {
|
||||||
use feature_gate::UnstableFeatures;
|
use feature_gate::UnstableFeatures;
|
||||||
use parse::token;
|
use parse::token;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -1704,6 +1705,7 @@ mod tests {
|
||||||
config: CrateConfig::new(),
|
config: CrateConfig::new(),
|
||||||
included_mod_stack: RefCell::new(Vec::new()),
|
included_mod_stack: RefCell::new(Vec::new()),
|
||||||
code_map: cm,
|
code_map: cm,
|
||||||
|
missing_fragment_specifiers: RefCell::new(HashSet::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ pub struct ParseSess {
|
||||||
pub span_diagnostic: Handler,
|
pub span_diagnostic: Handler,
|
||||||
pub unstable_features: UnstableFeatures,
|
pub unstable_features: UnstableFeatures,
|
||||||
pub config: CrateConfig,
|
pub config: CrateConfig,
|
||||||
|
pub missing_fragment_specifiers: RefCell<HashSet<Span>>,
|
||||||
/// Used to determine and report recursive mod inclusions
|
/// Used to determine and report recursive mod inclusions
|
||||||
included_mod_stack: RefCell<Vec<PathBuf>>,
|
included_mod_stack: RefCell<Vec<PathBuf>>,
|
||||||
code_map: Rc<CodeMap>,
|
code_map: Rc<CodeMap>,
|
||||||
|
@ -66,6 +67,7 @@ impl ParseSess {
|
||||||
span_diagnostic: handler,
|
span_diagnostic: handler,
|
||||||
unstable_features: UnstableFeatures::from_environment(),
|
unstable_features: UnstableFeatures::from_environment(),
|
||||||
config: HashSet::new(),
|
config: HashSet::new(),
|
||||||
|
missing_fragment_specifiers: RefCell::new(HashSet::new()),
|
||||||
included_mod_stack: RefCell::new(vec![]),
|
included_mod_stack: RefCell::new(vec![]),
|
||||||
code_map: code_map
|
code_map: code_map
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![deny(missing_fragment_specifier)] //~ NOTE lint level defined here
|
||||||
|
|
||||||
|
macro_rules! m { ($i) => {} }
|
||||||
|
//~^ ERROR missing fragment specifier
|
||||||
|
//~| WARN previously accepted
|
||||||
|
//~| NOTE issue #40107
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -8,8 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
macro_rules! test { ($a, //~ ERROR missing fragment
|
macro_rules! test { ($a, $b) => (()); } //~ ERROR missing fragment
|
||||||
$b) => (()); } //~ ERROR missing fragment
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
test!()
|
test!()
|
||||||
|
|
|
@ -16,3 +16,5 @@ macro_rules! foo {
|
||||||
$(x)(y) //~ ERROR expected `*` or `+`
|
$(x)(y) //~ ERROR expected `*` or `+`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foo!();
|
||||||
|
|
Loading…
Reference in New Issue