Merge commit '2f6439ae6a6803d030cceb3ee14c9150e91b328b' into clippyup

This commit is contained in:
flip1995 2020-10-09 12:45:29 +02:00
parent adb7fc6283
commit fbf2430f02
109 changed files with 1502 additions and 616 deletions

View file

@ -6,11 +6,129 @@ document.
## Unreleased / In Rust Nightly ## Unreleased / In Rust Nightly
[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) [e636b88...master](https://github.com/rust-lang/rust-clippy/compare/e636b88...master)
## Rust 1.48
Current beta, release 2020-11-19
[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
### New lints
* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
### Moves and Deprecations
* Downgrade [`verbose_bit_mask`] to pedantic
[#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
### Enhancements
* Extend [`precedence`] to handle chains of methods combined with unary negation
[#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
[#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
[#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
[#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
* Avoid [`redundant_pattern_matching`] triggering in macros
[#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
* [`option_if_let_else`]: distinguish pure from impure `else` expressions
[#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
* [`needless_doctest_main`]: parse doctests instead of using textual search
[#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
[#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
[#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
[#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
### False Positive Fixes
* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
[#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
[#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
[#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
[#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
* [`doc_markdown`]: allow using "GraphQL" without backticks
[#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self`
[#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
[#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
* [`should_implement_trait`]: ignore methods with lifetime parameters
[#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
* [`needless_return`]: avoid linting if a temporary borrows a local variable
[#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
[#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
[#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
[#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
### Suggestion Fixes/Improvements
* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
[#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
* [`useless_conversion`]: show the type in the error message
[#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
[#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
[#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
[#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
* [`collapsible_if`]: don't use expanded code in the suggestion
[#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
[#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
* [`unit_arg`]: improve the readability of the suggestion
[#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
[#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
* Show line count and max lines in [`too_many_lines`] lint message
[#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
[#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
[#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
[#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
* Make lint messages adhere to rustc dev guide conventions
[#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
### ICE Fixes
* Fix ICE in [`repeat_once`]
[#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
### Documentation Improvements
* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
[#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
* [`unnecessary_mut_passed`]: fix typo
[#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
* Add example of false positive to [`ptr_arg`] docs.
[#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
[#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
## Rust 1.47 ## Rust 1.47
Current beta, release 2020-10-08 Current stable, released 2020-10-08
[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) [c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
@ -112,7 +230,7 @@ Current beta, release 2020-10-08
## Rust 1.46 ## Rust 1.46
Current stable, released 2020-08-27 Released 2020-08-27
[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) [7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
@ -1559,6 +1677,7 @@ Released 2018-09-13
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@ -1634,6 +1753,8 @@ Released 2018-09-13
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
@ -1644,6 +1765,7 @@ Released 2018-09-13
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
@ -1919,6 +2041,5 @@ Released 2018-09-13
[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
<!-- end autogenerated links to lint list --> <!-- end autogenerated links to lint list -->

View file

@ -5,7 +5,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) [There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

View file

@ -8,8 +8,8 @@ edition = "2018"
bytecount = "0.6" bytecount = "0.6"
clap = "2.33" clap = "2.33"
itertools = "0.9" itertools = "0.9"
opener = "0.4"
regex = "1" regex = "1"
lazy_static = "1.0"
shell-escape = "0.1" shell-escape = "0.1"
walkdir = "2" walkdir = "2"

View file

@ -1,42 +1,47 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![feature(once_cell)]
use itertools::Itertools; use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use walkdir::WalkDir; use walkdir::WalkDir;
pub mod fmt; pub mod fmt;
pub mod new_lint; pub mod new_lint;
pub mod ra_setup; pub mod ra_setup;
pub mod serve;
pub mod stderr_length_check; pub mod stderr_length_check;
pub mod update_lints; pub mod update_lints;
lazy_static! { static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new( Regex::new(
r#"(?x) r#"(?x)
declare_clippy_lint!\s*[\{(] declare_clippy_lint!\s*[\{(]
(?:\s+///.*)* (?:\s+///.*)*
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s* \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
(?P<cat>[a-z_]+)\s*,\s* (?P<cat>[a-z_]+)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
"# "#,
) )
.unwrap(); .unwrap()
static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new( });
static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
Regex::new(
r#"(?x) r#"(?x)
declare_deprecated_lint!\s*[{(]\s* declare_deprecated_lint!\s*[{(]\s*
(?:\s+///.*)* (?:\s+///.*)*
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s* \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
"# "#,
) )
.unwrap(); .unwrap()
static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap(); });
} static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";

View file

@ -1,7 +1,7 @@
#![cfg_attr(feature = "deny-warnings", deny(warnings))] #![cfg_attr(feature = "deny-warnings", deny(warnings))]
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
fn main() { fn main() {
let matches = App::new("Clippy developer tooling") let matches = App::new("Clippy developer tooling")
@ -100,6 +100,19 @@ fn main() {
.required(true), .required(true),
), ),
) )
.subcommand(
SubCommand::with_name("serve")
.about("Launch a local 'ALL the Clippy Lints' website in a browser")
.arg(
Arg::with_name("port")
.long("port")
.short("p")
.help("Local port for the http server")
.default_value("8000")
.validator_os(serve::validate_port),
)
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
)
.get_matches(); .get_matches();
match matches.subcommand() { match matches.subcommand() {
@ -129,6 +142,11 @@ fn main() {
stderr_length_check::check(); stderr_length_check::check();
}, },
("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
("serve", Some(matches)) => {
let port = matches.value_of("port").unwrap().parse().unwrap();
let lint = matches.value_of("lint");
serve::run(port, lint);
},
_ => {}, _ => {},
} }
} }

64
clippy_dev/src/serve.rs Normal file
View file

@ -0,0 +1,64 @@
use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::process::Command;
use std::thread;
use std::time::{Duration, SystemTime};
pub fn run(port: u16, lint: Option<&str>) -> ! {
let mut url = Some(match lint {
None => format!("http://localhost:{}", port),
Some(lint) => format!("http://localhost:{}/#{}", port, lint),
});
loop {
if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
Command::new("python3")
.arg("util/export.py")
.spawn()
.unwrap()
.wait()
.unwrap();
}
if let Some(url) = url.take() {
thread::spawn(move || {
Command::new("python3")
.arg("-m")
.arg("http.server")
.arg(port.to_string())
.current_dir("util/gh-pages")
.spawn()
.unwrap();
// Give some time for python to start
thread::sleep(Duration::from_millis(500));
// Launch browser after first export.py has completed and http.server is up
let _ = opener::open(url);
});
}
thread::sleep(Duration::from_millis(1000));
}
}
fn mtime(path: impl AsRef<Path>) -> SystemTime {
let path = path.as_ref();
if path.is_dir() {
path.read_dir()
.into_iter()
.flatten()
.flatten()
.map(|entry| mtime(&entry.path()))
.max()
.unwrap_or(SystemTime::UNIX_EPOCH)
} else {
path.metadata()
.and_then(|metadata| metadata.modified())
.unwrap_or(SystemTime::UNIX_EPOCH)
}
}
#[allow(clippy::missing_errors_doc)]
pub fn validate_port(arg: &OsStr) -> Result<(), OsString> {
match arg.to_string_lossy().parse::<u16>() {
Ok(_port) => Ok(()),
Err(err) => Err(OsString::from(err.to_string())),
}
}

View file

@ -20,7 +20,6 @@ edition = "2018"
cargo_metadata = "0.11.1" cargo_metadata = "0.11.1"
if_chain = "1.0.0" if_chain = "1.0.0"
itertools = "0.9" itertools = "0.9"
lazy_static = "1.0.2"
pulldown-cmark = { version = "0.8", default-features = false } pulldown-cmark = { version = "0.8", default-features = false }
quine-mc_cluskey = "0.2.2" quine-mc_cluskey = "0.2.2"
regex-syntax = "0.6" regex-syntax = "0.6"

View file

@ -0,0 +1,125 @@
use std::fmt;
use crate::utils::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::{declare_lint_pass, declare_tool_lint};
#[derive(Clone, Copy, PartialEq, Eq)]
enum AsmStyle {
Intel,
Att,
}
impl fmt::Display for AsmStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AsmStyle::Intel => f.write_str("Intel"),
AsmStyle::Att => f.write_str("AT&T"),
}
}
}
impl std::ops::Not for AsmStyle {
type Output = AsmStyle;
fn not(self) -> AsmStyle {
match self {
AsmStyle::Intel => AsmStyle::Att,
AsmStyle::Att => AsmStyle::Intel,
}
}
}
fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) {
if let ExprKind::InlineAsm(ref inline_asm) = expr.kind {
let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) {
AsmStyle::Att
} else {
AsmStyle::Intel
};
if style == check_for {
span_lint_and_help(
cx,
lint,
expr.span,
&format!("{} x86 assembly syntax used", style),
None,
&format!("use {} x86 assembly syntax", !style),
);
}
}
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of Intel x86 assembly syntax.
///
/// **Why is this bad?** The lint has been enabled to indicate a preference
/// for AT&T x86 assembly syntax.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
/// # }
/// ```
/// Use instead:
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
/// # }
/// ```
pub INLINE_ASM_X86_INTEL_SYNTAX,
restriction,
"prefer AT&T x86 assembly syntax"
}
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
impl EarlyLintPass for InlineAsmX86IntelSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel);
}
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of AT&T x86 assembly syntax.
///
/// **Why is this bad?** The lint has been enabled to indicate a preference
/// for Intel x86 assembly syntax.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
/// # }
/// ```
/// Use instead:
/// ```rust,no_run
/// # #![feature(asm)]
/// # unsafe { let ptr = "".as_ptr();
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
/// # }
/// ```
pub INLINE_ASM_X86_ATT_SYNTAX,
restriction,
"prefer Intel x86 assembly syntax"
}
declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
impl EarlyLintPass for InlineAsmX86AttSyntax {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att);
}
}

View file

@ -137,17 +137,17 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// // Good (as inner attribute) /// // Good (as inner attribute)
/// #![inline(always)] /// #![allow(dead_code)]
/// ///
/// fn this_is_fine() { } /// fn this_is_fine() { }
/// ///
/// // Bad /// // Bad
/// #[inline(always)] /// #[allow(dead_code)]
/// ///
/// fn not_quite_good_code() { } /// fn not_quite_good_code() { }
/// ///
/// // Good (as outer attribute) /// // Good (as outer attribute)
/// #[inline(always)] /// #[allow(dead_code)]
/// fn this_is_fine_too() { } /// fn this_is_fine_too() { }
/// ``` /// ```
pub EMPTY_LINE_AFTER_OUTER_ATTR, pub EMPTY_LINE_AFTER_OUTER_ATTR,

View file

@ -0,0 +1,73 @@
use crate::utils::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Lints for specific trait methods defined in clippy.toml
///
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it would be beneficial to lint for them as needed.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust,ignore
/// // example code where clippy issues a warning
/// foo.bad_method(); // Foo::bad_method is disallowed in the configuration
/// ```
/// Use instead:
/// ```rust,ignore
/// // example code which does not raise clippy warning
/// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed
/// ```
pub DISALLOWED_METHOD,
nursery,
"use of a disallowed method call"
}
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
}
impl DisallowedMethod {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
}
}
}
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let method_call = cx.get_def_path(def_id);
if self.disallowed.contains(&method_call) {
let method = method_call
.iter()
.map(|s| s.to_ident_string())
.collect::<Vec<_>>()
.join("::");
span_lint(
cx,
DISALLOWED_METHOD,
expr.span,
&format!("use of a disallowed method `{}`", method),
);
}
}
}
}

View file

@ -6,6 +6,7 @@ use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_target::abi::LayoutOf; use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use crate::utils::span_lint; use crate::utils::span_lint;
@ -60,12 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
fn check_fn( fn check_fn(
&mut self, &mut self,
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
_: intravisit::FnKind<'tcx>, fn_kind: intravisit::FnKind<'tcx>,
_: &'tcx FnDecl<'_>, _: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>, body: &'tcx Body<'_>,
_: Span, _: Span,
hir_id: HirId, hir_id: HirId,
) { ) {
if let Some(header) = fn_kind.header() {
if header.abi != Abi::Rust {
return;
}
}
// If the method is an impl for a trait, don't warn. // If the method is an impl for a trait, don't warn.
let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_id = cx.tcx.hir().get_parent_item(hir_id);
let parent_node = cx.tcx.hir().find(parent_id); let parent_node = cx.tcx.hir().find(parent_id);

View file

@ -7,6 +7,7 @@
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(drain_filter)] #![feature(drain_filter)]
#![feature(in_band_lifetimes)] #![feature(in_band_lifetimes)]
#![feature(once_cell)]
#![feature(or_patterns)] #![feature(or_patterns)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(stmt_expr_attributes)] #![feature(stmt_expr_attributes)]
@ -153,6 +154,7 @@ mod utils;
mod approx_const; mod approx_const;
mod arithmetic; mod arithmetic;
mod as_conversions; mod as_conversions;
mod asm_syntax;
mod assertions_on_constants; mod assertions_on_constants;
mod assign_ops; mod assign_ops;
mod async_yields_async; mod async_yields_async;
@ -176,6 +178,7 @@ mod dbg_macro;
mod default_trait_access; mod default_trait_access;
mod dereference; mod dereference;
mod derive; mod derive;
mod disallowed_method;
mod doc; mod doc;
mod double_comparison; mod double_comparison;
mod double_parens; mod double_parens;
@ -489,6 +492,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&arithmetic::FLOAT_ARITHMETIC, &arithmetic::FLOAT_ARITHMETIC,
&arithmetic::INTEGER_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC,
&as_conversions::AS_CONVERSIONS, &as_conversions::AS_CONVERSIONS,
&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
&assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
&assign_ops::ASSIGN_OP_PATTERN, &assign_ops::ASSIGN_OP_PATTERN,
&assign_ops::MISREFACTORED_ASSIGN_OP, &assign_ops::MISREFACTORED_ASSIGN_OP,
@ -529,6 +534,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::DERIVE_ORD_XOR_PARTIAL_ORD,
&derive::EXPL_IMPL_CLONE_ON_COPY, &derive::EXPL_IMPL_CLONE_ON_COPY,
&derive::UNSAFE_DERIVE_DESERIALIZE, &derive::UNSAFE_DERIVE_DESERIALIZE,
&disallowed_method::DISALLOWED_METHOD,
&doc::DOC_MARKDOWN, &doc::DOC_MARKDOWN,
&doc::MISSING_ERRORS_DOC, &doc::MISSING_ERRORS_DOC,
&doc::MISSING_SAFETY_DOC, &doc::MISSING_SAFETY_DOC,
@ -851,9 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&types::UNIT_CMP, &types::UNIT_CMP,
&types::UNNECESSARY_CAST, &types::UNNECESSARY_CAST,
&types::VEC_BOX, &types::VEC_BOX,
&unicode::INVISIBLE_CHARACTERS,
&unicode::NON_ASCII_LITERAL, &unicode::NON_ASCII_LITERAL,
&unicode::UNICODE_NOT_NFC, &unicode::UNICODE_NOT_NFC,
&unicode::ZERO_WIDTH_SPACE,
&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
&unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::FN_ADDRESS_COMPARISONS,
&unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS,
@ -1120,11 +1126,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box manual_strip::ManualStrip);
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC),
LintId::of(&as_conversions::AS_CONVERSIONS), LintId::of(&as_conversions::AS_CONVERSIONS),
LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(&create_dir::CREATE_DIR), LintId::of(&create_dir::CREATE_DIR),
LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&dbg_macro::DBG_MACRO),
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
@ -1159,6 +1172,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_REUSE),
LintId::of(&shadow::SHADOW_SAME), LintId::of(&shadow::SHADOW_SAME),
LintId::of(&strings::STRING_ADD), LintId::of(&strings::STRING_ADD),
LintId::of(&types::RC_BUFFER),
LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
LintId::of(&write::PRINT_STDOUT), LintId::of(&write::PRINT_STDOUT),
@ -1463,7 +1477,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(&swap::ALMOST_SWAPPED), LintId::of(&swap::ALMOST_SWAPPED),
@ -1492,14 +1505,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::CHAR_LIT_AS_U8),
LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST),
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(&types::RC_BUFFER),
LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::TYPE_COMPLEXITY),
LintId::of(&types::UNIT_ARG), LintId::of(&types::UNIT_ARG),
LintId::of(&types::UNIT_CMP), LintId::of(&types::UNIT_CMP),
LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX), LintId::of(&types::VEC_BOX),
LintId::of(&unicode::ZERO_WIDTH_SPACE), LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
@ -1592,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&neg_multiply::NEG_MULTIPLY),
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&panic_unimplemented::PANIC_PARAMS),
@ -1604,7 +1618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::LET_AND_RETURN),
LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(&try_err::TRY_ERR), LintId::of(&try_err::TRY_ERR),
@ -1747,8 +1760,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::FLOAT_CMP),
LintId::of(&misc::MODULO_ONE), LintId::of(&misc::MODULO_ONE),
LintId::of(&mut_key::MUTABLE_KEY_TYPE), LintId::of(&mut_key::MUTABLE_KEY_TYPE),
LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ptr::MUT_FROM_REF),
@ -1766,7 +1777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::CAST_REF_TO_MUT),
LintId::of(&types::UNIT_CMP), LintId::of(&types::UNIT_CMP),
LintId::of(&unicode::ZERO_WIDTH_SPACE), LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
@ -1793,7 +1804,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(&types::BOX_VEC), LintId::of(&types::BOX_VEC),
LintId::of(&types::RC_BUFFER),
LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::REDUNDANT_ALLOCATION),
LintId::of(&vec::USELESS_VEC), LintId::of(&vec::USELESS_VEC),
]); ]);
@ -1807,6 +1817,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(&disallowed_method::DISALLOWED_METHOD),
LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),
@ -1818,6 +1829,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&needless_borrow::NEEDLESS_BORROW), LintId::of(&needless_borrow::NEEDLESS_BORROW),
LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&transmute::USELESS_TRANSMUTE), LintId::of(&transmute::USELESS_TRANSMUTE),
LintId::of(&use_self::USE_SELF), LintId::of(&use_self::USE_SELF),
]); ]);
@ -1896,6 +1908,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
} }
// only exists to let the dogfood integration test works. // only exists to let the dogfood integration test works.

View file

@ -1,21 +1,22 @@
use crate::utils::paths;
use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{ use rustc_hir::intravisit::{
walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
NestedVisitorMap, Visitor,
}; };
use rustc_hir::FnRetTy::Return; use rustc_hir::FnRetTy::Return;
use rustc_hir::{ use rustc_hir::{
BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn,
TyKind, WhereClause, WherePredicate, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span; use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Symbol}; use rustc_span::symbol::{kw, Symbol};
use std::iter::FromIterator;
use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method};
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for lifetime annotations which can be removed by /// **What it does:** Checks for lifetime annotations which can be removed by
@ -25,8 +26,11 @@ declare_clippy_lint! {
/// complicated, while there is nothing out of the ordinary going on. Removing /// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code. /// them leads to more readable code.
/// ///
/// **Known problems:** Potential false negatives: we bail out if the function /// **Known problems:**
/// has a `where` clause where lifetimes are mentioned. /// - We bail out if the function has a `where` clause where lifetimes
/// are mentioned due to potenial false positives.
/// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
/// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
} }
/// The lifetime of a &-reference. /// The lifetime of a &-reference.
#[derive(PartialEq, Eq, Hash, Debug)] #[derive(PartialEq, Eq, Hash, Debug, Clone)]
enum RefLt { enum RefLt {
Unnamed, Unnamed,
Static, Static,
@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>(
return; return;
} }
let mut bounds_lts = Vec::new();
let types = generics let types = generics
.params .params
.iter() .iter()
@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>(
if bound.name != LifetimeName::Static && !bound.is_elided() { if bound.name != LifetimeName::Static && !bound.is_elided() {
return; return;
} }
bounds_lts.push(bound);
} }
} }
} }
} }
} }
if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { if could_use_elision(cx, decl, body, &generics.params) {
span_lint( span_lint(
cx, cx,
NEEDLESS_LIFETIMES, NEEDLESS_LIFETIMES,
@ -181,7 +183,6 @@ fn could_use_elision<'tcx>(
func: &'tcx FnDecl<'_>, func: &'tcx FnDecl<'_>,
body: Option<BodyId>, body: Option<BodyId>,
named_generics: &'tcx [GenericParam<'_>], named_generics: &'tcx [GenericParam<'_>],
bounds_lts: Vec<&'tcx Lifetime>,
) -> bool { ) -> bool {
// There are two scenarios where elision works: // There are two scenarios where elision works:
// * no output references, all input references have different LT // * no output references, all input references have different LT
@ -204,15 +205,31 @@ fn could_use_elision<'tcx>(
if let Return(ref ty) = func.output { if let Return(ref ty) = func.output {
output_visitor.visit_ty(ty); output_visitor.visit_ty(ty);
} }
for lt in named_generics {
input_visitor.visit_generic_param(lt)
}
let input_lts = match input_visitor.into_vec() { if input_visitor.abort() || output_visitor.abort() {
Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), return false;
None => return false, }
};
let output_lts = match output_visitor.into_vec() { if allowed_lts
Some(val) => val, .intersection(&FxHashSet::from_iter(
None => return false, input_visitor
}; .nested_elision_site_lts
.iter()
.chain(output_visitor.nested_elision_site_lts.iter())
.cloned()
.filter(|v| matches!(v, RefLt::Named(_))),
))
.next()
.is_some()
{
return false;
}
let input_lts = input_visitor.lts;
let output_lts = output_visitor.lts;
if let Some(body_id) = body { if let Some(body_id) = body {
let mut checker = BodyLifetimeChecker { let mut checker = BodyLifetimeChecker {
@ -277,27 +294,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
allowed_lts allowed_lts
} }
fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
for lt in bounds_lts {
if lt.name != LifetimeName::Static {
vec.push(RefLt::Named(lt.name.ident().name));
}
}
vec
}
/// Number of unique lifetimes in the given vector. /// Number of unique lifetimes in the given vector.
#[must_use] #[must_use]
fn unique_lifetimes(lts: &[RefLt]) -> usize { fn unique_lifetimes(lts: &[RefLt]) -> usize {
lts.iter().collect::<FxHashSet<_>>().len() lts.iter().collect::<FxHashSet<_>>().len()
} }
const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE];
/// A visitor usable for `rustc_front::visit::walk_ty()`. /// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx> { struct RefVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
lts: Vec<RefLt>, lts: Vec<RefLt>,
abort: bool, nested_elision_site_lts: Vec<RefLt>,
unelided_trait_object_lifetime: bool,
} }
impl<'a, 'tcx> RefVisitor<'a, 'tcx> { impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
@ -305,7 +315,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
Self { Self {
cx, cx,
lts: Vec::new(), lts: Vec::new(),
abort: false, nested_elision_site_lts: Vec::new(),
unelided_trait_object_lifetime: false,
} }
} }
@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
} }
} }
fn into_vec(self) -> Option<Vec<RefLt>> { fn all_lts(&self) -> Vec<RefLt> {
if self.abort { self.lts
None .iter()
} else { .chain(self.nested_elision_site_lts.iter())
Some(self.lts) .cloned()
} .collect::<Vec<_>>()
} }
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { fn abort(&self) -> bool {
if let Some(ref last_path_segment) = last_path_segment(qpath).args { self.unelided_trait_object_lifetime
if !last_path_segment.parenthesized
&& !last_path_segment
.args
.iter()
.any(|arg| matches!(arg, GenericArg::Lifetime(_)))
{
let hir_id = ty.hir_id;
match self.cx.qpath_res(qpath, hir_id) {
Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => {
let generics = self.cx.tcx.generics_of(def_id);
for _ in generics.params.as_slice() {
self.record(&None);
}
},
Res::Def(DefKind::Trait, def_id) => {
let trait_def = self.cx.tcx.trait_def(def_id);
for _ in &self.cx.tcx.generics_of(trait_def.def_id).params {
self.record(&None);
}
},
_ => (),
}
}
}
} }
} }
@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
self.record(&Some(*lifetime)); self.record(&Some(*lifetime));
} }
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) {
let trait_ref = &poly_tref.trait_ref;
if CLOSURE_TRAIT_BOUNDS
.iter()
.any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path))
{
let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_trait_ref(trait_ref);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
} else {
walk_poly_trait_ref(self, poly_tref, tbm);
}
}
fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
match ty.kind { match ty.kind {
TyKind::Rptr(ref lt, _) if lt.is_elided() => {
self.record(&None);
},
TyKind::Path(ref path) => {
self.collect_anonymous_lifetimes(path, ty);
},
TyKind::OpaqueDef(item, _) => { TyKind::OpaqueDef(item, _) => {
let map = self.cx.tcx.hir(); let map = self.cx.tcx.hir();
if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { let item = map.expect_item(item.id);
for bound in exist_ty.bounds { walk_item(self, item);
if let GenericBound::Outlives(_) = *bound {
self.record(&None);
}
}
} else {
unreachable!()
}
walk_ty(self, ty); walk_ty(self, ty);
}, },
TyKind::BareFn(&BareFnTy { decl, .. }) => {
let mut sub_visitor = RefVisitor::new(self.cx);
sub_visitor.visit_fn_decl(decl);
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
return;
},
TyKind::TraitObject(bounds, ref lt) => { TyKind::TraitObject(bounds, ref lt) => {
if !lt.is_elided() { if !lt.is_elided() {
self.abort = true; self.unelided_trait_object_lifetime = true;
} }
for bound in bounds { for bound in bounds {
self.visit_poly_trait_ref(bound, TraitBoundModifier::None); self.visit_poly_trait_ref(bound, TraitBoundModifier::None);
@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl
walk_param_bound(&mut visitor, bound); walk_param_bound(&mut visitor, bound);
} }
// and check that all lifetimes are allowed // and check that all lifetimes are allowed
match visitor.into_vec() { return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it));
None => return false,
Some(lts) => {
for lt in lts {
if !allowed_lts.contains(&lt) {
return true;
}
}
},
}
}, },
WherePredicate::EqPredicate(ref pred) => { WherePredicate::EqPredicate(ref pred) => {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);

View file

@ -264,13 +264,10 @@ impl LiteralDigitGrouping {
let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
(exponent, &["32", "64"][..], 'f') (exponent, &["32", "64"][..], 'f')
} else if num_lit.fraction.is_some() {
(&mut num_lit.integer, &["32", "64"][..], 'f')
} else { } else {
num_lit (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
.fraction
.as_mut()
.map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| {
(fraction, &["32", "64"][..], 'f')
})
}; };
let mut split = part.rsplit('_'); let mut split = part.rsplit('_');

View file

@ -3,7 +3,7 @@ use crate::utils::paths;
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::usage::{is_unused, mutated_variables};
use crate::utils::{ use crate::utils::{
get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability,
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>(
let skip = if starts_at_zero { let skip = if starts_at_zero {
String::new() String::new()
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
return;
} else { } else {
format!(".skip({})", snippet(cx, start.span, "..")) format!(".skip({})", snippet(cx, start.span, ".."))
}; };
@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>(
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
String::new() String::new()
} else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
return;
} else { } else {
match limits { match limits {
ast::RangeLimits::Closed => { ast::RangeLimits::Closed => {
@ -2134,7 +2138,7 @@ enum VarState {
DontWarn, DontWarn,
} }
/// Scan a for loop for variables that are incremented exactly once. /// Scan a for loop for variables that are incremented exactly once and not used after that.
struct IncrementVisitor<'a, 'tcx> { struct IncrementVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>, // context reference cx: &'a LateContext<'tcx>, // context reference
states: FxHashMap<HirId, VarState>, // incremented variables states: FxHashMap<HirId, VarState>, // incremented variables
@ -2154,6 +2158,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(def_id) = var_def_id(self.cx, expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) {
let state = self.states.entry(def_id).or_insert(VarState::Initial); let state = self.states.entry(def_id).or_insert(VarState::Initial);
if *state == VarState::IncrOnce {
*state = VarState::DontWarn;
return;
}
match parent.kind { match parent.kind {
ExprKind::AssignOp(op, ref lhs, ref rhs) => { ExprKind::AssignOp(op, ref lhs, ref rhs) => {

View file

@ -18,9 +18,9 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// #[macro_use] /// #[macro_use]
/// use lazy_static; /// use some_macro;
/// ``` /// ```
pub MACRO_USE_IMPORTS, pub MACRO_USE_IMPORTS,
pedantic, pedantic,

View file

@ -400,8 +400,8 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// **What it does:** Checks for usage of `_.map(_).flatten(_)`,
/// ///
/// **Why is this bad?** Readability, this can be written more concisely as a /// **Why is this bad?** Readability, this can be written more concisely as
/// single method call using `_.flat_map(_)` /// `_.flat_map(_)`
/// ///
/// **Known problems:** /// **Known problems:**
/// ///
@ -424,8 +424,8 @@ declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.filter(_).map(_)`, /// **What it does:** Checks for usage of `_.filter(_).map(_)`,
/// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
/// ///
/// **Why is this bad?** Readability, this can be written more concisely as a /// **Why is this bad?** Readability, this can be written more concisely as
/// single method call. /// `_.filter_map(_)`.
/// ///
/// **Known problems:** Often requires a condition + Option/Iterator creation /// **Known problems:** Often requires a condition + Option/Iterator creation
/// inside the closure. /// inside the closure.
@ -452,8 +452,8 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// **What it does:** Checks for usage of `_.filter_map(_).next()`.
/// ///
/// **Why is this bad?** Readability, this can be written more concisely as a /// **Why is this bad?** Readability, this can be written more concisely as
/// single method call. /// `_.find_map(_)`.
/// ///
/// **Known problems:** None /// **Known problems:** None
/// ///
@ -496,8 +496,8 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.find(_).map(_)`. /// **What it does:** Checks for usage of `_.find(_).map(_)`.
/// ///
/// **Why is this bad?** Readability, this can be written more concisely as a /// **Why is this bad?** Readability, this can be written more concisely as
/// single method call. /// `_.find_map(_)`.
/// ///
/// **Known problems:** Often requires a condition + Option/Iterator creation /// **Known problems:** Often requires a condition + Option/Iterator creation
/// inside the closure. /// inside the closure.
@ -1276,8 +1276,8 @@ declare_clippy_lint! {
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
/// ///
/// **Why is this bad?** Readability, this can be written more concisely as a /// **Why is this bad?** Readability, this can be written more concisely as
/// single method call. /// `_.as_deref()`.
/// ///
/// **Known problems:** None. /// **Known problems:** None.
/// ///
@ -1668,9 +1668,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
if let ty::Opaque(def_id, _) = *ret_ty.kind() { if let ty::Opaque(def_id, _) = *ret_ty.kind() {
// one of the associated types must be Self // one of the associated types must be Self
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateAtom::Projection(projection_predicate) = if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
predicate.skip_binders()
{
// walk the associated type and check for Self // walk the associated type and check for Self
if contains_ty(projection_predicate.ty, self_ty) { if contains_ty(projection_predicate.ty, self_ty) {
return; return;

View file

@ -1,10 +1,10 @@
use crate::utils::qualify_min_const_fn::is_min_const_fn;
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use crate::utils::qualify_min_const_fn::is_min_const_fn;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span; use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;

View file

@ -1,4 +1,4 @@
use crate::utils::span_lint_and_sugg; use crate::utils::{in_macro, span_lint_and_sugg};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
if let [segment] = &path.segments[..]; if let [segment] = &path.segments[..];
if segment.ident.name == kw::SelfUpper; if segment.ident.name == kw::SelfUpper;
then { then {
// In case we have a named lifetime, we check if the name comes from expansion.
// If it does, at this point we know the rest of the parameter was written by the user,
// so let them decide what the name of the lifetime should be.
// See #6089 for more details.
let mut applicability = Applicability::MachineApplicable;
let self_param = match (binding_mode, mutbl) { let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), (Mode::Ref(Some(lifetime)), Mutability::Mut) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
format!("&{} mut self", &lifetime.ident.name)
}
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(), (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), (Mode::Ref(Some(lifetime)), Mutability::Not) => {
if in_macro(lifetime.ident.span) {
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
format!("&{} self", &lifetime.ident.name)
}
},
(Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Mut) => "mut self".to_string(),
(Mode::Value, Mutability::Not) => "self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(),
}; };
@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
"the type of the `self` parameter does not need to be arbitrary", "the type of the `self` parameter does not need to be arbitrary",
"consider to change this parameter to", "consider to change this parameter to",
self_param, self_param,
Applicability::MachineApplicable, applicability,
) )
} }
} }
@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
impl EarlyLintPass for NeedlessArbitrarySelfType { impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
if !p.is_self() { // Bail out if the parameter it's not a receiver or was not written by the user
if !p.is_self() || in_macro(p.span) {
return; return;
} }

View file

@ -1,6 +1,6 @@
//! Checks for uses of const which the type is not `Freeze` (`Cell`-free). //! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
//! //!
//! This lint is **deny** by default. //! This lint is **warn** by default.
use std::ptr; use std::ptr;
@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty;
use crate::utils::{in_constant, qpath_res, span_lint_and_then}; use crate::utils::{in_constant, qpath_res, span_lint_and_then};
use if_chain::if_chain; use if_chain::if_chain;
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for declaration of `const` items which is interior /// **What it does:** Checks for declaration of `const` items which is interior
/// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
@ -34,6 +36,15 @@ declare_clippy_lint! {
/// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
/// and this lint should be suppressed. /// and this lint should be suppressed.
/// ///
/// When an enum has variants with interior mutability, use of its non interior mutable
/// variants can generate false positives. See issue
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
///
/// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
@ -49,10 +60,12 @@ declare_clippy_lint! {
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ``` /// ```
pub DECLARE_INTERIOR_MUTABLE_CONST, pub DECLARE_INTERIOR_MUTABLE_CONST,
correctness, style,
"declaring `const` with interior mutability" "declaring `const` with interior mutability"
} }
// FIXME: this is a correctness problem but there's no suitable
// warn-by-default category.
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks if `const` items which is interior mutable (e.g., /// **What it does:** Checks if `const` items which is interior mutable (e.g.,
/// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
@ -64,7 +77,14 @@ declare_clippy_lint! {
/// ///
/// The `const` value should be stored inside a `static` item. /// The `const` value should be stored inside a `static` item.
/// ///
/// **Known problems:** None /// **Known problems:** When an enum has variants with interior mutability, use of its non
/// interior mutable variants can generate false positives. See issue
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
///
/// Types that have underlying or potential interior mutability trigger the lint whether
/// the interior mutable field is used or not. See issues
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
@ -81,7 +101,7 @@ declare_clippy_lint! {
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ``` /// ```
pub BORROW_INTERIOR_MUTABLE_CONST, pub BORROW_INTERIOR_MUTABLE_CONST,
correctness, style,
"referencing `const` with interior mutability" "referencing `const` with interior mutability"
} }

View file

@ -143,7 +143,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
let mut parser = regex_syntax::ParserBuilder::new() let mut parser = regex_syntax::ParserBuilder::new()
.unicode(utf8) .unicode(true)
.allow_invalid_utf8(!utf8) .allow_invalid_utf8(!utf8)
.build(); .build();

View file

@ -69,7 +69,27 @@ declare_clippy_lint! {
/// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used
/// instead. They are shorter but less discoverable than `as_bytes()`. /// instead. They are shorter but less discoverable than `as_bytes()`.
/// ///
/// **Known Problems:** None. /// **Known Problems:**
/// `"str".as_bytes()` and the suggested replacement of `b"str"` are not
/// equivalent because they have different types. The former is `&[u8]`
/// while the latter is `&[u8; 3]`. That means in general they will have a
/// different set of methods and different trait implementations.
///
/// ```compile_fail
/// fn f(v: Vec<u8>) {}
///
/// f("...".as_bytes().to_owned()); // works
/// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec<u8>
///
/// fn g(r: impl std::io::Read) {}
///
/// g("...".as_bytes()); // works
/// g(b"..."); // does not work
/// ```
///
/// The actual equivalent of `"str".as_bytes()` with the same type is not
/// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not
/// more readable than a function call.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
@ -80,7 +100,7 @@ declare_clippy_lint! {
/// let bs = b"a byte string"; /// let bs = b"a byte string";
/// ``` /// ```
pub STRING_LIT_AS_BYTES, pub STRING_LIT_AS_BYTES,
style, nursery,
"calling `as_bytes` on a string literal instead of using a byte string literal" "calling `as_bytes` on a string literal instead of using a byte string literal"
} }

View file

@ -216,18 +216,19 @@ declare_clippy_lint! {
} }
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec /// **What it does:** Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
/// ///
/// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since /// **Why is this bad?** Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
/// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>. /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
/// ///
/// While mutating a buffer type would still be possible with Rc::get_mut(), it only /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
/// works if there are no additional references yet, which defeats the purpose of /// works if there are no additional references yet, which usually defeats the purpose of
/// enclosing it in a shared ownership type. Instead, additionally wrapping the inner /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
/// type with an interior mutable container (such as RefCell or Mutex) would normally /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
/// be used. /// be used.
/// ///
/// **Known problems:** None. /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
/// cases where mutation only happens before there are any additional references.
/// ///
/// **Example:** /// **Example:**
/// ```rust,ignore /// ```rust,ignore
@ -241,7 +242,7 @@ declare_clippy_lint! {
/// fn foo(interned: Rc<str>) { ... } /// fn foo(interned: Rc<str>) { ... }
/// ``` /// ```
pub RC_BUFFER, pub RC_BUFFER,
perf, restriction,
"shared ownership of a buffer type" "shared ownership of a buffer type"
} }

View file

@ -8,18 +8,18 @@ use rustc_span::source_map::Span;
use unicode_normalization::UnicodeNormalization; use unicode_normalization::UnicodeNormalization;
declare_clippy_lint! { declare_clippy_lint! {
/// **What it does:** Checks for the Unicode zero-width space in the code. /// **What it does:** Checks for invisible Unicode characters in the code.
/// ///
/// **Why is this bad?** Having an invisible character in the code makes for all /// **Why is this bad?** Having an invisible character in the code makes for all
/// sorts of April fools, but otherwise is very much frowned upon. /// sorts of April fools, but otherwise is very much frowned upon.
/// ///
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** You don't see it, but there may be a zero-width space /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen
/// somewhere in this text. /// some­where in this text.
pub ZERO_WIDTH_SPACE, pub INVISIBLE_CHARACTERS,
correctness, correctness,
"using a zero-width space in a string literal, which is confusing" "using an invisible character in a string literal, which is confusing"
} }
declare_clippy_lint! { declare_clippy_lint! {
@ -63,7 +63,7 @@ declare_clippy_lint! {
"using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
} }
declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
impl LateLintPass<'_> for Unicode { impl LateLintPass<'_> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
@ -91,14 +91,17 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
let string = snippet(cx, span, ""); let string = snippet(cx, span, "");
if string.contains('\u{200B}') { if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
ZERO_WIDTH_SPACE, INVISIBLE_CHARACTERS,
span, span,
"zero-width space detected", "invisible character detected",
"consider replacing the string with", "consider replacing the string with",
string.replace("\u{200B}", "\\u{200B}"), string
.replace("\u{200B}", "\\u{200B}")
.replace("\u{ad}", "\\u{AD}")
.replace("\u{2060}", "\\u{2060}"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -170,22 +170,12 @@ fn mirrored_exprs(
} }
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
// NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162,
// (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested
// closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more
// than one level of references would add some extra complexity as we would have to compensate
// in the closure body.
if_chain! { if_chain! {
if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
if let name = name_ident.ident.name.to_ident_string(); if let name = name_ident.ident.name.to_ident_string();
if name == "sort_by" || name == "sort_unstable_by"; if name == "sort_by" || name == "sort_unstable_by";
if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
let vec_ty = cx.typeck_results().expr_ty(vec); if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type));
if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type));
let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec<T>
if !matches!(&ty.kind(), ty::Ref(..));
if utils::is_copy(cx, ty);
if let closure_body = cx.tcx.hir().body(*closure_body_id); if let closure_body = cx.tcx.hir().body(*closure_body_id);
if let &[ if let &[
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
@ -210,24 +200,22 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
let unstable = name == "sort_unstable_by"; let unstable = name == "sort_unstable_by";
if_chain! {
if let ExprKind::Path(QPath::Resolved(_, Path { if let ExprKind::Path(QPath::Resolved(_, Path {
segments: [PathSegment { ident: left_name, .. }], .. segments: [PathSegment { ident: left_name, .. }], ..
})) = &left_expr.kind; })) = &left_expr.kind {
if left_name == left_ident; if left_name == left_ident {
then { return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) }
} else { }
if !key_returns_borrow(cx, left_expr) {
if !expr_borrows(cx, left_expr) {
return Some(LintTrigger::SortByKey(SortByKeyDetection { return Some(LintTrigger::SortByKey(SortByKeyDetection {
vec_name, vec_name,
unstable, unstable,
closure_arg, closure_arg,
closure_body, closure_body,
reverse reverse
})) }));
}
}
} }
} }
} }
@ -235,15 +223,9 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
None None
} }
fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(def_id) = utils::fn_def_id(cx, expr) { let ty = cx.typeck_results().expr_ty(expr);
let output = cx.tcx.fn_sig(def_id).output(); matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
let ty = output.skip_binder();
return matches!(ty.kind(), ty::Ref(..))
|| ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}
false
} }
impl LateLintPass<'_> for UnnecessarySortBy { impl LateLintPass<'_> for UnnecessarySortBy {
@ -256,7 +238,7 @@ impl LateLintPass<'_> for UnnecessarySortBy {
"use Vec::sort_by_key here instead", "use Vec::sort_by_key here instead",
"try", "try",
format!( format!(
"{}.sort{}_by_key(|&{}| {})", "{}.sort{}_by_key(|{}| {})",
trigger.vec_name, trigger.vec_name,
if trigger.unstable { "_unstable" } else { "" }, if trigger.unstable { "_unstable" } else { "" },
trigger.closure_arg, trigger.closure_arg,

View file

@ -2,10 +2,10 @@
#![deny(clippy::missing_docs_in_private_items)] #![deny(clippy::missing_docs_in_private_items)]
use lazy_static::lazy_static;
use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem};
use rustc_span::source_map; use rustc_span::source_map;
use source_map::Span; use source_map::Span;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Mutex; use std::sync::Mutex;
use std::{env, fmt, fs, io}; use std::{env, fmt, fs, io};
@ -54,9 +54,8 @@ impl From<io::Error> for Error {
} }
} }
lazy_static! { /// Vec of errors that might be collected during config toml parsing
static ref ERRORS: Mutex<Vec<Error>> = Mutex::new(Vec::new()); static ERRORS: SyncLazy<Mutex<Vec<Error>>> = SyncLazy::new(|| Mutex::new(Vec::new()));
}
macro_rules! define_Conf { macro_rules! define_Conf {
($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => {
@ -82,6 +81,7 @@ macro_rules! define_Conf {
use serde::Deserialize; use serde::Deserialize;
pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> {
use super::super::{ERRORS, Error}; use super::super::{ERRORS, Error};
Ok( Ok(
<$Ty>::deserialize(deserializer).unwrap_or_else(|e| { <$Ty>::deserialize(deserializer).unwrap_or_else(|e| {
ERRORS ERRORS
@ -164,6 +164,8 @@ define_Conf! {
(max_fn_params_bools, "max_fn_params_bools": u64, 3), (max_fn_params_bools, "max_fn_params_bools": u64, 3),
/// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
} }
impl Default for Conf { impl Default for Conf {

View file

@ -18,9 +18,9 @@ pub mod internal_lints;
pub mod numeric_literal; pub mod numeric_literal;
pub mod paths; pub mod paths;
pub mod ptr; pub mod ptr;
pub mod qualify_min_const_fn;
pub mod sugg; pub mod sugg;
pub mod usage; pub mod usage;
pub mod qualify_min_const_fn;
pub use self::attrs::*; pub use self::attrs::*;
pub use self::diagnostics::*; pub use self::diagnostics::*;
@ -47,7 +47,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp; use rustc_span::source_map::original_sp;
use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::symbol::{self, kw, Symbol};
@ -884,19 +883,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
/// Checks if an expression is constructing a tuple-like enum variant or struct /// Checks if an expression is constructing a tuple-like enum variant or struct
pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty()
}
if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Call(ref fun, _) = expr.kind {
if let ExprKind::Path(ref qp) = fun.kind { if let ExprKind::Path(ref qp) = fun.kind {
let res = cx.qpath_res(qp, fun.hir_id); let res = cx.qpath_res(qp, fun.hir_id);
return match res { return match res {
def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
// FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210
def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => {
const_eval::is_const_fn(cx.tcx, def_id)
},
def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
_ => false, _ => false,
}; };
@ -1287,8 +1278,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty::Opaque(ref def_id, _) => { ty::Opaque(ref def_id, _) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() { if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() {
if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
{
return true; return true;
} }
} }

View file

@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> {
pub integer: &'a str, pub integer: &'a str,
/// The fraction part of the number. /// The fraction part of the number.
pub fraction: Option<&'a str>, pub fraction: Option<&'a str>,
/// The character used as exponent separator (b'e' or b'E') and the exponent part. /// The exponent separator (b'e' or b'E') including preceding underscore if present
pub exponent: Option<(char, &'a str)>, /// and the exponent part.
pub exponent: Option<(&'a str, &'a str)>,
/// The type suffix, including preceding underscore if present. /// The type suffix, including preceding underscore if present.
pub suffix: Option<&'a str>, pub suffix: Option<&'a str>,
@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> {
self.radix == Radix::Decimal self.radix == Radix::Decimal
} }
pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
let mut integer = digits; let mut integer = digits;
let mut fraction = None; let mut fraction = None;
let mut exponent = None; let mut exponent = None;
@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> {
fraction = Some(&digits[i + 1..]); fraction = Some(&digits[i + 1..]);
}, },
'e' | 'E' => { 'e' | 'E' => {
if integer.len() > i { let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };
integer = &digits[..i];
if integer.len() > exp_start {
integer = &digits[..exp_start];
} else { } else {
fraction = Some(&digits[integer.len() + 1..i]); fraction = Some(&digits[integer.len() + 1..exp_start]);
}; };
exponent = Some((c, &digits[i + 1..])); exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
break; break;
}, },
_ => {}, _ => {},
@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> {
} }
if let Some((separator, exponent)) = self.exponent { if let Some((separator, exponent)) = self.exponent {
output.push(separator); output.push_str(separator);
Self::group_digits(&mut output, exponent, group_size, true, false); Self::group_digits(&mut output, exponent, group_size, true, false);
} }

View file

@ -41,6 +41,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FN: [&str; 3] = ["core", "ops", "Fn"];
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];

View file

@ -1,9 +1,12 @@
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_middle::mir::*; use rustc_middle::mir::{
Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
use rustc_span::symbol::{sym}; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_target::spec::abi::Abi::RustIntrinsic;
use std::borrow::Cow; use std::borrow::Cow;
@ -24,15 +27,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
| ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEvaluatable(..)
| ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::ConstEquate(..)
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
ty::PredicateAtom::ObjectSafe(_) => { ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
panic!("object safe predicate on function: {:#?}", predicate) ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
} ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
ty::PredicateAtom::ClosureKind(..) => {
panic!("closure kind predicate on function: {:#?}", predicate)
}
ty::PredicateAtom::Subtype(_) => {
panic!("subtype predicate on function: {:#?}", predicate)
}
ty::PredicateAtom::Trait(pred, _) => { ty::PredicateAtom::Trait(pred, _) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() { if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue; continue;
@ -48,12 +45,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
on const fn parameters are unstable" on const fn parameters are unstable"
.into(), .into(),
)); ));
} },
// other kinds of bounds are either tautologies // other kinds of bounds are either tautologies
// or cause errors in other passes // or cause errors in other passes
_ => continue, _ => continue,
} }
} },
} }
} }
match predicates.parent { match predicates.parent {
@ -94,23 +91,22 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
match ty.kind() { match ty.kind() {
ty::Ref(_, _, hir::Mutability::Mut) => { ty::Ref(_, _, hir::Mutability::Mut) => {
return Err((span, "mutable references in const fn are unstable".into())); return Err((span, "mutable references in const fn are unstable".into()));
} },
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
ty::FnPtr(..) => { ty::FnPtr(..) => {
return Err((span, "function pointers in const fn are unstable".into())); return Err((span, "function pointers in const fn are unstable".into()));
} },
ty::Dynamic(preds, _) => { ty::Dynamic(preds, _) => {
for pred in preds.iter() { for pred in preds.iter() {
match pred.skip_binder() { match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_) ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
| ty::ExistentialPredicate::Projection(_) => {
return Err(( return Err((
span, span,
"trait bounds other than `Sized` \ "trait bounds other than `Sized` \
on const fn parameters are unstable" on const fn parameters are unstable"
.into(), .into(),
)); ));
} },
ty::ExistentialPredicate::Trait(trait_ref) => { ty::ExistentialPredicate::Trait(trait_ref) => {
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
return Err(( return Err((
@ -120,34 +116,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
.into(), .into(),
)); ));
} }
},
} }
} }
} },
} _ => {},
_ => {}
} }
} }
Ok(()) Ok(())
} }
fn check_rvalue( fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
rvalue: &Rvalue<'tcx>,
span: Span,
) -> McfResult {
match rvalue { match rvalue {
Rvalue::ThreadLocalRef(_) => { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
Err((span, "cannot access thread local storage in const fn".into())) Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
} Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { check_place(tcx, *place, span, body)
check_operand(tcx, operand, span, body) },
}
Rvalue::Len(place)
| Rvalue::Discriminant(place)
| Rvalue::Ref(_, _, place)
| Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body),
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::cast::CastTy;
let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
@ -155,20 +140,16 @@ fn check_rvalue(
match (cast_in, cast_out) { match (cast_in, cast_out) {
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
Err((span, "casting pointers to ints is unstable in const fn".into())) Err((span, "casting pointers to ints is unstable in const fn".into()))
} },
_ => check_operand(tcx, operand, span, body), _ => check_operand(tcx, operand, span, body),
} }
} },
Rvalue::Cast( Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), check_operand(tcx, operand, span, body)
operand, },
_,
) => check_operand(tcx, operand, span, body),
Rvalue::Cast( Rvalue::Cast(
CastKind::Pointer( CastKind::Pointer(
PointerCast::UnsafeFnPointer PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
| PointerCast::ClosureFnPointer(_)
| PointerCast::ReifyFnPointer,
), ),
_, _,
_, _,
@ -178,10 +159,7 @@ fn check_rvalue(
deref_ty.ty deref_ty.ty
} else { } else {
// We cannot allow this for now. // We cannot allow this for now.
return Err(( return Err((span, "unsizing casts are only allowed for references right now".into()));
span,
"unsizing casts are only allowed for references right now".into(),
));
}; };
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
if let ty::Slice(_) | ty::Str = unsized_ty.kind() { if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
@ -192,7 +170,7 @@ fn check_rvalue(
// We just can't allow trait objects until we have figured out trait method calls. // We just can't allow trait objects until we have figured out trait method calls.
Err((span, "unsizing casts are not allowed in const fn".into())) Err((span, "unsizing casts are not allowed in const fn".into()))
} }
} },
// binops are fine on integers // binops are fine on integers
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
check_operand(tcx, lhs, span, body)?; check_operand(tcx, lhs, span, body)?;
@ -201,13 +179,14 @@ fn check_rvalue(
if ty.is_integral() || ty.is_bool() || ty.is_char() { if ty.is_integral() || ty.is_bool() || ty.is_char() {
Ok(()) Ok(())
} else { } else {
Err((span, "only int, `bool` and `char` operations are stable in const fn".into())) Err((
} span,
"only int, `bool` and `char` operations are stable in const fn".into(),
))
} }
},
Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
Rvalue::NullaryOp(NullOp::Box, _) => { Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
Err((span, "heap allocations are not allowed in const fn".into()))
}
Rvalue::UnaryOp(_, operand) => { Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx); let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() { if ty.is_integral() || ty.is_bool() {
@ -215,39 +194,29 @@ fn check_rvalue(
} else { } else {
Err((span, "only int and `bool` operations are stable in const fn".into())) Err((span, "only int and `bool` operations are stable in const fn".into()))
} }
} },
Rvalue::Aggregate(_, operands) => { Rvalue::Aggregate(_, operands) => {
for operand in operands { for operand in operands {
check_operand(tcx, operand, span, body)?; check_operand(tcx, operand, span, body)?;
} }
Ok(()) Ok(())
} },
} }
} }
fn check_statement( fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
def_id: DefId,
statement: &Statement<'tcx>,
) -> McfResult {
let span = statement.source_info.span; let span = statement.source_info.span;
match &statement.kind { match &statement.kind {
StatementKind::Assign(box (place, rval)) => { StatementKind::Assign(box (place, rval)) => {
check_place(tcx, *place, span, body)?; check_place(tcx, *place, span, body)?;
check_rvalue(tcx, body, def_id, rval, span) check_rvalue(tcx, body, def_id, rval, span)
} },
StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body),
StatementKind::FakeRead(_, place) |
// just an assignment // just an assignment
StatementKind::SetDiscriminant { place, .. } => { StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
check_place(tcx, **place, span, body)
}
StatementKind::LlvmInlineAsm { .. } => { StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
Err((span, "cannot use inline assembly in const fn".into()))
}
// These are all NOPs // These are all NOPs
StatementKind::StorageLive(_) StatementKind::StorageLive(_)
@ -259,12 +228,7 @@ fn check_statement(
} }
} }
fn check_operand( fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
tcx: TyCtxt<'tcx>,
operand: &Operand<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
match operand { match operand {
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) { Operand::Constant(c) => match c.check_static_ptr(tcx) {
@ -274,14 +238,9 @@ fn check_operand(
} }
} }
fn check_place( fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
tcx: TyCtxt<'tcx>,
place: Place<'tcx>,
span: Span,
body: &Body<'tcx>,
) -> McfResult {
let mut cursor = place.projection.as_ref(); let mut cursor = place.projection.as_ref();
while let &[ref proj_base @ .., elem] = cursor { while let [ref proj_base @ .., elem] = *cursor {
cursor = proj_base; cursor = proj_base;
match elem { match elem {
ProjectionElem::Field(..) => { ProjectionElem::Field(..) => {
@ -292,23 +251,19 @@ fn check_place(
return Err((span, "accessing union fields is unstable".into())); return Err((span, "accessing union fields is unstable".into()));
} }
} }
} },
ProjectionElem::ConstantIndex { .. } ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Downcast(..) | ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. } | ProjectionElem::Subslice { .. }
| ProjectionElem::Deref | ProjectionElem::Deref
| ProjectionElem::Index(_) => {} | ProjectionElem::Index(_) => {},
} }
} }
Ok(()) Ok(())
} }
fn check_terminator( fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
terminator: &Terminator<'tcx>,
) -> McfResult {
let span = terminator.source_info.span; let span = terminator.source_info.span;
match &terminator.kind { match &terminator.kind {
TerminatorKind::FalseEdge { .. } TerminatorKind::FalseEdge { .. }
@ -322,16 +277,19 @@ fn check_terminator(
TerminatorKind::DropAndReplace { place, value, .. } => { TerminatorKind::DropAndReplace { place, value, .. } => {
check_place(tcx, *place, span, body)?; check_place(tcx, *place, span, body)?;
check_operand(tcx, value, span, body) check_operand(tcx, value, span, body)
} },
TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { TerminatorKind::SwitchInt {
check_operand(tcx, discr, span, body) discr,
} switch_ty: _,
values: _,
targets: _,
} => check_operand(tcx, discr, span, body),
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into())) Err((span, "const fn generators are unstable".into()))
} },
TerminatorKind::Call { TerminatorKind::Call {
func, func,
@ -343,8 +301,7 @@ fn check_terminator(
} => { } => {
let fn_ty = func.ty(body, tcx); let fn_ty = func.ty(body, tcx);
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
{
return Err(( return Err((
span, span,
format!( format!(
@ -360,9 +317,7 @@ fn check_terminator(
// within const fns. `transmute` is allowed in all other const contexts. // within const fns. `transmute` is allowed in all other const contexts.
// This won't really scale to more intrinsics or functions. Let's allow const // This won't really scale to more intrinsics or functions. Let's allow const
// transmutes in const fn before we add more hacks to this. // transmutes in const fn before we add more hacks to this.
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
&& tcx.item_name(fn_def_id) == sym::transmute
{
return Err(( return Err((
span, span,
"can only call `transmute` from const items, not `const fn`".into(), "can only call `transmute` from const items, not `const fn`".into(),
@ -378,14 +333,16 @@ fn check_terminator(
} else { } else {
Err((span, "can only call other const fns within const fn".into())) Err((span, "can only call other const fns within const fn".into()))
} }
} },
TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { TerminatorKind::Assert {
check_operand(tcx, cond, span, body) cond,
} expected: _,
msg: _,
target: _,
cleanup: _,
} => check_operand(tcx, cond, span, body),
TerminatorKind::InlineAsm { .. } => { TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
Err((span, "cannot use inline assembly in const fn".into()))
}
} }
} }

View file

@ -235,8 +235,19 @@ impl EarlyLintPass for Write {
} }
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
fn is_build_script(cx: &EarlyContext<'_>) -> bool {
// Cargo sets the crate name for build scripts to `build_script_build`
cx.sess
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build")
}
if mac.path == sym!(println) { if mac.path == sym!(println) {
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == Symbol::intern("") { if fmt_str.symbol == Symbol::intern("") {
span_lint_and_sugg( span_lint_and_sugg(
@ -251,7 +262,9 @@ impl EarlyLintPass for Write {
} }
} }
} else if mac.path == sym!(print) { } else if mac.path == sym!(print) {
if !is_build_script(cx) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
}
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if check_newlines(&fmt_str) { if check_newlines(&fmt_str) {
span_lint_and_then( span_lint_and_then(

View file

@ -0,0 +1,7 @@
#![deny(clippy::print_stdout)]
fn main() {
// Test for #6041
println!("Hello");
print!("Hello");
}

View file

@ -189,7 +189,8 @@ declare_clippy_lint! {
* The section of lines prefixed with `///` constitutes the lint documentation * The section of lines prefixed with `///` constitutes the lint documentation
section. This is the default documentation style and will be displayed section. This is the default documentation style and will be displayed
[like this][example_lint_page]. [like this][example_lint_page]. To render and open this documentation locally
in a browser, run `cargo dev serve`.
* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
[lint naming guidelines][lint_naming] here when naming your lint. [lint naming guidelines][lint_naming] here when naming your lint.
In short, the name should state the thing that is being checked for and In short, the name should state the thing that is being checked for and

View file

@ -13,6 +13,7 @@ Lints] or [Common Tools].
- [Setup](#setup) - [Setup](#setup)
- [Building and Testing](#building-and-testing) - [Building and Testing](#building-and-testing)
- [`cargo dev`](#cargo-dev) - [`cargo dev`](#cargo-dev)
- [PR](#pr)
## Get the Code ## Get the Code
@ -110,3 +111,8 @@ cargo dev new_lint
# (experimental) Setup Clippy to work with rust-analyzer # (experimental) Setup Clippy to work with rust-analyzer
cargo dev ra-setup cargo dev ra-setup
``` ```
## PR
We follow a rustc no merge-commit policy.
See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.

View file

@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
}, },
Lint { Lint {
name: "borrow_interior_mutable_const", name: "borrow_interior_mutable_const",
group: "correctness", group: "style",
desc: "referencing `const` with interior mutability", desc: "referencing `const` with interior mutability",
deprecation: None, deprecation: None,
module: "non_copy_const", module: "non_copy_const",
@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
}, },
Lint { Lint {
name: "declare_interior_mutable_const", name: "declare_interior_mutable_const",
group: "correctness", group: "style",
desc: "declaring `const` with interior mutability", desc: "declaring `const` with interior mutability",
deprecation: None, deprecation: None,
module: "non_copy_const", module: "non_copy_const",
@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "derive", module: "derive",
}, },
Lint {
name: "disallowed_method",
group: "nursery",
desc: "use of a disallowed method call",
deprecation: None,
module: "disallowed_method",
},
Lint { Lint {
name: "diverging_sub_expression", name: "diverging_sub_expression",
group: "complexity", group: "complexity",
@ -885,6 +892,20 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "attrs", module: "attrs",
}, },
Lint {
name: "inline_asm_x86_att_syntax",
group: "restriction",
desc: "prefer Intel x86 assembly syntax",
deprecation: None,
module: "asm_syntax",
},
Lint {
name: "inline_asm_x86_intel_syntax",
group: "restriction",
desc: "prefer AT&T x86 assembly syntax",
deprecation: None,
module: "asm_syntax",
},
Lint { Lint {
name: "inline_fn_without_body", name: "inline_fn_without_body",
group: "correctness", group: "correctness",
@ -941,6 +962,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "types", module: "types",
}, },
Lint {
name: "invisible_characters",
group: "correctness",
desc: "using an invisible character in a string literal, which is confusing",
deprecation: None,
module: "unicode",
},
Lint { Lint {
name: "items_after_statements", name: "items_after_statements",
group: "pedantic", group: "pedantic",
@ -1860,7 +1888,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
}, },
Lint { Lint {
name: "rc_buffer", name: "rc_buffer",
group: "perf", group: "restriction",
desc: "shared ownership of a buffer type", desc: "shared ownership of a buffer type",
deprecation: None, deprecation: None,
module: "types", module: "types",
@ -2133,7 +2161,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
}, },
Lint { Lint {
name: "string_lit_as_bytes", name: "string_lit_as_bytes",
group: "style", group: "nursery",
desc: "calling `as_bytes` on a string literal instead of using a byte string literal", desc: "calling `as_bytes` on a string literal instead of using a byte string literal",
deprecation: None, deprecation: None,
module: "strings", module: "strings",
@ -2782,13 +2810,6 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None, deprecation: None,
module: "misc", module: "misc",
}, },
Lint {
name: "zero_width_space",
group: "correctness",
desc: "using a zero-width space in a string literal, which is confusing",
deprecation: None,
module: "unicode",
},
Lint { Lint {
name: "zst_offset", name: "zst_offset",
group: "correctness", group: "correctness",

View file

@ -1,15 +1,13 @@
use lazy_static::lazy_static;
use std::env; use std::env;
use std::lazy::SyncLazy;
use std::path::PathBuf; use std::path::PathBuf;
lazy_static! { pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
pub static ref CARGO_TARGET_DIR: PathBuf = {
match env::var_os("CARGO_TARGET_DIR") {
Some(v) => v.into(), Some(v) => v.into(),
None => env::current_dir().unwrap().join("target"), None => env::current_dir().unwrap().join("target"),
} });
};
pub static ref TARGET_LIB: PathBuf = { pub static TARGET_LIB: SyncLazy<PathBuf> = SyncLazy::new(|| {
if let Some(path) = option_env!("TARGET_LIBS") { if let Some(path) = option_env!("TARGET_LIBS") {
path.into() path.into()
} else { } else {
@ -20,8 +18,7 @@ lazy_static! {
dir.push(env!("PROFILE")); dir.push(env!("PROFILE"));
dir dir
} }
}; });
}
#[must_use] #[must_use]
pub fn is_rustc_test_suite() -> bool { pub fn is_rustc_test_suite() -> bool {

View file

@ -1,4 +1,5 @@
#![feature(test)] // compiletest_rs requires this attribute #![feature(test)] // compiletest_rs requires this attribute
#![feature(once_cell)]
use compiletest_rs as compiletest; use compiletest_rs as compiletest;
use compiletest_rs::common::Mode as TestMode; use compiletest_rs::common::Mode as TestMode;
@ -71,7 +72,7 @@ fn default_config() -> compiletest::Config {
} }
config.target_rustcflags = Some(format!( config.target_rustcflags = Some(format!(
"-L {0} -L {1} -Dwarnings -Zui-testing {2}", "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}",
host_lib().join("deps").display(), host_lib().join("deps").display(),
cargo::TARGET_LIB.join("deps").display(), cargo::TARGET_LIB.join("deps").display(),
third_party_crates(), third_party_crates(),

View file

@ -1,15 +1,14 @@
// Dogfood cannot run on Windows // Dogfood cannot run on Windows
#![cfg(not(windows))] #![cfg(not(windows))]
#![feature(once_cell)]
use lazy_static::lazy_static; use std::lazy::SyncLazy;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
mod cargo; mod cargo;
lazy_static! { static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy");
}
#[test] #[test]
fn dogfood_clippy() { fn dogfood_clippy() {

View file

@ -0,0 +1 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"]

View file

@ -0,0 +1,13 @@
#![warn(clippy::disallowed_method)]
extern crate regex;
use regex::Regex;
fn main() {
let a = vec![1, 2, 3, 4];
let re = Regex::new(r"ab.*c").unwrap();
re.is_match("abc");
a.iter().sum::<i32>();
}

View file

@ -0,0 +1,16 @@
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:10:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:12:5
|
LL | a.iter().sum::<i32>();
| ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
error: aborting due to previous error error: aborting due to previous error

31
tests/ui/asm_syntax.rs Normal file
View file

@ -0,0 +1,31 @@
#![feature(asm)]
// only-x86_64
#[warn(clippy::inline_asm_x86_intel_syntax)]
mod warn_intel {
pub(super) unsafe fn use_asm() {
asm!("");
asm!("", options());
asm!("", options(nostack));
asm!("", options(att_syntax));
asm!("", options(nostack, att_syntax));
}
}
#[warn(clippy::inline_asm_x86_att_syntax)]
mod warn_att {
pub(super) unsafe fn use_asm() {
asm!("");
asm!("", options());
asm!("", options(nostack));
asm!("", options(att_syntax));
asm!("", options(nostack, att_syntax));
}
}
fn main() {
unsafe {
warn_att::use_asm();
warn_intel::use_asm();
}
}

View file

@ -0,0 +1,44 @@
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:7:9
|
LL | asm!("");
| ^^^^^^^^^
|
= note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings`
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:8:9
|
LL | asm!("", options());
| ^^^^^^^^^^^^^^^^^^^^
|
= help: use AT&T x86 assembly syntax
error: Intel x86 assembly syntax used
--> $DIR/asm_syntax.rs:9:9
|
LL | asm!("", options(nostack));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use AT&T x86 assembly syntax
error: AT&T x86 assembly syntax used
--> $DIR/asm_syntax.rs:21:9
|
LL | asm!("", options(att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings`
= help: use Intel x86 assembly syntax
error: AT&T x86 assembly syntax used
--> $DIR/asm_syntax.rs:22:9
|
LL | asm!("", options(nostack, att_syntax));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use Intel x86 assembly syntax
error: aborting due to 5 previous errors

View file

@ -1,8 +1,5 @@
#![warn(clippy::inline_always, clippy::deprecated_semver)] #![warn(clippy::inline_always, clippy::deprecated_semver)]
#![allow(clippy::assertions_on_constants)] #![allow(clippy::assertions_on_constants)]
// Test that the whole restriction group is not enabled
#![warn(clippy::restriction)]
#![deny(clippy::restriction)]
#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)] #![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)]
#[inline(always)] #[inline(always)]

View file

@ -1,5 +1,5 @@
error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
--> $DIR/attrs.rs:8:1 --> $DIR/attrs.rs:5:1
| |
LL | #[inline(always)] LL | #[inline(always)]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | #[inline(always)]
= note: `-D clippy::inline-always` implied by `-D warnings` = note: `-D clippy::inline-always` implied by `-D warnings`
error: the since field must contain a semver-compliant version error: the since field must contain a semver-compliant version
--> $DIR/attrs.rs:28:14 --> $DIR/attrs.rs:25:14
| |
LL | #[deprecated(since = "forever")] LL | #[deprecated(since = "forever")]
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
@ -15,27 +15,10 @@ LL | #[deprecated(since = "forever")]
= note: `-D clippy::deprecated-semver` implied by `-D warnings` = note: `-D clippy::deprecated-semver` implied by `-D warnings`
error: the since field must contain a semver-compliant version error: the since field must contain a semver-compliant version
--> $DIR/attrs.rs:31:14 --> $DIR/attrs.rs:28:14
| |
LL | #[deprecated(since = "1")] LL | #[deprecated(since = "1")]
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: restriction lints are not meant to be all enabled error: aborting due to 3 previous errors
--> $DIR/attrs.rs:4:9
|
LL | #![warn(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
= help: try enabling only the lints you really need
error: restriction lints are not meant to be all enabled
--> $DIR/attrs.rs:5:9
|
LL | #![deny(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: try enabling only the lints you really need
error: aborting due to 5 previous errors

View file

@ -1,7 +1,8 @@
// compile-flags: --emit=link
// no-prefer-dynamic // no-prefer-dynamic
#![crate_type = "proc-macro"] #![crate_type = "proc-macro"]
#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] #![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)]
#![allow(clippy::useless_conversion)] #![allow(clippy::useless_conversion)]
extern crate proc_macro; extern crate proc_macro;
@ -11,7 +12,11 @@ extern crate syn;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::parse_macro_input; use syn::parse_macro_input;
use syn::{parse_quote, ItemTrait, TraitItem}; use syn::spanned::Spanned;
use syn::token::Star;
use syn::{
parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type,
};
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
@ -35,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
} }
TokenStream::from(quote!(#item)) TokenStream::from(quote!(#item))
} }
#[proc_macro_attribute]
pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream {
fn make_name(count: usize) -> String {
format!("'life{}", count)
}
fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> {
let arg = sig.inputs.first_mut()?;
if let FnArg::Typed(PatType { pat, .. }) = arg {
if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
if ident == "self" {
return Some(arg);
}
}
}
None
}
let mut elided = 0;
let mut item = parse_macro_input!(input as ItemImpl);
// Look for methods having arbitrary self type taken by &mut ref
for inner in &mut item.items {
if let ImplItem::Method(method) = inner {
if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) {
if let box Type::Reference(reference) = &mut pat_type.ty {
// Target only unnamed lifetimes
let name = match &reference.lifetime {
Some(lt) if lt.ident == "_" => make_name(elided),
None => make_name(elided),
_ => continue,
};
elided += 1;
// HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it.
// In order to avoid adding the dependency, get a default span from a non-existent token.
// A default span is needed to mark the code as coming from expansion.
let span = Star::default().span();
// Replace old lifetime with the named one
let lifetime = Lifetime::new(&name, span);
reference.lifetime = Some(parse_quote!(#lifetime));
// Add lifetime to the generics of the method
method.sig.generics.params.push(parse_quote!(#lifetime));
}
}
}
}
TokenStream::from(quote!(#item))
}

View file

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic // no-prefer-dynamic
#![crate_type = "proc-macro"] #![crate_type = "proc-macro"]

View file

@ -0,0 +1,8 @@
#![warn(clippy::blanket_clippy_restriction_lints)]
//! Test that the whole restriction group is not enabled
#![warn(clippy::restriction)]
#![deny(clippy::restriction)]
#![forbid(clippy::restriction)]
fn main() {}

View file

@ -0,0 +1,27 @@
error: restriction lints are not meant to be all enabled
--> $DIR/blanket_clippy_restriction_lints.rs:4:9
|
LL | #![warn(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
= help: try enabling only the lints you really need
error: restriction lints are not meant to be all enabled
--> $DIR/blanket_clippy_restriction_lints.rs:5:9
|
LL | #![deny(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: try enabling only the lints you really need
error: restriction lints are not meant to be all enabled
--> $DIR/blanket_clippy_restriction_lints.rs:6:11
|
LL | #![forbid(clippy::restriction)]
| ^^^^^^^^^^^^^^^^^^^
|
= help: try enabling only the lints you really need
error: aborting due to 3 previous errors

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/1698 /// Test for https://github.com/rust-lang/rust-clippy/issues/1698
pub trait Trait { pub trait Trait {

View file

@ -1,3 +1,4 @@
// compile-flags: --emit=link
// no-prefer-dynamic // no-prefer-dynamic
// ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro // ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro
// crates. If we don't set this, compiletest will override the `crate_type` attribute below and // crates. If we don't set this, compiletest will override the `crate_type` attribute below and

View file

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)] #[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/478 /// Test for https://github.com/rust-lang/rust-clippy/issues/478

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)] #![deny(clippy::all)]
#![allow(unused_imports)] #![allow(unused_imports)]

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)] #![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1588 /// Test for https://github.com/rust-lang/rust-clippy/issues/1588

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessEq` / `consts::constant` /// Should not trigger an ICE in `SpanlessEq` / `consts::constant`

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::all)] #![allow(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1969 /// Test for https://github.com/rust-lang/rust-clippy/issues/1969

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` /// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code, unused_variables)] #![allow(dead_code, unused_variables)]
/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` /// Should not trigger an ICE in `SpanlessHash` / `consts::constant`

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2727 /// Test for https://github.com/rust-lang/rust-clippy/issues/2727
pub fn f(new: fn()) { pub fn f(new: fn()) {

View file

@ -1,5 +1,3 @@
// run-pass
#![allow( #![allow(
unused_variables, unused_variables,
clippy::blacklisted_name, clippy::blacklisted_name,

View file

@ -1,5 +1,3 @@
// run-pass
use std::collections::HashSet; use std::collections::HashSet;
// See rust-lang/rust-clippy#2774. // See rust-lang/rust-clippy#2774.

View file

@ -0,0 +1,10 @@
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/ice-2774.rs:15:1
|
LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
error: aborting due to previous error

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2862 /// Test for https://github.com/rust-lang/rust-clippy/issues/2862
pub trait FooMap { pub trait FooMap {

View file

@ -1,5 +1,3 @@
// run-pass
#[allow(dead_code)] #[allow(dead_code)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 /// Test for https://github.com/rust-lang/rust-clippy/issues/2865

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 /// Test for https://github.com/rust-lang/rust-clippy/issues/2865
#[derive(Clone)] #[derive(Clone)]

View file

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::all)] #![warn(clippy::all)]
#![allow(clippy::blacklisted_name)] #![allow(clippy::blacklisted_name)]
#![allow(unused)] #![allow(unused)]

View file

@ -1,5 +1,4 @@
// aux-build:proc_macro_crash.rs // aux-build:proc_macro_crash.rs
// run-pass
#![warn(clippy::suspicious_else_formatting)] #![warn(clippy::suspicious_else_formatting)]

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/3747 /// Test for https://github.com/rust-lang/rust-clippy/issues/3747
macro_rules! a { macro_rules! a {

View file

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::use_self)] #![warn(clippy::use_self)]
#[path = "auxiliary/ice-4727-aux.rs"] #[path = "auxiliary/ice-4727-aux.rs"]

View file

@ -1,4 +1,3 @@
// run-pass
const COUNT: usize = 2; const COUNT: usize = 2;
struct Thing; struct Thing;
trait Dummy {} trait Dummy {}

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)] #![deny(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/700 /// Test for https://github.com/rust-lang/rust-clippy/issues/700

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::all)] #![deny(clippy::all)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/1336 /// Test for https://github.com/rust-lang/rust-clippy/issues/1336

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::comparison_chain)] #![allow(clippy::comparison_chain)]
#![deny(clippy::if_same_then_else)] #![deny(clippy::if_same_then_else)]

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(warnings)] #![allow(warnings)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/825 /// Test for https://github.com/rust-lang/rust-clippy/issues/825

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(dead_code)] #![allow(dead_code)]
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::match_same_arms)] #![deny(clippy::match_same_arms)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2427 /// Test for https://github.com/rust-lang/rust-clippy/issues/2427

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] #![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
#![allow(dead_code)] #![allow(dead_code)]

View file

@ -1,5 +1,3 @@
// run-pass
#[deny(clippy::all)] #[deny(clippy::all)]
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {

View file

@ -1,5 +1,3 @@
// run-pass
#![deny(clippy::needless_lifetimes)] #![deny(clippy::needless_lifetimes)]
#![allow(dead_code)] #![allow(dead_code)]

View file

@ -0,0 +1,14 @@
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes_impl_trait.rs:15:5
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/needless_lifetimes_impl_trait.rs:1:9
|
LL | #![deny(clippy::needless_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View file

@ -1,5 +1,3 @@
// run-pass
#[macro_use] #[macro_use]
extern crate clippy_mini_macro_test; extern crate clippy_mini_macro_test;

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::blacklisted_name)] #![allow(clippy::blacklisted_name)]
pub fn foo(bar: *const u8) { pub fn foo(bar: *const u8) {

View file

@ -1,5 +1,3 @@
// run-pass
/// Test for https://github.com/rust-lang/rust-clippy/issues/1346 /// Test for https://github.com/rust-lang/rust-clippy/issues/1346
#[deny(warnings)] #[deny(warnings)]

View file

@ -1,5 +1,3 @@
// run-pass
#![warn(clippy::single_match_else)] #![warn(clippy::single_match_else)]
//! Test for https://github.com/rust-lang/rust-clippy/issues/1588 //! Test for https://github.com/rust-lang/rust-clippy/issues/1588

View file

@ -1,5 +1,3 @@
// run-pass
#![feature(trivial_bounds)] #![feature(trivial_bounds)]
#![allow(unused, trivial_bounds)] #![allow(unused, trivial_bounds)]

View file

@ -1,5 +1,3 @@
// run-pass
#![allow(clippy::useless_attribute)] //issue #2910 #![allow(clippy::useless_attribute)] //issue #2910
#[macro_use] #[macro_use]

View file

@ -174,3 +174,11 @@ mod issue_3739 {
}; };
} }
} }
/// Issue #5542
///
/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code.
pub extern "C" fn do_not_warn_me(_c_pointer: Box<String>) -> () {}
#[rustfmt::skip] // Forces rustfmt to not add ABI
pub extern fn do_not_warn_me_no_abi(_c_pointer: Box<String>) -> () {}

View file

@ -38,54 +38,54 @@ mod issue_1219 {
let text = "banana"; let text = "banana";
let mut count = 0; let mut count = 0;
for ch in text.chars() { for ch in text.chars() {
println!("{}", count);
if ch == 'a' { if ch == 'a' {
continue; continue;
} }
count += 1; count += 1;
println!("{}", count);
} }
// should not trigger the lint because the count is conditional // should not trigger the lint because the count is conditional
let text = "banana"; let text = "banana";
let mut count = 0; let mut count = 0;
for ch in text.chars() { for ch in text.chars() {
println!("{}", count);
if ch == 'a' { if ch == 'a' {
count += 1; count += 1;
} }
println!("{}", count);
} }
// should trigger the lint because the count is not conditional // should trigger the lint because the count is not conditional
let text = "banana"; let text = "banana";
let mut count = 0; let mut count = 0;
for ch in text.chars() { for ch in text.chars() {
println!("{}", count);
count += 1; count += 1;
if ch == 'a' { if ch == 'a' {
continue; continue;
} }
println!("{}", count);
} }
// should trigger the lint because the count is not conditional // should trigger the lint because the count is not conditional
let text = "banana"; let text = "banana";
let mut count = 0; let mut count = 0;
for ch in text.chars() { for ch in text.chars() {
println!("{}", count);
count += 1; count += 1;
for i in 0..2 { for i in 0..2 {
let _ = 123; let _ = 123;
} }
println!("{}", count);
} }
// should not trigger the lint because the count is incremented multiple times // should not trigger the lint because the count is incremented multiple times
let text = "banana"; let text = "banana";
let mut count = 0; let mut count = 0;
for ch in text.chars() { for ch in text.chars() {
println!("{}", count);
count += 1; count += 1;
for i in 0..2 { for i in 0..2 {
count += 1; count += 1;
} }
println!("{}", count);
} }
} }
} }
@ -96,30 +96,30 @@ mod issue_3308 {
let mut skips = 0; let mut skips = 0;
let erasures = vec![]; let erasures = vec![];
for i in 0..10 { for i in 0..10 {
println!("{}", skips);
while erasures.contains(&(i + skips)) { while erasures.contains(&(i + skips)) {
skips += 1; skips += 1;
} }
println!("{}", skips);
} }
// should not trigger the lint because the count is incremented multiple times // should not trigger the lint because the count is incremented multiple times
let mut skips = 0; let mut skips = 0;
for i in 0..10 { for i in 0..10 {
println!("{}", skips);
let mut j = 0; let mut j = 0;
while j < 5 { while j < 5 {
skips += 1; skips += 1;
j += 1; j += 1;
} }
println!("{}", skips);
} }
// should not trigger the lint because the count is incremented multiple times // should not trigger the lint because the count is incremented multiple times
let mut skips = 0; let mut skips = 0;
for i in 0..10 { for i in 0..10 {
println!("{}", skips);
for j in 0..5 { for j in 0..5 {
skips += 1; skips += 1;
} }
println!("{}", skips);
} }
} }
} }
@ -145,3 +145,16 @@ mod issue_4732 {
let _closure = || println!("index: {}", index); let _closure = || println!("index: {}", index);
} }
} }
mod issue_4677 {
pub fn test() {
let slice = &[1, 2, 3];
// should not trigger the lint because the count is used after incremented
let mut count = 0;
for _i in slice {
count += 1;
println!("{}", count);
}
}
}

View file

@ -40,4 +40,8 @@ fn main() {
// Ignore literals in macros // Ignore literals in macros
let _ = mac1!(); let _ = mac1!();
let _ = mac2!(); let _ = mac2!();
// Issue #6096
// Allow separating exponent with '_'
let _ = 1.025_011_10_E0;
} }

View file

@ -40,4 +40,8 @@ fn main() {
// Ignore literals in macros // Ignore literals in macros
let _ = mac1!(); let _ = mac1!();
let _ = mac2!(); let _ = mac2!();
// Issue #6096
// Allow separating exponent with '_'
let _ = 1.025_011_10_E0;
} }

View file

@ -1,6 +1,11 @@
// run-rustfix // run-rustfix
#![allow(dead_code, unused_variables, clippy::excessive_precision)] #![allow(
dead_code,
unused_variables,
clippy::excessive_precision,
clippy::inconsistent_digit_grouping
)]
fn main() { fn main() {
let fail14 = 2_i32; let fail14 = 2_i32;
@ -12,13 +17,13 @@ fn main() {
let fail20 = 2_i8; // let fail20 = 2_i8; //
let fail21 = 4_i16; // let fail21 = 4_i16; //
let fail24 = 12.34_f64; let ok24 = 12.34_64;
let fail25 = 1E2_f32; let fail25 = 1E2_f32;
let fail26 = 43E7_f64; let fail26 = 43E7_f64;
let fail27 = 243E17_f32; let fail27 = 243E17_f32;
#[allow(overflowing_literals)] #[allow(overflowing_literals)]
let fail28 = 241_251_235E723_f64; let fail28 = 241_251_235E723_f64;
let fail29 = 42_279.911_f32; let ok29 = 42279.911_32;
let _ = 1.123_45E1_f32; let _ = 1.123_45E1_f32;
} }

View file

@ -1,6 +1,11 @@
// run-rustfix // run-rustfix
#![allow(dead_code, unused_variables, clippy::excessive_precision)] #![allow(
dead_code,
unused_variables,
clippy::excessive_precision,
clippy::inconsistent_digit_grouping
)]
fn main() { fn main() {
let fail14 = 2_32; let fail14 = 2_32;
@ -12,13 +17,13 @@ fn main() {
let fail20 = 2__8; // let fail20 = 2__8; //
let fail21 = 4___16; // let fail21 = 4___16; //
let fail24 = 12.34_64; let ok24 = 12.34_64;
let fail25 = 1E2_32; let fail25 = 1E2_32;
let fail26 = 43E7_64; let fail26 = 43E7_64;
let fail27 = 243E17_32; let fail27 = 243E17_32;
#[allow(overflowing_literals)] #[allow(overflowing_literals)]
let fail28 = 241251235E723_64; let fail28 = 241251235E723_64;
let fail29 = 42279.911_32; let ok29 = 42279.911_32;
let _ = 1.12345E1_32; let _ = 1.12345E1_32;
} }

View file

@ -1,5 +1,5 @@
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:6:18 --> $DIR/mistyped_literal_suffix.rs:11:18
| |
LL | let fail14 = 2_32; LL | let fail14 = 2_32;
| ^^^^ help: did you mean to write: `2_i32` | ^^^^ help: did you mean to write: `2_i32`
@ -7,76 +7,64 @@ LL | let fail14 = 2_32;
= note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:7:18 --> $DIR/mistyped_literal_suffix.rs:12:18
| |
LL | let fail15 = 4_64; LL | let fail15 = 4_64;
| ^^^^ help: did you mean to write: `4_i64` | ^^^^ help: did you mean to write: `4_i64`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:8:18 --> $DIR/mistyped_literal_suffix.rs:13:18
| |
LL | let fail16 = 7_8; // LL | let fail16 = 7_8; //
| ^^^ help: did you mean to write: `7_i8` | ^^^ help: did you mean to write: `7_i8`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:9:18 --> $DIR/mistyped_literal_suffix.rs:14:18
| |
LL | let fail17 = 23_16; // LL | let fail17 = 23_16; //
| ^^^^^ help: did you mean to write: `23_i16` | ^^^^^ help: did you mean to write: `23_i16`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:12:18 --> $DIR/mistyped_literal_suffix.rs:17:18
| |
LL | let fail20 = 2__8; // LL | let fail20 = 2__8; //
| ^^^^ help: did you mean to write: `2_i8` | ^^^^ help: did you mean to write: `2_i8`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:13:18 --> $DIR/mistyped_literal_suffix.rs:18:18
| |
LL | let fail21 = 4___16; // LL | let fail21 = 4___16; //
| ^^^^^^ help: did you mean to write: `4_i16` | ^^^^^^ help: did you mean to write: `4_i16`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:15:18 --> $DIR/mistyped_literal_suffix.rs:21:18
|
LL | let fail24 = 12.34_64;
| ^^^^^^^^ help: did you mean to write: `12.34_f64`
error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:16:18
| |
LL | let fail25 = 1E2_32; LL | let fail25 = 1E2_32;
| ^^^^^^ help: did you mean to write: `1E2_f32` | ^^^^^^ help: did you mean to write: `1E2_f32`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:17:18 --> $DIR/mistyped_literal_suffix.rs:22:18
| |
LL | let fail26 = 43E7_64; LL | let fail26 = 43E7_64;
| ^^^^^^^ help: did you mean to write: `43E7_f64` | ^^^^^^^ help: did you mean to write: `43E7_f64`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:18:18 --> $DIR/mistyped_literal_suffix.rs:23:18
| |
LL | let fail27 = 243E17_32; LL | let fail27 = 243E17_32;
| ^^^^^^^^^ help: did you mean to write: `243E17_f32` | ^^^^^^^^^ help: did you mean to write: `243E17_f32`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:20:18 --> $DIR/mistyped_literal_suffix.rs:25:18
| |
LL | let fail28 = 241251235E723_64; LL | let fail28 = 241251235E723_64;
| ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
error: mistyped literal suffix error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:21:18 --> $DIR/mistyped_literal_suffix.rs:28:13
|
LL | let fail29 = 42279.911_32;
| ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32`
error: mistyped literal suffix
--> $DIR/mistyped_literal_suffix.rs:23:13
| |
LL | let _ = 1.12345E1_32; LL | let _ = 1.12345E1_32;
| ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
error: aborting due to 13 previous errors error: aborting due to 11 previous errors

View file

@ -0,0 +1,45 @@
// aux-build:proc_macro_attr.rs
#![warn(clippy::needless_arbitrary_self_type)]
#[macro_use]
extern crate proc_macro_attr;
mod issue_6089 {
// Check that we don't lint if the `self` parameter comes from expansion
macro_rules! test_from_expansion {
() => {
trait T1 {
fn test(self: &Self);
}
struct S1 {}
impl T1 for S1 {
fn test(self: &Self) {}
}
};
}
test_from_expansion!();
// If only the lifetime name comes from expansion we will lint, but the suggestion will have
// placeholders and will not be applied automatically, as we can't reliably know the original name.
// This specific case happened with async_trait.
trait T2 {
fn call_with_mut_self(&mut self);
}
struct S2 {}
// The method's signature will be expanded to:
// fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
#[rename_my_lifetimes]
impl T2 for S2 {
fn call_with_mut_self(self: &mut Self) {}
}
}
fn main() {}

View file

@ -0,0 +1,10 @@
error: the type of the `self` parameter does not need to be arbitrary
--> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31
|
LL | fn call_with_mut_self(self: &mut Self) {}
| ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self`
|
= note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings`
error: aborting due to previous error

View file

@ -259,4 +259,102 @@ mod issue4291 {
} }
} }
mod issue2944 {
trait Foo {}
struct Bar {}
struct Baz<'a> {
bar: &'a Bar,
}
impl<'a> Foo for Baz<'a> {}
impl Bar {
fn baz<'a>(&'a self) -> impl Foo + 'a {
Baz { bar: self }
}
}
}
mod nested_elision_sites {
// issue #issue2944
// closure trait bounds subject to nested elision
// don't lint because they refer to outer lifetimes
fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
move || i
}
fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
move || i
}
// don't lint
fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
f()
}
fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
// lint
fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
f(i)
}
fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
f(i)
}
// don't lint
fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
f()
}
// lint
fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
f(i)
}
// don't lint
fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
where
T: Fn() -> &'a i32,
{
f()
}
// lint
fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
where
T: Fn(&i32) -> &i32,
{
f(i)
}
// don't lint
fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
f(i)
}
fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
|i| i
}
// lint
fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
f(i)
}
// don't lint
fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
|f| ()
}
// lint
fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
|f| ()
}
}
fn main() {} fn main() {}

View file

@ -102,5 +102,53 @@ error: explicit lifetimes given in parameter types where they could be elided (o
LL | fn needless_lt<'a>(_x: &'a u8) {} LL | fn needless_lt<'a>(_x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 17 previous errors error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:271:9
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:300:5
|
LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:303:5
|
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:312:5
|
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:324:5
|
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:339:5
|
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:352:5
|
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:355:5
|
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 25 previous errors

View file

@ -82,6 +82,20 @@ fn main() {
for i in 1..3 { for i in 1..3 {
println!("{}", arr[i]); println!("{}", arr[i]);
} }
// Fix #5945
let mut vec = vec![1, 2, 3, 4];
for i in 0..vec.len() - 1 {
vec[i] += 1;
}
let mut vec = vec![1, 2, 3, 4];
for i in vec.len() - 3..vec.len() {
vec[i] += 1;
}
let mut vec = vec![1, 2, 3, 4];
for i in vec.len() - 3..vec.len() - 1 {
vec[i] += 1;
}
} }
mod issue2277 { mod issue2277 {

View file

@ -58,6 +58,12 @@ fn or_fun_call() {
let without_default = Some(Foo); let without_default = Some(Foo);
without_default.unwrap_or_else(Foo::new); without_default.unwrap_or_else(Foo::new);
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert_with(String::new);
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert_with(String::new);
let stringy = Some(String::from("")); let stringy = Some(String::from(""));
let _ = stringy.unwrap_or_else(|| "".to_owned()); let _ = stringy.unwrap_or_else(|| "".to_owned());
@ -110,23 +116,4 @@ fn f() -> Option<()> {
Some(()) Some(())
} }
// Issue 5886 - const fn (with no arguments)
pub fn skip_const_fn_with_no_args() {
const fn foo() -> Option<i32> {
Some(42)
}
let _ = None.or(foo());
// See issue #5693.
let mut map = std::collections::HashMap::new();
map.insert(1, vec![1]);
map.entry(1).or_insert(vec![]);
let mut map = HashMap::<u64, String>::new();
map.entry(42).or_insert(String::new());
let mut btree = BTreeMap::<u64, String>::new();
btree.entry(42).or_insert(String::new());
}
fn main() {} fn main() {}

Some files were not shown because too many files have changed in this diff Show more