diff options
| author | Michael Wright <mikerite@lavabit.com> | 2018-11-06 07:00:54 +0200 |
|---|---|---|
| committer | Michael Wright <mikerite@lavabit.com> | 2018-11-06 07:00:54 +0200 |
| commit | 2353f240951aeb3f1b81a0cfb814ba3ebeff55e5 (patch) | |
| tree | 52cedec706de6805bde3bd5d6b9cc4dd67162ecf | |
| parent | a3ab512576c77e08646ba731e8906a02983da2c8 (diff) | |
| parent | 4c3408c61d1042bf0585de440041ee7edfc5b350 (diff) | |
| download | rust-2353f240951aeb3f1b81a0cfb814ba3ebeff55e5.tar.gz rust-2353f240951aeb3f1b81a0cfb814ba3ebeff55e5.zip | |
Merge branch 'master' into fix-missing-comma-fp
| -rw-r--r-- | CONTRIBUTING.md | 2 | ||||
| -rwxr-xr-x | ci/base-tests.sh | 5 | ||||
| -rw-r--r-- | clippy_dev/src/lib.rs | 122 | ||||
| -rw-r--r-- | clippy_dev/src/main.rs | 59 | ||||
| -rw-r--r-- | clippy_lints/src/lib.rs | 12 | ||||
| -rw-r--r-- | clippy_lints/src/literal_representation.rs | 156 | ||||
| -rw-r--r-- | clippy_lints/src/utils/mod.rs | 4 | ||||
| -rw-r--r-- | tests/ui/literals.rs | 10 | ||||
| -rw-r--r-- | tests/ui/literals.stderr | 146 | ||||
| -rwxr-xr-x | util/update_lints.py | 242 |
10 files changed, 322 insertions, 436 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4080a5fa9c..50f61eb1e67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,7 +180,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { The [`rustc_plugin::PluginRegistry`][plugin_registry] provides two methods to register lints: [register_early_lint_pass][reg_early_lint_pass] and [register_late_lint_pass][reg_late_lint_pass]. Both take an object that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in every single lint. -It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `util/update_lints.py` and you don't have to add anything by hand. When you are writing your own lint, you can use that script to save you some time. +It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `util/dev update_lints` and you don't have to add anything by hand. When you are writing your own lint, you can use that script to save you some time. ```rust // ./clippy_lints/src/else_if_without_else.rs diff --git a/ci/base-tests.sh b/ci/base-tests.sh index 0ed1494be82..88cc20842e8 100755 --- a/ci/base-tests.sh +++ b/ci/base-tests.sh @@ -23,8 +23,9 @@ cargo test --features debugging cd clippy_lints && cargo test && cd .. cd rustc_tools_util && cargo test && cd .. cd clippy_dev && cargo test && cd .. -# check that the lint lists are up-to-date -./util/update_lints.py -c + +# Perform various checks for lint registration +./util/dev update_lints --check CLIPPY="`pwd`/target/debug/cargo-clippy clippy" # run clippy on its own codebase... diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 2ae8423381d..5e1a454195e 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -114,19 +114,22 @@ pub fn gen_changelog_lint_list(lints: Vec<Lint>) -> Vec<String> { /// Generates the `register_removed` code in `./clippy_lints/src/lib.rs`. pub fn gen_deprecated(lints: &[Lint]) -> Vec<String> { - lints.iter() - .filter_map(|l| { - l.clone().deprecation.and_then(|depr_text| { - Some( - format!( - " store.register_removed(\n \"{}\",\n \"{}\",\n );", - l.name, - depr_text + itertools::flatten( + lints + .iter() + .filter_map(|l| { + l.clone().deprecation.and_then(|depr_text| { + Some( + vec![ + " store.register_removed(".to_string(), + format!(" \"{}\",", l.name), + format!(" \"{}\",", depr_text), + " );".to_string() + ] ) - ) + }) }) - }) - .collect() + ).collect() } /// Gathers all files in `src/clippy_lints` and gathers all lints inside @@ -168,23 +171,33 @@ fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> { .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } +/// Whether a file has had its text changed or not +#[derive(PartialEq, Debug)] +pub struct FileChange { + pub changed: bool, + pub new_lines: String, +} + /// Replace a region in a file delimited by two lines matching regexes. /// /// `path` is the relative path to the file on which you want to perform the replacement. /// /// See `replace_region_in_text` for documentation of the other options. #[allow(clippy::expect_fun_call)] -pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, replacements: F) where F: Fn() -> Vec<String> { +pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_start: bool, write_back: bool, replacements: F) -> FileChange where F: Fn() -> Vec<String> { let mut f = fs::File::open(path).expect(&format!("File not found: {}", path)); let mut contents = String::new(); f.read_to_string(&mut contents).expect("Something went wrong reading the file"); - let replaced = replace_region_in_text(&contents, start, end, replace_start, replacements); - - let mut f = fs::File::create(path).expect(&format!("File not found: {}", path)); - f.write_all(replaced.as_bytes()).expect("Unable to write file"); - // Ensure we write the changes with a trailing newline so that - // the file has the proper line endings. - f.write_all(b"\n").expect("Unable to write file"); + let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements); + + if write_back { + let mut f = fs::File::create(path).expect(&format!("File not found: {}", path)); + f.write_all(file_change.new_lines.as_bytes()).expect("Unable to write file"); + // Ensure we write the changes with a trailing newline so that + // the file has the proper line endings. + f.write_all(b"\n").expect("Unable to write file"); + } + file_change } /// Replace a region in a text delimited by two lines matching regexes. @@ -213,10 +226,10 @@ pub fn replace_region_in_file<F>(path: &str, start: &str, end: &str, replace_sta /// || { /// vec!["a different".to_string(), "text".to_string()] /// } -/// ); +/// ).new_lines; /// assert_eq!("replace_start\na different\ntext\nreplace_end", result); /// ``` -pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> String where F: Fn() -> Vec<String> { +pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange where F: Fn() -> Vec<String> { let lines = text.lines(); let mut in_old_region = false; let mut found = false; @@ -224,7 +237,7 @@ pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_sta let start = Regex::new(start).unwrap(); let end = Regex::new(end).unwrap(); - for line in lines { + for line in lines.clone() { if in_old_region { if end.is_match(&line) { in_old_region = false; @@ -248,7 +261,11 @@ pub fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_sta // is incorrect. eprintln!("error: regex `{:?}` not found. You may have to update it.", start); } - new_lines.join("\n") + + FileChange { + changed: lines.ne(new_lines.clone()), + new_lines: new_lines.join("\n") + } } #[test] @@ -292,17 +309,11 @@ declare_deprecated_lint! { #[test] fn test_replace_region() { - let text = r#" -abc -123 -789 -def -ghi"#; - let expected = r#" -abc -hello world -def -ghi"#; + let text = "\nabc\n123\n789\ndef\nghi"; + let expected = FileChange { + changed: true, + new_lines: "\nabc\nhello world\ndef\nghi".to_string() + }; let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || { vec!["hello world".to_string()] }); @@ -311,16 +322,11 @@ ghi"#; #[test] fn test_replace_region_with_start() { - let text = r#" -abc -123 -789 -def -ghi"#; - let expected = r#" -hello world -def -ghi"#; + let text = "\nabc\n123\n789\ndef\nghi"; + let expected = FileChange { + changed: true, + new_lines: "\nhello world\ndef\nghi".to_string() + }; let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || { vec!["hello world".to_string()] }); @@ -328,6 +334,19 @@ ghi"#; } #[test] +fn test_replace_region_no_changes() { + let text = "123\n456\n789"; + let expected = FileChange { + changed: false, + new_lines: "123\n456\n789".to_string() + }; + let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, || { + vec![] + }); + assert_eq!(expected, result); +} + +#[test] fn test_usable_lints() { let lints = vec![ Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"), @@ -377,14 +396,19 @@ fn test_gen_changelog_lint_list() { fn test_gen_deprecated() { let lints = vec![ Lint::new("should_assert_eq", "group1", "abc", Some("has been superseeded by should_assert_eq2"), "module_name"), + Lint::new("another_deprecated", "group2", "abc", Some("will be removed"), "module_name"), Lint::new("should_assert_eq2", "group2", "abc", None, "module_name") ]; let expected: Vec<String> = vec![ - r#" store.register_removed( - "should_assert_eq", - "has been superseeded by should_assert_eq2", - );"#.to_string() - ]; + " store.register_removed(", + " \"should_assert_eq\",", + " \"has been superseeded by should_assert_eq2\",", + " );", + " store.register_removed(", + " \"another_deprecated\",", + " \"will be removed\",", + " );" + ].into_iter().map(String::from).collect(); assert_eq!(expected, gen_deprecated(&lints)); } diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 288fb7c58b4..bfd98968c42 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -15,6 +15,12 @@ extern crate regex; use clap::{App, Arg, SubCommand}; use clippy_dev::*; +#[derive(PartialEq)] +enum UpdateMode { + Check, + Change +} + fn main() { let matches = App::new("Clippy developer tooling") .subcommand( @@ -28,17 +34,23 @@ fn main() { .arg( Arg::with_name("print-only") .long("print-only") - .short("p") - .help("Print a table of lints to STDOUT. This does not include deprecated and internal lints. (Does not modify any files)"), + .help("Print a table of lints to STDOUT. This does not include deprecated and internal lints. (Does not modify any files)") + ) + .arg( + Arg::with_name("check") + .long("check") + .help("Checks that util/dev update_lints has been run. Used on CI."), ) - ) - .get_matches(); + ) + .get_matches(); if let Some(matches) = matches.subcommand_matches("update_lints") { if matches.is_present("print-only") { print_lints(); + } else if matches.is_present("check") { + update_lints(&UpdateMode::Check); } else { - update_lints(); + update_lints(&UpdateMode::Change); } } } @@ -63,53 +75,58 @@ fn print_lints() { println!("there are {} lints", lint_count); } -fn update_lints() { +fn update_lints(update_mode: &UpdateMode) { let lint_list: Vec<Lint> = gather_all().collect(); let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect(); let lint_count = usable_lints.len(); - replace_region_in_file( + let mut file_change = replace_region_in_file( "../README.md", r#"\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)"#, "", true, + update_mode == &UpdateMode::Change, || { vec", lint_count) ] } - ); + ).changed; - replace_region_in_file( + file_change |= replace_region_in_file( "../CHANGELOG.md", "<!-- begin autogenerated links to lint list -->", "<!-- end autogenerated links to lint list -->", false, + update_mode == &UpdateMode::Change, || { gen_changelog_lint_list(lint_list.clone()) } - ); + ).changed; - replace_region_in_file( + file_change |= replace_region_in_file( "../clippy_lints/src/lib.rs", "begin deprecated lints", "end deprecated lints", false, + update_mode == &UpdateMode::Change, || { gen_deprecated(&lint_list) } - ); + ).changed; - replace_region_in_file( + file_change |= replace_region_in_file( "../clippy_lints/src/lib.rs", "begin lints modules", "end lints modules", false, + update_mode == &UpdateMode::Change, || { gen_modules_list(lint_list.clone()) } - ); + ).changed; // Generate lists of lints in the clippy::all lint group - replace_region_in_file( + file_change |= replace_region_in_file( "../clippy_lints/src/lib.rs", r#"reg.register_lint_group\("clippy::all""#, r#"\]\);"#, false, + update_mode == &UpdateMode::Change, || { // clippy::all should only include the following lint groups: let all_group_lints = usable_lints.clone().into_iter().filter(|l| { @@ -121,16 +138,22 @@ fn update_lints() { gen_lint_group_list(all_group_lints) } - ); + ).changed; // Generate the list of lints for all other lint groups for (lint_group, lints) in Lint::by_lint_group(&usable_lints) { - replace_region_in_file( + file_change |= replace_region_in_file( "../clippy_lints/src/lib.rs", &format!("reg.register_lint_group\\(\"clippy::{}\"", lint_group), r#"\]\);"#, false, + update_mode == &UpdateMode::Change, || { gen_lint_group_list(lints.clone()) } - ); + ).changed; + } + + if update_mode == &UpdateMode::Check && file_change { + println!("Not all lints defined properly. Please run `util/dev update_lints` to make sure all lints are defined properly."); + std::process::exit(1); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 06ae78a6509..c93e9d57d67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -548,8 +548,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { bytecount::NAIVE_BYTECOUNT, collapsible_if::COLLAPSIBLE_IF, const_static_lifetime::CONST_STATIC_LIFETIME, - copies::IF_SAME_THEN_ELSE, copies::IFS_SAME_COND, + copies::IF_SAME_THEN_ELSE, cyclomatic_complexity::CYCLOMATIC_COMPLEXITY, derive::DERIVE_HASH_XOR_EQ, double_comparison::DOUBLE_COMPARISONS, @@ -743,12 +743,12 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { unused_io_amount::UNUSED_IO_AMOUNT, unused_label::UNUSED_LABEL, vec::USELESS_VEC, + write::PRINTLN_EMPTY_STRING, write::PRINT_LITERAL, write::PRINT_WITH_NEWLINE, - write::PRINTLN_EMPTY_STRING, + write::WRITELN_EMPTY_STRING, write::WRITE_LITERAL, write::WRITE_WITH_NEWLINE, - write::WRITELN_EMPTY_STRING, zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); @@ -831,12 +831,12 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { types::IMPLICIT_HASHER, types::LET_UNIT_VALUE, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + write::PRINTLN_EMPTY_STRING, write::PRINT_LITERAL, write::PRINT_WITH_NEWLINE, - write::PRINTLN_EMPTY_STRING, + write::WRITELN_EMPTY_STRING, write::WRITE_LITERAL, write::WRITE_WITH_NEWLINE, - write::WRITELN_EMPTY_STRING, ]); reg.register_lint_group("clippy::complexity", Some("clippy_complexity"), vec![ @@ -916,8 +916,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { bit_mask::BAD_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK, booleans::LOGIC_BUG, - copies::IF_SAME_THEN_ELSE, copies::IFS_SAME_COND, + copies::IF_SAME_THEN_ELSE, derive::DERIVE_HASH_XOR_EQ, drop_forget_ref::DROP_COPY, drop_forget_ref::DROP_REF, diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index f029dd65b39..bd54c068486 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -7,16 +7,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. -use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass, in_external_macro, LintContext}; +use crate::rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; use crate::rustc::{declare_tool_lint, lint_array}; -use if_chain::if_chain; use crate::syntax::ast::*; use crate::syntax_pos; use crate::utils::{snippet_opt, span_lint_and_sugg}; +use if_chain::if_chain; /// **What it does:** Warns if a long integral or floating-point constant does /// not contain underscores. @@ -41,9 +40,9 @@ declare_clippy_lint! { /// **Why is this bad?** This is most probably a typo /// /// **Known problems:** -/// - Recommends a signed suffix, even though the number might be too big and an unsigned +/// - Recommends a signed suffix, even though the number might be too big and an unsigned /// suffix is required -/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers +/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers /// /// **Example:** /// @@ -168,21 +167,21 @@ impl<'a> DigitInfo<'a> { let len = sans_prefix.len(); let mut last_d = '\0'; for (d_idx, d) in sans_prefix.char_indices() { - let suffix_start = if last_d == '_' { - d_idx - 1 - } else { - d_idx - }; - if float && (d == 'f' || d == 'e' || d == 'E') || - !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len)) { - let (digits, suffix) = sans_prefix.split_at(suffix_start); - return Self { - digits, - radix, - prefix, - suffix: Some(suffix), - float, - }; + let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx }; + if float + && (d == 'f' + || is_possible_float_suffix_index(&sans_prefix, suffix_start, len) + || ((d == 'E' || d == 'e') && !has_possible_float_suffix(&sans_prefix))) + || !float && (d == 'i' || d == 'u' || is_possible_suffix_index(&sans_prefix, suffix_start, len)) + { + let (digits, suffix) = sans_prefix.split_at(suffix_start); + return Self { + digits, + radix, + prefix, + suffix: Some(suffix), + float, + }; } last_d = d } @@ -224,18 +223,44 @@ impl<'a> DigitInfo<'a> { .map(|chunk| chunk.iter().collect()) .collect::<Vec<String>>() .join("_"); + let suffix_hint = match self.suffix { + Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]), + Some(suffix) => suffix.to_string(), + None => String::new(), + }; + format!("{}.{}{}", int_part_hint, frac_part_hint, suffix_hint) + } else if self.float && (self.digits.contains('E') || self.digits.contains('e')) { + let which_e = if self.digits.contains('E') { 'E' } else { 'e' }; + let parts: Vec<&str> = self.digits.split(which_e).collect(); + let filtered_digits_vec_0 = parts[0].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>(); + let filtered_digits_vec_1 = parts[1].chars().filter(|&c| c != '_').rev().collect::<Vec<_>>(); + let before_e_hint = filtered_digits_vec_0 + .chunks(group_size) + .map(|chunk| chunk.iter().rev().collect()) + .rev() + .collect::<Vec<String>>() + .join("_"); + let after_e_hint = filtered_digits_vec_1 + .chunks(group_size) + .map(|chunk| chunk.iter().rev().collect()) + .rev() + .collect::<Vec<String>>() + .join("_"); + let suffix_hint = match self.suffix { + Some(suffix) if is_mistyped_float_suffix(suffix) => format!("_f{}", &suffix[1..]), + Some(suffix) => suffix.to_string(), + None => String::new(), + }; format!( - "{}.{}{}", - int_part_hint, - frac_part_hint, - self.suffix.unwrap_or("") + "{}{}{}{}{}", + self.prefix.unwrap_or(""), + before_e_hint, + which_e, + after_e_hint, + suffix_hint ) } else { - let filtered_digits_vec = self.digits - .chars() - .filter(|&c| c != '_') - .rev() - .collect::<Vec<_>>(); + let filtered_digits_vec = self.digits.chars().filter(|&c| c != '_').rev().collect::<Vec<_>>(); let mut hint = filtered_digits_vec .chunks(group_size) .map(|chunk| chunk.iter().rev().collect()) @@ -248,18 +273,11 @@ impl<'a> DigitInfo<'a> { hint = format!("{:0>4}{}", &hint[..nb_digits_to_fill], &hint[nb_digits_to_fill..]); } let suffix_hint = match self.suffix { - Some(suffix) if is_mistyped_suffix(suffix) => { - format!("_i{}", &suffix[1..]) - }, + Some(suffix) if is_mistyped_suffix(suffix) => format!("_i{}", &suffix[1..]), Some(suffix) => suffix.to_string(), - None => String::new() + None => String::new(), }; - format!( - "{}{}{}", - self.prefix.unwrap_or(""), - hint, - suffix_hint - ) + format!("{}{}{}", self.prefix.unwrap_or(""), hint, suffix_hint) } } } @@ -269,22 +287,20 @@ enum WarningType { InconsistentDigitGrouping, LargeDigitGroups, DecimalRepresentation, - MistypedLiteralSuffix + MistypedLiteralSuffix, } impl WarningType { crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) { match self { - WarningType::MistypedLiteralSuffix => { - span_lint_and_sugg( - cx, - MISTYPED_LITERAL_SUFFIXES, - span, - "mistyped literal suffix", - "did you mean to write", - grouping_hint.to_string() - ) - }, + WarningType::MistypedLiteralSuffix => span_lint_and_sugg( + cx, + MISTYPED_LITERAL_SUFFIXES, + span, + "mistyped literal suffix", + "did you mean to write", + grouping_hint.to_string(), + ), WarningType::UnreadableLiteral => span_lint_and_sugg( cx, UNREADABLE_LITERAL, @@ -380,7 +396,7 @@ impl LiteralDigitGrouping { // Lint integral and fractional parts separately, and then check consistency of digit // groups if both pass. - let _ = Self::do_lint(parts[0], None) + let _ = Self::do_lint(parts[0], digit_info.suffix) .map(|integral_group_size| { if parts.len() > 1 { // Lint the fractional part of literal just like integral part, but reversed. @@ -391,11 +407,11 @@ impl LiteralDigitGrouping { fractional_group_size, parts[0].len(), parts[1].len()); - if !consistent { - WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), - cx, - lit.span); - } + if !consistent { + WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), + cx, + lit.span); + } }) .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, @@ -494,9 +510,7 @@ impl EarlyLintPass for LiteralRepresentation { impl LiteralRepresentation { pub fn new(threshold: u64) -> Self { - Self { - threshold, - } + Self { threshold } } fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { // Lint integral literals. @@ -529,7 +543,12 @@ impl LiteralRepresentation { fn do_lint(digits: &str) -> Result<(), WarningType> { if digits.len() == 1 { // Lint for 1 digit literals, if someone really sets the threshold that low - if digits == "1" || digits == "2" || digits == "4" || digits == "8" || digits == "3" || digits == "7" + if digits == "1" + || digits == "2" + || digits == "4" + || digits == "8" + || digits == "3" + || digits == "7" || digits == "F" { return Err(WarningType::DecimalRepresentation); @@ -538,6 +557,7 @@ impl LiteralRepresentation { // Lint for Literals with a hex-representation of 2 or 3 digits let f = &digits[0..1]; // first digit let s = &digits[1..]; // suffix + // Powers of 2 if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) // Powers of 2 minus 1 @@ -550,6 +570,7 @@ impl LiteralRepresentation { let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix + // Powers of 2 with a margin of +15/-16 if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) @@ -570,6 +591,17 @@ fn is_mistyped_suffix(suffix: &str) -> bool { } fn is_possible_suffix_index(lit: &str, idx: usize, len: usize) -> bool { - ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && - is_mistyped_suffix(lit.split_at(idx).1) + ((len > 3 && idx == len - 3) || (len > 2 && idx == len - 2)) && is_mistyped_suffix(lit.split_at(idx).1) +} + +fn is_mistyped_float_suffix(suffix: &str) -> bool { + ["_32", "_64"].contains(&suffix) +} + +fn is_possible_float_suffix_index(lit: &str, idx: usize, len: usize) -> bool { + (len > 3 && idx == len - 3) && is_mistyped_float_suffix(lit.split_at(idx).1) +} + +fn has_possible_float_suffix(lit: &str) -> bool { + lit.ends_with("_32") || lit.ends_with("_64") } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 1cd20b68421..ad91acbcbfd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1016,7 +1016,7 @@ pub fn get_arg_name(pat: &Pat) -> Option<ast::Name> { } pub fn int_bits(tcx: TyCtxt<'_, '_, '_>, ity: ast::IntTy) -> u64 { - layout::Integer::from_attr(tcx, attr::IntType::SignedInt(ity)).size().bits() + layout::Integer::from_attr(&tcx, attr::IntType::SignedInt(ity)).size().bits() } #[allow(clippy::cast_possible_wrap)] @@ -1035,7 +1035,7 @@ pub fn unsext(tcx: TyCtxt<'_, '_, '_>, u: i128, ity: ast::IntTy) -> u128 { /// clip unused bytes pub fn clip(tcx: TyCtxt<'_, '_, '_>, u: u128, ity: ast::UintTy) -> u128 { - let bits = layout::Integer::from_attr(tcx, attr::IntType::UnsignedInt(ity)).size().bits(); + let bits = layout::Integer::from_attr(&tcx, attr::IntType::UnsignedInt(ity)).size().bits(); let amt = 128 - bits; (u << amt) >> amt } diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 4db7ce95712..c08c4b693b8 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,9 +7,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - - - #![warn(clippy::mixed_case_hex_literals)] #![warn(clippy::unseparated_literal_suffix)] #![warn(clippy::zero_prefixed_literal)] @@ -64,4 +61,11 @@ fn main() { let fail21 = 4___16; let fail22 = 3__4___23; let fail23 = 3__16___23; + + let fail24 = 12.34_64; + let fail25 = 1E2_32; + let fail26 = 43E7_64; + let fail27 = 243E17_32; + let fail28 = 241251235E723_64; + let fail29 = 42279.911_32; } diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 4e26b9dd321..d2a50e2ded5 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -1,182 +1,218 @@ error: inconsistent casing in hexadecimal literal - --> $DIR/literals.rs:24:17 + --> $DIR/literals.rs:21:17 | -24 | let fail1 = 0xabCD; +21 | let fail1 = 0xabCD; | ^^^^^^ | = note: `-D clippy::mixed-case-hex-literals` implied by `-D warnings` error: inconsistent casing in hexadecimal literal - --> $DIR/literals.rs:25:17 + --> $DIR/literals.rs:22:17 | -25 | let fail2 = 0xabCD_u32; +22 | let fail2 = 0xabCD_u32; | ^^^^^^^^^^ error: inconsistent casing in hexadecimal literal - --> $DIR/literals.rs:26:17 + --> $DIR/literals.rs:23:17 | -26 | let fail2 = 0xabCD_isize; +23 | let fail2 = 0xabCD_isize; | ^^^^^^^^^^^^ error: integer type suffix should be separated by an underscore - --> $DIR/literals.rs:27:27 + --> $DIR/literals.rs:24:27 | -27 | let fail_multi_zero = 000_123usize; +24 | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ | = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings` error: this is a decimal constant - --> $DIR/literals.rs:27:27 + --> $DIR/literals.rs:24:27 | -27 | let fail_multi_zero = 000_123usize; +24 | let fail_multi_zero = 000_123usize; | ^^^^^^^^^^^^ | = note: `-D clippy::zero-prefixed-literal` implied by `-D warnings` help: if you mean to use a decimal constant, remove the `0` to remove confusion | -27 | let fail_multi_zero = 123usize; +24 | let fail_multi_zero = 123usize; | ^^^^^^^^ help: if you mean to use an octal constant, use `0o` | -27 | let fail_multi_zero = 0o123usize; +24 | let fail_multi_zero = 0o123usize; | ^^^^^^^^^^ error: integer type suffix should be separated by an underscore - --> $DIR/literals.rs:32:17 + --> $DIR/literals.rs:29:17 | -32 | let fail3 = 1234i32; +29 | let fail3 = 1234i32; | ^^^^^^^ error: integer type suffix should be separated by an underscore - --> $DIR/literals.rs:33:17 + --> $DIR/literals.rs:30:17 | -33 | let fail4 = 1234u32; +30 | let fail4 = 1234u32; | ^^^^^^^ error: integer type suffix should be separated by an underscore - --> $DIR/literals.rs:34:17 + --> $DIR/literals.rs:31:17 | -34 | let fail5 = 1234isize; +31 | let fail5 = 1234isize; | ^^^^^^^^^ error: integer type suffix should be separated by an underscore - --> $DIR/literals.rs:35:17 + --> $DIR/literals.rs:32:17 | -35 | let fail6 = 1234usize; +32 | let fail6 = 1234usize; | ^^^^^^^^^ error: float type suffix should be separated by an underscore - --> $DIR/literals.rs:36:17 + --> $DIR/literals.rs:33:17 | -36 | let fail7 = 1.5f32; +33 | let fail7 = 1.5f32; | ^^^^^^ error: this is a decimal constant - --> $DIR/literals.rs:40:17 + --> $DIR/literals.rs:37:17 | -40 | let fail8 = 0123; +37 | let fail8 = 0123; | ^^^^ help: if you mean to use a decimal constant, remove the `0` to remove confusion | -40 | let fail8 = 123; +37 | let fail8 = 123; | ^^^ help: if you mean to use an octal constant, use `0o` | -40 | let fail8 = 0o123; +37 | let fail8 = 0o123; | ^^^^^ error: long literal lacking separators - --> $DIR/literals.rs:51:17 + --> $DIR/literals.rs:48:17 | -51 | let fail9 = 0xabcdef; +48 | let fail9 = 0xabcdef; | ^^^^^^^^ help: consider: `0x00ab_cdef` | = note: `-D clippy::unreadable-literal` implied by `-D warnings` error: long literal lacking separators - --> $DIR/literals.rs:52:18 + --> $DIR/literals.rs:49:18 | -52 | let fail10 = 0xBAFEBAFE; +49 | let fail10 = 0xBAFEBAFE; | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` error: long literal lacking separators - --> $DIR/literals.rs:53:18 + --> $DIR/literals.rs:50:18 | -53 | let fail11 = 0xabcdeff; +50 | let fail11 = 0xabcdeff; | ^^^^^^^^^ help: consider: `0x0abc_deff` error: long literal lacking separators - --> $DIR/literals.rs:54:18 + --> $DIR/literals.rs:51:18 | -54 | let fail12 = 0xabcabcabcabcabcabc; +51 | let fail12 = 0xabcabcabcabcabcabc; | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` error: digit groups should be smaller - --> $DIR/literals.rs:55:18 + --> $DIR/literals.rs:52:18 | -55 | let fail13 = 0x1_23456_78901_usize; +52 | let fail13 = 0x1_23456_78901_usize; | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `0x0123_4567_8901_usize` | = note: `-D clippy::large-digit-groups` implied by `-D warnings` error: mistyped literal suffix - --> $DIR/literals.rs:57:18 + --> $DIR/literals.rs:54:18 | -57 | let fail14 = 2_32; +54 | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` | = note: #[deny(clippy::mistyped_literal_suffixes)] on by default error: mistyped literal suffix - --> $DIR/literals.rs:58:18 + --> $DIR/literals.rs:55:18 | -58 | let fail15 = 4_64; +55 | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/literals.rs:59:18 + --> $DIR/literals.rs:56:18 | -59 | let fail16 = 7_8; +56 | let fail16 = 7_8; | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/literals.rs:60:18 + --> $DIR/literals.rs:57:18 | -60 | let fail17 = 23_16; +57 | let fail17 = 23_16; | ^^^^^ help: did you mean to write: `23_i16` error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:62:18 + --> $DIR/literals.rs:59:18 | -62 | let fail19 = 12_3456_21; +59 | let fail19 = 12_3456_21; | ^^^^^^^^^^ help: consider: `12_345_621` | = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` error: mistyped literal suffix - --> $DIR/literals.rs:63:18 + --> $DIR/literals.rs:60:18 | -63 | let fail20 = 2__8; +60 | let fail20 = 2__8; | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/literals.rs:64:18 + --> $DIR/literals.rs:61:18 | -64 | let fail21 = 4___16; +61 | let fail21 = 4___16; | ^^^^^^ help: did you mean to write: `4_i16` error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:65:18 + --> $DIR/literals.rs:62:18 | -65 | let fail22 = 3__4___23; +62 | let fail22 = 3__4___23; | ^^^^^^^^^ help: consider: `3_423` error: digits grouped inconsistently by underscores - --> $DIR/literals.rs:66:18 + --> $DIR/literals.rs:63:18 | -66 | let fail23 = 3__16___23; +63 | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` -error: aborting due to 25 previous errors +error: mistyped literal suffix + --> $DIR/literals.rs:65:18 + | +65 | let fail24 = 12.34_64; + | ^^^^^^^^ help: did you mean to write: `12.34_f64` + +error: mistyped literal suffix + --> $DIR/literals.rs:66:18 + | +66 | let fail25 = 1E2_32; + | ^^^^^^ help: did you mean to write: `1E2_f32` + +error: mistyped literal suffix + --> $DIR/literals.rs:67:18 + | +67 | let fail26 = 43E7_64; + | ^^^^^^^ help: did you mean to write: `43E7_f64` + +error: mistyped literal suffix + --> $DIR/literals.rs:68:18 + | +68 | let fail27 = 243E17_32; + | ^^^^^^^^^ help: did you mean to write: `243E17_f32` + +error: mistyped literal suffix + --> $DIR/literals.rs:69:18 + | +69 | let fail28 = 241251235E723_64; + | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` + +error: mistyped literal suffix + --> $DIR/literals.rs:70:18 + | +70 | let fail29 = 42279.911_32; + | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` + +error: aborting due to 31 previous errors diff --git a/util/update_lints.py b/util/update_lints.py index 221069d353c..4467b5c0cf7 100755 --- a/util/update_lints.py +++ b/util/update_lints.py @@ -10,245 +10,11 @@ # option. This file may not be copied, modified, or distributed # except according to those terms. - -# Generate a Markdown table of all lints, and put it in README.md. -# With -n option, only print the new table to stdout. -# With -c option, print a warning and set exit status to 1 if a file would be -# changed. - -import os -import re import sys -from subprocess import call - -declare_deprecated_lint_re = re.compile(r''' - declare_deprecated_lint! \s* [{(] \s* - pub \s+ (?P<name>[A-Z_][A-Z_0-9]*) \s*,\s* - " (?P<desc>(?:[^"\\]+|\\.)*) " \s* [})] -''', re.VERBOSE | re.DOTALL) - -declare_clippy_lint_re = re.compile(r''' - declare_clippy_lint! \s* [{(] \s* - pub \s+ (?P<name>[A-Z_][A-Z_0-9]*) \s*,\s* - (?P<cat>[a-z_]+) \s*,\s* - " (?P<desc>(?:[^"\\]+|\\.)*) " \s* [})] -''', re.VERBOSE | re.DOTALL) - -nl_escape_re = re.compile(r'\\\n\s*') - -docs_link = 'https://rust-lang-nursery.github.io/rust-clippy/master/index.html' - - -def collect(deprecated_lints, clippy_lints, fn): - """Collect all lints from a file. - - Adds entries to the lints list as `(module, name, level, desc)`. - """ - with open(fn) as fp: - code = fp.read() - - for match in declare_deprecated_lint_re.finditer(code): - # remove \-newline escapes from description string - desc = nl_escape_re.sub('', match.group('desc')) - deprecated_lints.append((os.path.splitext(os.path.basename(fn))[0], - match.group('name').lower(), - desc.replace('\\"', '"'))) - - for match in declare_clippy_lint_re.finditer(code): - # remove \-newline escapes from description string - desc = nl_escape_re.sub('', match.group('desc')) - cat = match.group('cat') - if cat in ('internal', 'internal_warn'): - continue - module_name = os.path.splitext(os.path.basename(fn))[0] - if module_name == 'mod': - module_name = os.path.basename(os.path.dirname(fn)) - clippy_lints[cat].append((module_name, - match.group('name').lower(), - "allow", - desc.replace('\\"', '"'))) - - -def gen_group(lints): - """Write lint group (list of all lints in the form module::NAME).""" - for (module, name, _, _) in sorted(lints): - yield ' %s::%s,\n' % (module, name.upper()) - - -def gen_mods(lints): - """Declare modules""" - - for module in sorted(set(lint[0] for lint in lints)): - yield 'pub mod %s;\n' % module - - -def gen_deprecated(lints): - """Declare deprecated lints""" - - for lint in lints: - yield ' store.register_removed(\n' - yield ' "%s",\n' % lint[1] - yield ' "%s",\n' % lint[2] - yield ' );\n' - - -def replace_region(fn, region_start, region_end, callback, - replace_start=True, write_back=True): - """Replace a region in a file delimited by two lines matching regexes. - - A callback is called to write the new region. If `replace_start` is true, - the start delimiter line is replaced as well. The end delimiter line is - never replaced. - """ - # read current content - with open(fn) as fp: - lines = list(fp) - - found = False - - # replace old region with new region - new_lines = [] - in_old_region = False - for line in lines: - if in_old_region: - if re.search(region_end, line): - in_old_region = False - new_lines.extend(callback()) - new_lines.append(line) - elif re.search(region_start, line): - if not replace_start: - new_lines.append(line) - # old region starts here - in_old_region = True - found = True - else: - new_lines.append(line) - - if not found: - print("regex " + region_start + " not found") - - # write back to file - if write_back: - with open(fn, 'w') as fp: - fp.writelines(new_lines) - - # if something changed, return true - return lines != new_lines - - -def main(print_only=False, check=False): - deprecated_lints = [] - clippy_lints = { - "correctness": [], - "style": [], - "complexity": [], - "perf": [], - "restriction": [], - "pedantic": [], - "cargo": [], - "nursery": [], - } - - # check directory - if not os.path.isfile('clippy_lints/src/lib.rs'): - print('Error: call this script from clippy checkout directory!') - return - - # collect all lints from source files - for root, dirs, files in os.walk('clippy_lints/src'): - for fn in files: - if fn.endswith('.rs'): - collect(deprecated_lints, clippy_lints, - os.path.join(root, fn)) - - # determine version - with open('Cargo.toml') as fp: - for line in fp: - if line.startswith('version ='): - clippy_version = line.split()[2].strip('"') - break - else: - print('Error: version not found in Cargo.toml!') - return - - all_lints = [] - clippy_lint_groups = [ - "correctness", - "style", - "complexity", - "perf", - ] - clippy_lint_list = [] - for x in clippy_lint_groups: - clippy_lint_list += clippy_lints[x] - for _, value in clippy_lints.iteritems(): - all_lints += value - - if print_only: - call(["./util/dev", "update_lints", "--print-only"]) - return - - # update the lint counter in README.md - changed = replace_region( - 'README.md', - r'^\[There are \d+ lints included in this crate!\]\(https://rust-lang-nursery.github.io/rust-clippy/master/index.html\)$', "", - lambda: ['[There are %d lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html)\n' % - (len(all_lints))], - write_back=not check) - - # update the links in the CHANGELOG - changed |= replace_region( - 'CHANGELOG.md', - "<!-- begin autogenerated links to lint list -->", - "<!-- end autogenerated links to lint list -->", - lambda: ["[`{0}`]: {1}#{0}\n".format(l[1], docs_link) for l in - sorted(all_lints + deprecated_lints, - key=lambda l: l[1])], - replace_start=False, write_back=not check) - - # update version of clippy_lints in Cargo.toml - changed |= replace_region( - 'Cargo.toml', r'# begin automatic update', '# end automatic update', - lambda: ['clippy_lints = { version = "%s", path = "clippy_lints" }\n' % - clippy_version], - replace_start=False, write_back=not check) - - # update version of clippy_lints in Cargo.toml - changed |= replace_region( - 'clippy_lints/Cargo.toml', r'# begin automatic update', '# end automatic update', - lambda: ['version = "%s"\n' % clippy_version], - replace_start=False, write_back=not check) - - # update the `pub mod` list - changed |= replace_region( - 'clippy_lints/src/lib.rs', r'begin lints modules', r'end lints modules', - lambda: gen_mods(all_lints), - replace_start=False, write_back=not check) - - # same for "clippy::*" lint collections - changed |= replace_region( - 'clippy_lints/src/lib.rs', r'reg.register_lint_group\("clippy::all"', r'\]\);', - lambda: gen_group(clippy_lint_list), - replace_start=False, write_back=not check) - - for key, value in clippy_lints.iteritems(): - # same for "clippy::*" lint collections - changed |= replace_region( - 'clippy_lints/src/lib.rs', r'reg.register_lint_group\("clippy::' + key + r'"', r'\]\);', - lambda: gen_group(value), - replace_start=False, write_back=not check) - - # same for "deprecated" lint collection - changed |= replace_region( - 'clippy_lints/src/lib.rs', r'begin deprecated lints', r'end deprecated lints', - lambda: gen_deprecated(deprecated_lints), - replace_start=False, - write_back=not check) - - if check and changed: - print('Please run util/update_lints.py to regenerate lints lists.') - return 1 +def main(): + print('Error: Please use `util/dev` to update lints') + return 1 if __name__ == '__main__': - sys.exit(main(print_only='-n' in sys.argv, check='-c' in sys.argv)) + sys.exit(main()) |
