Skip to content

Commit 4c2181b

Browse files
committed
submodules: update clippy from 5afdf8b to b1d0343
Changes: ```` new_ret_no_self: add sample from rust-lang#3313 to Known Problems section. Support multiline comments and hopefully fix panic Check for comments in collapsible ifs Resolve ICE in needless range loop lint RIIR update_lints: Update changelog links Rename if_let_redundant_pattern_matching to redundant_pattern_matching Add lint for redundant pattern matching for explicit return boolean Fix issue rust-lang#3322: reword help message for len_zero Simplify manual_memcpy suggestion in some cases Fix dogfood Update known problems for unnecessary_fold RIIR update_lints: Replace lint count in README.md Rename `active_lints` to `usable_lints` Add comment on WalkDir vs. fs::read_dir sort_by -> sort_by_key Some more documentation for clippy_dev Use `WalkDir` to also gather from subdirectories Avoid linting `boxed_local` on trait implementations. Website: Make lint categories linkable Restore clippy_dummy's placeholder name Swap order of methods in `needless_range_loop` suggestion in some cases Revert "Exclude pattern guards from unnecessary_fold lint" Exclude pattern guards from unnecessary_fold lint ````
1 parent 7da731a commit 4c2181b

34 files changed

+886
-203
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ All notable changes to this project will be documented in this file.
816816
[`redundant_closure_call`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_closure_call
817817
[`redundant_field_names`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_field_names
818818
[`redundant_pattern`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_pattern
819+
[`redundant_pattern_matching`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_pattern_matching
819820
[`ref_in_deref`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#ref_in_deref
820821
[`regex_macro`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#regex_macro
821822
[`replace_consts`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#replace_consts

clippy_dev/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ clap = "~2.32"
99
itertools = "0.7"
1010
regex = "1"
1111
lazy_static = "1.0"
12+
walkdir = "2"

clippy_dev/src/lib.rs

Lines changed: 167 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use itertools::Itertools;
1515
use lazy_static::lazy_static;
1616
use regex::Regex;
17+
use walkdir::WalkDir;
1718
use std::collections::HashMap;
1819
use std::ffi::OsStr;
1920
use std::fs;
@@ -35,6 +36,7 @@ lazy_static! {
3536
pub static ref DOCS_LINK: String = "https://rust-lang-nursery.github.io/rust-clippy/master/index.html".to_string();
3637
}
3738

39+
/// Lint data parsed from the Clippy source code.
3840
#[derive(Clone, PartialEq, Debug)]
3941
pub struct Lint {
4042
pub name: String,
@@ -55,22 +57,39 @@ impl Lint {
5557
}
5658
}
5759

58-
/// Returns all non-deprecated lints
59-
pub fn active_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
60-
lints.filter(|l| l.deprecation.is_none())
60+
/// Returns all non-deprecated lints and non-internal lints
61+
pub fn usable_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
62+
lints.filter(|l| l.deprecation.is_none() && !l.is_internal())
6163
}
6264

6365
/// Returns the lints in a HashMap, grouped by the different lint groups
6466
pub fn by_lint_group(lints: &[Self]) -> HashMap<String, Vec<Self>> {
6567
lints.iter().map(|lint| (lint.group.to_string(), lint.clone())).into_group_map()
6668
}
69+
70+
pub fn is_internal(&self) -> bool {
71+
self.group.starts_with("internal")
72+
}
73+
}
74+
75+
pub fn gen_changelog_lint_list(lints: Vec<Lint>) -> Vec<String> {
76+
let mut lint_list_sorted: Vec<Lint> = lints;
77+
lint_list_sorted.sort_by_key(|l| l.name.clone());
78+
lint_list_sorted
79+
.iter()
80+
.filter(|l| !l.is_internal())
81+
.map(|l| {
82+
format!("[`{}`]: {}#{}", l.name, DOCS_LINK.clone(), l.name)
83+
})
84+
.collect()
6785
}
6886

87+
/// Gathers all files in `src/clippy_lints` and gathers all lints inside
6988
pub fn gather_all() -> impl Iterator<Item=Lint> {
7089
lint_files().flat_map(|f| gather_from_file(&f))
7190
}
7291

73-
fn gather_from_file(dir_entry: &fs::DirEntry) -> impl Iterator<Item=Lint> {
92+
fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item=Lint> {
7493
let mut file = fs::File::open(dir_entry.path()).unwrap();
7594
let mut content = String::new();
7695
file.read_to_string(&mut content).unwrap();
@@ -89,13 +108,98 @@ fn parse_contents(content: &str, filename: &str) -> impl Iterator<Item=Lint> {
89108
}
90109

91110
/// Collects all .rs files in the `clippy_lints/src` directory
92-
fn lint_files() -> impl Iterator<Item=fs::DirEntry> {
93-
fs::read_dir("../clippy_lints/src")
94-
.unwrap()
111+
fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
112+
// We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
113+
// Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
114+
WalkDir::new("../clippy_lints/src")
115+
.into_iter()
95116
.filter_map(|f| f.ok())
96117
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
97118
}
98119

120+
/// Replace a region in a file delimited by two lines matching regexes.
121+
///
122+
/// `path` is the relative path to the file on which you want to perform the replacement.
123+
///
124+
/// See `replace_region_in_text` for documentation of the other options.
125+
#[allow(clippy::expect_fun_call)]
126+
pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec<String> {
127+
let mut f = fs::File::open(path).expect(&format!("File not found: {}", path));
128+
let mut contents = String::new();
129+
f.read_to_string(&mut contents).expect("Something went wrong reading the file");
130+
let replaced = replace_region_in_text(&contents, start, end, replace_start, replacements);
131+
132+
let mut f = fs::File::create(path).expect(&format!("File not found: {}", path));
133+
f.write_all(replaced.as_bytes()).expect("Unable to write file");
134+
// Ensure we write the changes with a trailing newline so that
135+
// the file has the proper line endings.
136+
f.write_all(b"\n").expect("Unable to write file");
137+
}
138+
139+
/// Replace a region in a text delimited by two lines matching regexes.
140+
///
141+
/// * `text` is the input text on which you want to perform the replacement
142+
/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
143+
/// As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
144+
/// * `end` is a `&str` that describes the delimiter line until where the replacement should
145+
/// happen. As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
146+
/// * If `replace_start` is true, the `start` delimiter line is replaced as well.
147+
/// The `end` delimiter line is never replaced.
148+
/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
149+
///
150+
/// If you want to perform the replacement on files instead of already parsed text,
151+
/// use `replace_region_in_file`.
152+
///
153+
/// # Example
154+
///
155+
/// ```
156+
/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
157+
/// let result = clippy_dev::replace_region_in_text(
158+
/// the_text,
159+
/// r#"replace_start"#,
160+
/// r#"replace_end"#,
161+
/// false,
162+
/// || {
163+
/// vec!["a different".to_string(), "text".to_string()]
164+
/// }
165+
/// );
166+
/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
167+
/// ```
168+
pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec<String> {
169+
let lines = text.lines();
170+
let mut in_old_region = false;
171+
let mut found = false;
172+
let mut new_lines = vec![];
173+
let start = Regex::new(start).unwrap();
174+
let end = Regex::new(end).unwrap();
175+
176+
for line in lines {
177+
if in_old_region {
178+
if end.is_match(&line) {
179+
in_old_region = false;
180+
new_lines.extend(replacements());
181+
new_lines.push(line.to_string());
182+
}
183+
} else if start.is_match(&line) {
184+
if !replace_start {
185+
new_lines.push(line.to_string());
186+
}
187+
in_old_region = true;
188+
found = true;
189+
} else {
190+
new_lines.push(line.to_string());
191+
}
192+
}
193+
194+
if !found {
195+
// This happens if the provided regex in `clippy_dev/src/main.rs` is not found in the
196+
// given text or file. Most likely this is an error on the programmer's side and the Regex
197+
// is incorrect.
198+
println!("regex {:?} not found. You may have to update it.", start);
199+
}
200+
new_lines.join("\n")
201+
}
202+
99203
#[test]
100204
fn test_parse_contents() {
101205
let result: Vec<Lint> = parse_contents(
@@ -136,15 +240,54 @@ declare_deprecated_lint! {
136240
}
137241

138242
#[test]
139-
fn test_active_lints() {
243+
fn test_replace_region() {
244+
let text = r#"
245+
abc
246+
123
247+
789
248+
def
249+
ghi"#;
250+
let expected = r#"
251+
abc
252+
hello world
253+
def
254+
ghi"#;
255+
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
256+
vec!["hello world".to_string()]
257+
});
258+
assert_eq!(expected, result);
259+
}
260+
261+
#[test]
262+
fn test_replace_region_with_start() {
263+
let text = r#"
264+
abc
265+
123
266+
789
267+
def
268+
ghi"#;
269+
let expected = r#"
270+
hello world
271+
def
272+
ghi"#;
273+
let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
274+
vec!["hello world".to_string()]
275+
});
276+
assert_eq!(expected, result);
277+
}
278+
279+
#[test]
280+
fn test_usable_lints() {
140281
let lints = vec![
141282
Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
142-
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
283+
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
284+
Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
285+
Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name")
143286
];
144287
let expected = vec![
145288
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
146289
];
147-
assert_eq!(expected, Lint::active_lints(lints.into_iter()).collect::<Vec<Lint>>());
290+
assert_eq!(expected, Lint::usable_lints(lints.into_iter()).collect::<Vec<Lint>>());
148291
}
149292

150293
#[test]
@@ -164,3 +307,17 @@ fn test_by_lint_group() {
164307
]);
165308
assert_eq!(expected, Lint::by_lint_group(&lints));
166309
}
310+
311+
#[test]
312+
fn test_gen_changelog_lint_list() {
313+
let lints = vec![
314+
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
315+
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
316+
Lint::new("incorrect_internal", "internal_style", "abc", None, "module_name"),
317+
];
318+
let expected = vec![
319+
format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
320+
format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string())
321+
];
322+
assert_eq!(expected, gen_changelog_lint_list(lints));
323+
}

clippy_dev/src/main.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,54 @@ fn main() {
3232
if let Some(matches) = matches.subcommand_matches("update_lints") {
3333
if matches.is_present("print-only") {
3434
print_lints();
35+
} else {
36+
update_lints();
3537
}
3638
}
3739
}
3840

3941
fn print_lints() {
40-
let lint_list = gather_all().collect::<Vec<Lint>>();
41-
let grouped_by_lint_group = Lint::by_lint_group(&lint_list);
42+
let lint_list = gather_all();
43+
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
44+
let lint_count = usable_lints.len();
45+
let grouped_by_lint_group = Lint::by_lint_group(&usable_lints);
4246

4347
for (lint_group, mut lints) in grouped_by_lint_group {
4448
if lint_group == "Deprecated" { continue; }
4549
println!("\n## {}", lint_group);
4650

47-
lints.sort_by(|a, b| a.name.cmp(&b.name));
51+
lints.sort_by_key(|l| l.name.clone());
4852

4953
for lint in lints {
5054
println!("* [{}]({}#{}) ({})", lint.name, clippy_dev::DOCS_LINK.clone(), lint.name, lint.desc);
5155
}
5256
}
5357

54-
println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count());
58+
println!("there are {} lints", lint_count);
59+
}
60+
61+
fn update_lints() {
62+
let lint_list: Vec<Lint> = gather_all().collect();
63+
let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
64+
let lint_count = usable_lints.len();
65+
66+
replace_region_in_file(
67+
"../README.md",
68+
r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#,
69+
"",
70+
true,
71+
|| {
72+
vec![
73+
format!("[There are {} lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)", lint_count)
74+
]
75+
}
76+
);
77+
78+
replace_region_in_file(
79+
"../CHANGELOG.md",
80+
"<!-- begin autogenerated links to lint list -->",
81+
"<!-- end autogenerated links to lint list -->",
82+
false,
83+
|| { gen_changelog_lint_list(lint_list.clone()) }
84+
);
5585
}

clippy_dummy/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "clippy" # rename to clippy before publishing
2+
name = "clippy_dummy" # rename to clippy before publishing
33
version = "0.0.302"
44
authors = ["Manish Goregaokar <manishsmail@gmail.com>"]
55
edition = "2018"

clippy_lints/src/collapsible_if.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,17 @@ fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
112112
}
113113
}
114114

115+
fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
116+
// We trim all opening braces and whitespaces and then check if the next string is a comment.
117+
let trimmed_block_text =
118+
snippet_block(cx, expr.span, "..").trim_left_matches(|c: char| c.is_whitespace() || c == '{').to_owned();
119+
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
120+
}
121+
115122
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
116123
if_chain! {
117124
if let ast::ExprKind::Block(ref block, _) = else_.node;
125+
if !block_starts_with_comment(cx, block);
118126
if let Some(else_) = expr_block(block);
119127
if !in_macro(else_.span);
120128
then {
@@ -135,6 +143,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
135143

136144
fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
137145
if_chain! {
146+
if !block_starts_with_comment(cx, then);
138147
if let Some(inner) = expr_block(then);
139148
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node;
140149
then {

clippy_lints/src/deprecated_lints.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ macro_rules! declare_deprecated_lint {
1616

1717
/// **What it does:** Nothing. This lint has been deprecated.
1818
///
19-
/// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
19+
/// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend
2020
/// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
2121
declare_deprecated_lint! {
2222
pub SHOULD_ASSERT_EQ,
@@ -102,3 +102,13 @@ declare_deprecated_lint! {
102102
pub ASSIGN_OPS,
103103
"using compound assignment operators (e.g. `+=`) is harmless"
104104
}
105+
106+
/// **What it does:** Nothing. This lint has been deprecated.
107+
///
108+
/// **Deprecation reason:** The original rule will only lint for `if let`. After
109+
/// making it support to lint `match`, naming as `if let` is not suitable for it.
110+
/// So, this lint is deprecated.
111+
declare_deprecated_lint! {
112+
pub IF_LET_REDUNDANT_PATTERN_MATCHING,
113+
"this lint has been changed to redundant_pattern_matching"
114+
}

clippy_lints/src/escape.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl LintPass for Pass {
6565
}
6666

6767
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
68+
6869
fn check_fn(
6970
&mut self,
7071
cx: &LateContext<'a, 'tcx>,
@@ -74,13 +75,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
7475
_: Span,
7576
node_id: NodeId,
7677
) {
77-
let fn_def_id = cx.tcx.hir.local_def_id(node_id);
78+
// If the method is an impl for a trait, don't warn
79+
let parent_id = cx.tcx.hir.get_parent(node_id);
80+
let parent_node = cx.tcx.hir.find(parent_id);
81+
82+
if let Some(Node::Item(item)) = parent_node {
83+
if let ItemKind::Impl(_, _, _, _, Some(..), _, _) = item.node {
84+
return;
85+
}
86+
}
87+
7888
let mut v = EscapeDelegate {
7989
cx,
8090
set: NodeSet(),
8191
too_large_for_stack: self.too_large_for_stack,
8292
};
8393

94+
let fn_def_id = cx.tcx.hir.local_def_id(node_id);
8495
let region_scope_tree = &cx.tcx.region_scope_tree(fn_def_id);
8596
ExprUseVisitor::new(&mut v, cx.tcx, cx.param_env, region_scope_tree, cx.tables, None).consume_body(body);
8697

0 commit comments

Comments
 (0)