mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 15:14:29 +00:00
Merge branch 'master' into rustup
This commit is contained in:
commit
3ad398d9b0
216 changed files with 8495 additions and 3900 deletions
158
CHANGELOG.md
158
CHANGELOG.md
|
@ -6,11 +6,157 @@ document.
|
|||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
|
||||
[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
|
||||
|
||||
## Rust 1.63
|
||||
|
||||
Current stable, released 2022-08-11
|
||||
|
||||
[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
|
||||
|
||||
### New Lints
|
||||
|
||||
* [`borrow_deref_ref`]
|
||||
[#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
|
||||
* [`doc_link_with_quotes`]
|
||||
[#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
|
||||
* [`no_effect_replace`]
|
||||
[#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
|
||||
* [`rc_clone_in_vec_init`]
|
||||
[#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
|
||||
* [`derive_partial_eq_without_eq`]
|
||||
[#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
|
||||
* [`mismatching_type_param_order`]
|
||||
[#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
|
||||
* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
|
||||
* [`unused_rounding`]
|
||||
[#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
|
||||
* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
|
||||
* [`swap_ptr_to_ref`]
|
||||
[#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
|
||||
* [`almost_complete_letter_range`]
|
||||
[#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
|
||||
* [`needless_parens_on_range_literals`]
|
||||
[#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
|
||||
* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
|
||||
`nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
|
||||
[#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
|
||||
* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
|
||||
[#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
|
||||
* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
|
||||
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
|
||||
* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
|
||||
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
|
||||
* [`disallowed_methods`]: Now also lints indirect usages
|
||||
[#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
|
||||
* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
|
||||
[#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
|
||||
* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
|
||||
[#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
|
||||
* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
|
||||
[#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
|
||||
* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
|
||||
[#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
|
||||
* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
|
||||
[#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
|
||||
* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
|
||||
method chains inside `map`
|
||||
[#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
|
||||
* [`needless_return`]: Now also lints on macro expressions in return statements
|
||||
[#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
|
||||
* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
|
||||
should extend the default and not replace it
|
||||
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
|
||||
* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
|
||||
config should extend the default and not replace it
|
||||
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
|
||||
* [`never_loop`]: Now checks for `continue` in struct expression
|
||||
[#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`useless_transmute`]: No longer lints on types with erased regions
|
||||
[#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
|
||||
* [`vec_init_then_push`]: No longer lints when further extended
|
||||
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
|
||||
* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
|
||||
[#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
|
||||
* [`redundant_allocation`]: No longer lints on fat pointers that would become
|
||||
thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
|
||||
* [`derive_partial_eq_without_eq`]:
|
||||
* Handle differing predicates applied by `#[derive(PartialEq)]` and
|
||||
`#[derive(Eq)]`
|
||||
[#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
|
||||
* No longer lints on non-public types and better handles generics
|
||||
[#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
|
||||
* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
|
||||
string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
|
||||
* [`branches_sharing_code`]: No longer lints when using different binding names
|
||||
[#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
|
||||
* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
|
||||
desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
|
||||
* [`checked_conversions`]: No longer lints in `const` contexts
|
||||
[#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
|
||||
* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
|
||||
`T::Item` doesn't implement `IntoIterator`
|
||||
[#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
|
||||
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
|
||||
* [`manual_range_contains`]: Fix suggestion for integers with different signs
|
||||
[#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
|
||||
* [`identity_op`]: Add parenthesis to suggestions where required
|
||||
[#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
|
||||
* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
|
||||
[#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
|
||||
* [`rc_clone_in_vec_init`]: Add suggestion
|
||||
[#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
|
||||
* The "unknown field" error messages for config files now wraps the field names
|
||||
[#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
|
||||
* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
|
||||
[#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
|
||||
* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
|
||||
references and not trivially clone-able
|
||||
[#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
|
||||
* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
|
||||
`iter_mut()` or `into_iter()`
|
||||
[#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
|
||||
[#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
|
||||
* Fix ICEs on callable `static`/`const`s
|
||||
[#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
|
||||
* [`needless_late_init`]
|
||||
[#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
|
||||
* Fix ICE in shadow lints
|
||||
[#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
|
||||
[#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
|
||||
* Add a *copy lint name*-button to Clippy's lint list
|
||||
[#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
|
||||
* Display past names of renamed lints on Clippy's lint list
|
||||
[#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
|
||||
* Add the ability to show the lint output in the lint list
|
||||
[#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
|
||||
|
||||
## Rust 1.62
|
||||
|
||||
Current stable, released 2022-06-30
|
||||
Released 2022-06-30
|
||||
|
||||
[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
|
||||
|
||||
|
@ -3496,6 +3642,7 @@ Released 2018-09-13
|
|||
[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
|
||||
[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
|
||||
|
@ -3656,6 +3803,8 @@ Released 2018-09-13
|
|||
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
|
||||
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
|
||||
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
|
||||
[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
|
||||
[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
|
||||
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
|
||||
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
|
||||
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
|
||||
|
@ -3697,6 +3846,7 @@ Released 2018-09-13
|
|||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
|
||||
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
|
||||
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
|
||||
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
|
||||
|
@ -3747,6 +3897,7 @@ Released 2018-09-13
|
|||
[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
|
||||
[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
|
||||
[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
|
||||
[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
|
||||
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
|
||||
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
|
||||
[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
|
||||
|
@ -3827,6 +3978,7 @@ Released 2018-09-13
|
|||
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
|
||||
|
@ -3930,6 +4082,7 @@ Released 2018-09-13
|
|||
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
|
||||
[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
|
||||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
|
||||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
|
@ -4002,6 +4155,7 @@ Released 2018-09-13
|
|||
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
||||
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
|
||||
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
|
|
|
@ -42,6 +42,7 @@ filetime = "0.2"
|
|||
rustc-workspace-hack = "1.0"
|
||||
|
||||
# UI test dependencies
|
||||
clap = { version = "3.1", features = ["derive"] }
|
||||
clippy_utils = { path = "clippy_utils" }
|
||||
derive-new = "0.5"
|
||||
if_chain = "1.0"
|
||||
|
|
|
@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
|
|||
return;
|
||||
}
|
||||
|
||||
let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
|
||||
let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if test_output_file != reference_file {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) {
|
|||
// dependency
|
||||
if fs::read_to_string(project_root.join("Cargo.toml"))
|
||||
.expect("Failed to read clippy Cargo.toml")
|
||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
{
|
||||
return Err(CliError::IntellijSetupActive);
|
||||
}
|
||||
|
@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
|
|||
let args = &["--version"];
|
||||
|
||||
if context.verbose {
|
||||
println!("{}", format_command(&program, &dir, args));
|
||||
println!("{}", format_command(program, &dir, args));
|
||||
}
|
||||
|
||||
let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
|
||||
let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
|
@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
|
|||
Err(CliError::RustfmtNotInstalled)
|
||||
} else {
|
||||
Err(CliError::CommandFailed(
|
||||
format_command(&program, &dir, args),
|
||||
format_command(program, &dir, args),
|
||||
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String {
|
|||
name.split('_')
|
||||
.map(|s| {
|
||||
if s.is_empty() {
|
||||
String::from("")
|
||||
String::new()
|
||||
} else {
|
||||
[&s[0..1].to_uppercase(), &s[1..]].concat()
|
||||
}
|
||||
|
|
|
@ -418,7 +418,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
|||
.expect("failed to find `impl_lint_pass` terminator");
|
||||
|
||||
impl_lint_pass_end += impl_lint_pass_start;
|
||||
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
|
||||
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
|
||||
let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
|
||||
for c in content[lint_name_end..impl_lint_pass_end].chars() {
|
||||
// Remove trailing whitespace
|
||||
|
@ -451,7 +451,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
|||
}
|
||||
|
||||
let mut content =
|
||||
fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{}` from `{}`",
|
||||
|
|
|
@ -18,7 +18,7 @@ quine-mc_cluskey = "0.2"
|
|||
regex-syntax = "0.6"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
tempfile = { version = "3.2", optional = true }
|
||||
tempfile = { version = "3.3.0", optional = true }
|
||||
toml = "0.5"
|
||||
unicode-normalization = "0.1"
|
||||
unicode-script = { version = "0.5", default-features = false }
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected du to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
|
||||
|
||||
let ty_resolved = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Error(_) = ty_resolved.kind() {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
None,
|
||||
"consider giving the type explicitly",
|
||||
);
|
||||
} else {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AS_UNDERSCORE,
|
||||
expr.span,
|
||||
"using `as _` conversion",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"consider giving the type explicitly",
|
||||
ty_resolved,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_no_std_crate;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
|
||||
|
||||
pub struct BorrowAsPtr {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl BorrowAsPtr {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(left_expr, ty) = &expr.kind;
|
||||
if let TyKind::Ptr(_) = ty.kind;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
|
||||
|
||||
then {
|
||||
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
let macro_name = match mutability {
|
||||
Mutability::Not => "addr_of",
|
||||
Mutability::Mut => "addr_of_mut",
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROW_AS_PTR,
|
||||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!(
|
||||
"{}::ptr::{}!({})",
|
||||
core_or_std,
|
||||
macro_name,
|
||||
snippet_opt(cx, e.span).unwrap()
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
|
@ -29,22 +29,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(_x: &str) {}
|
||||
///
|
||||
/// let s = &String::new();
|
||||
///
|
||||
/// let a: &String = &* s;
|
||||
/// foo(&*s);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(_x: &str) {}
|
||||
/// # let s = &String::new();
|
||||
/// let a: &String = s;
|
||||
/// foo(&**s);
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub BORROW_DEREF_REF,
|
||||
complexity,
|
||||
"deref on an immutable reference returns the same type as itself"
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for naive byte counts
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The [`bytecount`](https://crates.io/crates/bytecount)
|
||||
/// crate has methods to count your bytes faster, especially for large slices.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If you have predominantly small slices, the
|
||||
/// `bytecount::count(..)` method may actually be slower. However, if you can
|
||||
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
|
||||
/// faster in those cases.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = vec.iter().filter(|x| **x == 0u8).count();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # let vec = vec![1_u8];
|
||||
/// let count = bytecount::count(&vec, 0u8);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NAIVE_BYTECOUNT,
|
||||
pedantic,
|
||||
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
|
||||
if count.ident.name == sym::count;
|
||||
if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
|
||||
if filter.ident.name == sym!(filter);
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, args, _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
filter_recv
|
||||
}
|
||||
} else {
|
||||
filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It checks for `str::bytes().count()` and suggests replacing it with
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `str::bytes().count()` is longer and may not be as performant as using
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// "hello".bytes().count();
|
||||
/// String::from("hello").bytes().count();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// "hello".len();
|
||||
/// String::from("hello").len();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub BYTES_COUNT_TO_LEN,
|
||||
complexity,
|
||||
"Using `bytes().count()` when `len()` performs the same functionality"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
|
||||
|
||||
if let [bytes_expr] = &**expr_args;
|
||||
if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
|
||||
if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
|
||||
if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
|
||||
|
||||
if let [str_expr] = &**bytes_args;
|
||||
let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind, PathSegment};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{source_map::Spanned, symbol::sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `ends_with` with possible file extensions
|
||||
/// and suggests to use a case-insensitive approach instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// filename.ends_with(".rs")
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn is_rust_file(filename: &str) -> bool {
|
||||
/// let filename = std::path::Path::new(filename);
|
||||
/// filename.extension()
|
||||
/// .map(|ext| ext.eq_ignore_ascii_case("rs"))
|
||||
/// .unwrap_or(false)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.51.0"]
|
||||
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
pedantic,
|
||||
"Checks for calls to ends_with with case-sensitive file extensions"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
|
||||
|
||||
fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
|
||||
if ident.as_str() == "ends_with";
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
if ext_literal.as_str().starts_with('.');
|
||||
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
then {
|
||||
let mut ty = ctx.typeck_results().expr_ty(obj);
|
||||
ty = match ty.kind() {
|
||||
ty::Ref(_, ty, ..) => *ty,
|
||||
_ => ty
|
||||
};
|
||||
|
||||
match ty.kind() {
|
||||
ty::Str => {
|
||||
return Some(span);
|
||||
},
|
||||
ty::Adt(def, _) => {
|
||||
if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
|
||||
return Some(span);
|
||||
}
|
||||
},
|
||||
_ => { return None; }
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
|
||||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
|
||||
span_lint_and_help(
|
||||
ctx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
25
clippy_lints/src/casts/as_underscore.rs
Normal file
25
clippy_lints/src/casts/as_underscore.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use super::AS_UNDERSCORE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
|
||||
if matches!(ty.kind, TyKind::Infer) {
|
||||
span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
|
||||
let ty_resolved = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Error(_) = ty_resolved.kind() {
|
||||
diag.help("consider giving the type explicitly");
|
||||
} else {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"consider giving the type explicitly",
|
||||
ty_resolved,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
37
clippy_lints/src/casts/borrow_as_ptr.rs
Normal file
37
clippy_lints/src/casts/borrow_as_ptr.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_no_std_crate;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::BORROW_AS_PTR;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
cast_expr: &'tcx Expr<'_>,
|
||||
cast_to: &'tcx Ty<'_>,
|
||||
) {
|
||||
if matches!(cast_to.kind, TyKind::Ptr(_))
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
|
||||
{
|
||||
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
let macro_name = match mutability {
|
||||
Mutability::Not => "addr_of",
|
||||
Mutability::Mut => "addr_of_mut",
|
||||
};
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROW_AS_PTR,
|
||||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
mod as_underscore;
|
||||
mod borrow_as_ptr;
|
||||
mod cast_abs_to_unsigned;
|
||||
mod cast_enum_constructor;
|
||||
mod cast_lossless;
|
||||
|
@ -16,7 +18,7 @@ mod ptr_as_ptr;
|
|||
mod unnecessary_cast;
|
||||
mod utils;
|
||||
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -506,6 +508,67 @@ declare_clippy_lint! {
|
|||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
@ -534,7 +597,9 @@ impl_lint_pass!(Casts => [
|
|||
PTR_AS_PTR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ABS_TO_UNSIGNED
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
@ -547,8 +612,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
|
||||
return;
|
||||
}
|
||||
let (cast_from, cast_to) = (
|
||||
|
@ -575,6 +640,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
|
||||
as_underscore::check(cx, expr, cast_to_hir);
|
||||
|
||||
if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
}
|
||||
}
|
||||
|
||||
cast_ref_to_mut::check(cx, expr);
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
|
||||
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{
|
||||
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
|
||||
walk_to_expr_usage,
|
||||
};
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
|
||||
TraitItemKind, TyKind, UnOp,
|
||||
self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
|
||||
GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
|
||||
Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
|
||||
use rustc_middle::ty::{
|
||||
self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
|
||||
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
|
||||
};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -151,6 +161,7 @@ pub struct Dereferencing {
|
|||
/// been finished. Note we can't lint at the end of every body as they can be nested within each
|
||||
/// other.
|
||||
current_body: Option<BodyId>,
|
||||
|
||||
/// The list of locals currently being checked by the lint.
|
||||
/// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
|
||||
/// This is needed for or patterns where one of the branches can be linted, but another can not
|
||||
|
@ -158,6 +169,19 @@ pub struct Dereferencing {
|
|||
///
|
||||
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
||||
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
||||
|
||||
// `IntoIterator` for arrays requires Rust 1.53.
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Dereferencing {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Dereferencing::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StateData {
|
||||
|
@ -170,6 +194,7 @@ struct StateData {
|
|||
struct DerefedBorrow {
|
||||
count: usize,
|
||||
msg: &'static str,
|
||||
snip_expr: Option<HirId>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
|
@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
match (self.state.take(), kind) {
|
||||
(None, kind) => {
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
let (position, adjustments) = walk_parents(cx, expr);
|
||||
let (position, adjustments) = walk_parents(cx, expr, self.msrv);
|
||||
|
||||
match kind {
|
||||
RefOp::Deref => {
|
||||
|
@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
let deref_msg =
|
||||
"this expression creates a reference which is immediately dereferenced by the compiler";
|
||||
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
|
||||
let impl_msg = "the borrowed expression implements the required traits";
|
||||
|
||||
let (required_refs, msg) = if position.can_auto_borrow() {
|
||||
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
|
||||
let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
|
||||
(1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
|
||||
} else if let Position::ImplArg(hir_id) = position {
|
||||
(0, impl_msg, Some(hir_id))
|
||||
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
|
||||
next_adjust.map(|a| &a.kind)
|
||||
{
|
||||
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
|
||||
{
|
||||
(3, deref_msg)
|
||||
(3, deref_msg, None)
|
||||
} else {
|
||||
(2, deref_msg)
|
||||
(2, deref_msg, None)
|
||||
}
|
||||
} else {
|
||||
(2, deref_msg)
|
||||
(2, deref_msg, None)
|
||||
};
|
||||
|
||||
if deref_count >= required_refs {
|
||||
|
@ -354,6 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
// can't be removed without breaking the code. See earlier comment.
|
||||
count: deref_count - required_refs,
|
||||
msg,
|
||||
snip_expr,
|
||||
}),
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
|
@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
spans: vec![pat.span],
|
||||
app,
|
||||
replacements: vec![(pat.span, snip.into())],
|
||||
hir_id: pat.hir_id
|
||||
hir_id: pat.hir_id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -542,6 +571,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
self.current_body = None;
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn try_parse_ref_op<'tcx>(
|
||||
|
@ -594,6 +625,7 @@ enum Position {
|
|||
/// The method is defined on a reference type. e.g. `impl Foo for &T`
|
||||
MethodReceiverRefImpl,
|
||||
Callee,
|
||||
ImplArg(HirId),
|
||||
FieldAccess(Symbol),
|
||||
Postfix,
|
||||
Deref,
|
||||
|
@ -630,7 +662,7 @@ impl Position {
|
|||
| Self::Callee
|
||||
| Self::FieldAccess(_)
|
||||
| Self::Postfix => PREC_POSTFIX,
|
||||
Self::Deref => PREC_PREFIX,
|
||||
Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
|
||||
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
|
||||
}
|
||||
}
|
||||
|
@ -639,8 +671,12 @@ impl Position {
|
|||
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
|
||||
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
|
||||
/// locations as those follow different rules.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn walk_parents<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||
let mut adjustments = [].as_slice();
|
||||
let mut precedence = 0i8;
|
||||
let ctxt = e.span.ctxt();
|
||||
|
@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
|||
.iter()
|
||||
.position(|arg| arg.hir_id == child_id)
|
||||
.zip(expr_sig(cx, func))
|
||||
.and_then(|(i, sig)| sig.input_with_hir(i))
|
||||
.map(|(hir_ty, ty)| match hir_ty {
|
||||
// Type inference for closures can depend on how they're called. Only go by the explicit
|
||||
// types here.
|
||||
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
|
||||
None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
|
||||
.position_for_arg(),
|
||||
.and_then(|(i, sig)| {
|
||||
sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
|
||||
// Type inference for closures can depend on how they're called. Only go by the explicit
|
||||
// types here.
|
||||
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
|
||||
None => {
|
||||
if let ty::Param(param_ty) = ty.skip_binder().kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
|
||||
.position_for_arg()
|
||||
}
|
||||
},
|
||||
})
|
||||
}),
|
||||
ExprKind::MethodCall(_, args, _) => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
|
||||
|
@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
|||
.and_then(|subs| subs.get(1..))
|
||||
{
|
||||
Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
|
||||
None => cx.tcx.mk_substs([].iter()),
|
||||
None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
|
||||
} && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
|
||||
// Trait methods taking `&self`
|
||||
sub_ty
|
||||
|
@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
|||
Position::MethodReceiver
|
||||
}
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
|
||||
if let ty::Param(param_ty) = ty.kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -948,6 +996,205 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
|||
v.0
|
||||
}
|
||||
|
||||
// Checks whether:
|
||||
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
|
||||
// * `e`'s type implements `Trait` and is copyable
|
||||
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
|
||||
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
|
||||
// be moved, but it cannot be.
|
||||
fn needless_borrow_impl_arg_position<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
parent: &Expr<'tcx>,
|
||||
arg_index: usize,
|
||||
param_ty: ParamTy,
|
||||
mut expr: &Expr<'tcx>,
|
||||
precedence: i8,
|
||||
msrv: Option<RustcVersion>,
|
||||
) -> Position {
|
||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||
|
||||
let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
|
||||
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
|
||||
let substs_with_expr_ty = cx
|
||||
.typeck_results()
|
||||
.node_substs(if let ExprKind::Call(callee, _) = parent.kind {
|
||||
callee.hir_id
|
||||
} else {
|
||||
parent.hir_id
|
||||
});
|
||||
|
||||
let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
|
||||
let projection_predicates = predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
||||
Some(projection_predicate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut trait_with_ref_mut_self_method = false;
|
||||
|
||||
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
||||
if predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
{
|
||||
Some(trait_predicate.trait_ref.def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.inspect(|trait_def_id| {
|
||||
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
||||
})
|
||||
.all(|trait_def_id| {
|
||||
Some(trait_def_id) == destruct_trait_def_id
|
||||
|| Some(trait_def_id) == sized_trait_def_id
|
||||
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
||||
})
|
||||
{
|
||||
return Position::Other(precedence);
|
||||
}
|
||||
|
||||
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
|
||||
// elements are modified each time `check_referent` is called.
|
||||
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
|
||||
|
||||
let mut check_referent = |referent| {
|
||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||
|
||||
if !is_copy(cx, referent_ty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !replace_types(
|
||||
cx,
|
||||
param_ty,
|
||||
referent_ty,
|
||||
fn_sig,
|
||||
arg_index,
|
||||
&projection_predicates,
|
||||
&mut substs_with_referent_ty,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
predicates.iter().all(|predicate| {
|
||||
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
|
||||
&& ty.is_array()
|
||||
&& !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
|
||||
let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
|
||||
cx.tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
|
||||
})
|
||||
};
|
||||
|
||||
let mut needless_borrow = false;
|
||||
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
||||
if !check_referent(referent) {
|
||||
break;
|
||||
}
|
||||
expr = referent;
|
||||
needless_borrow = true;
|
||||
}
|
||||
|
||||
if needless_borrow {
|
||||
Position::ImplArg(expr.hir_id)
|
||||
} else {
|
||||
Position::Other(precedence)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.any(|assoc_item| {
|
||||
if assoc_item.fn_has_self_parameter {
|
||||
let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
|
||||
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
|
||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
||||
fn replace_types<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
param_ty: ParamTy,
|
||||
new_ty: Ty<'tcx>,
|
||||
fn_sig: FnSig<'tcx>,
|
||||
arg_index: usize,
|
||||
projection_predicates: &[ProjectionPredicate<'tcx>],
|
||||
substs: &mut [ty::GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
let mut replaced = BitSet::new_empty(substs.len());
|
||||
|
||||
let mut deque = VecDeque::with_capacity(substs.len());
|
||||
deque.push_back((param_ty, new_ty));
|
||||
|
||||
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
||||
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
||||
if !fn_sig
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
||||
|
||||
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
||||
if replaced.insert(param_ty.index) {
|
||||
for projection_predicate in projection_predicates {
|
||||
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
&& let ty::Term::Ty(term_ty) = projection_predicate.term
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
{
|
||||
let item_def_id = projection_predicate.projection_ty.item_def_id;
|
||||
let assoc_item = cx.tcx.associated_item(item_def_id);
|
||||
let projection = cx.tcx
|
||||
.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
|
||||
|
||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||
&& substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
||||
{
|
||||
deque.push_back((*term_param_ty, projected_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
struct TyPosition<'tcx> {
|
||||
position: Position,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
|
@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
|||
},
|
||||
State::DerefedBorrow(state) => {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||
let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
|
||||
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
||||
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
|
||||
let sugg = if !snip_is_macro
|
||||
|
|
|
@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
|
|||
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
|
||||
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
|
||||
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
|
||||
trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
|
||||
trait_ref: TraitRef::new(
|
||||
eq_trait_id,
|
||||
tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
|
||||
),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
})))
|
||||
|
|
|
@ -21,7 +21,7 @@ declare_clippy_lint! {
|
|||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
|
|
|
@ -39,7 +39,7 @@ declare_clippy_lint! {
|
|||
/// // a.rs
|
||||
/// use crate::b;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DUPLICATE_MOD,
|
||||
suspicious,
|
||||
"file loaded as module multiple times"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir;
|
||||
use clippy_utils::ty::contains_ty;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
|
@ -30,18 +29,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// fn foo(x: Box<u32>) {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = 1;
|
||||
/// foo(x);
|
||||
/// println!("{}", x);
|
||||
/// fn foo(x: u32) {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BOXED_LOCAL,
|
||||
|
@ -172,7 +165,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
// skip if there is a `self` parameter binding to a type
|
||||
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
|
||||
if let Some(trait_self_ty) = self.trait_self_ty {
|
||||
if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
|
||||
if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
|||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if format_args.value_args.is_empty() {
|
||||
match *format_args.format_string_parts {
|
||||
if format_args.args.is_empty() {
|
||||
match *format_args.format_string.parts {
|
||||
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
|
||||
[_] => {
|
||||
if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
|
||||
let sugg = format!("{}.to_string()", s_expand);
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
}
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
|
||||
let sugg = format!("{}.to_string()", s_expand);
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
},
|
||||
[..] => {},
|
||||
}
|
||||
} else if let [value] = *format_args.value_args {
|
||||
} else if let [arg] = &*format_args.args {
|
||||
let value = arg.param.value;
|
||||
if_chain! {
|
||||
if format_args.format_string_parts == [kw::Empty];
|
||||
if format_args.format_string.parts == [kw::Empty];
|
||||
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
|
||||
ty::Str => true,
|
||||
_ => false,
|
||||
};
|
||||
if let Some(args) = format_args.args();
|
||||
if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
|
||||
if !arg.format.has_string_formatting();
|
||||
then {
|
||||
let is_new_string = match value.kind {
|
||||
ExprKind::Binary(..) => true,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::Ty;
|
||||
|
@ -74,20 +75,16 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
|
||||
if is_format_macro(cx, macro_def_id);
|
||||
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
|
||||
if let Some(args) = format_args.args();
|
||||
then {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if arg.format_trait != sym::Display {
|
||||
for arg in &format_args.args {
|
||||
if arg.format.has_string_formatting() {
|
||||
continue;
|
||||
}
|
||||
if arg.has_string_formatting() {
|
||||
if is_aliased(&format_args, arg.param.value.hir_id) {
|
||||
continue;
|
||||
}
|
||||
if is_aliased(&args, i) {
|
||||
continue;
|
||||
}
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
|
||||
check_to_string_in_format_args(cx, name, arg.value);
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
|
||||
check_to_string_in_format_args(cx, name, arg.param.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
if is_diag_trait_item(cx, method_def_id, sym::ToString);
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
|
||||
let (n_needed_derefs, target) =
|
||||
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
|
||||
if implements_trait(cx, target, display_trait_id, &[]);
|
||||
if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
let (n_needed_derefs, target) = count_needed_derefs(
|
||||
receiver_ty,
|
||||
cx.typeck_results().expr_adjustments(receiver).iter(),
|
||||
);
|
||||
if implements_trait(cx, target, display_trait_id, &[]) {
|
||||
if n_needed_derefs == 0 {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span.with_lo(receiver.span.hi()),
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
|
||||
"use this",
|
||||
format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
|
||||
if n_needed_derefs == 0 && !needs_ref {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span.with_lo(receiver.span.hi()),
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
),
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
),
|
||||
"use this",
|
||||
format!(
|
||||
"{}{:*>width$}{}",
|
||||
if needs_ref { "&" } else { "" },
|
||||
"",
|
||||
receiver_snippet,
|
||||
width = n_needed_derefs
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
|
||||
fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
|
||||
let value = args[i].value;
|
||||
args.iter()
|
||||
.enumerate()
|
||||
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
|
||||
// Returns true if `hir_id` is referred to by multiple format params
|
||||
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
|
||||
args.params()
|
||||
.filter(|param| param.value.hir_id == hir_id)
|
||||
.at_most_one()
|
||||
.is_err()
|
||||
}
|
||||
|
||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
|
||||
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
|||
if let macro_def_id = outer_macro.def_id;
|
||||
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
|
||||
if is_format_macro(cx, macro_def_id);
|
||||
if let Some(args) = format_args.args();
|
||||
then {
|
||||
for arg in args {
|
||||
if arg.format_trait != impl_trait.name {
|
||||
for arg in format_args.args {
|
||||
if arg.format.r#trait != impl_trait.name {
|
||||
continue;
|
||||
}
|
||||
check_format_arg_self(cx, expr, &arg, impl_trait);
|
||||
|
@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
|||
}
|
||||
}
|
||||
|
||||
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
|
||||
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
|
||||
// Handle multiple dereferencing of references e.g. &&self
|
||||
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
|
||||
// Since the argument to fmt is itself a reference: &self
|
||||
let reference = peel_ref_operators(cx, arg.value);
|
||||
let reference = peel_ref_operators(cx, arg.param.value);
|
||||
let map = cx.tcx.hir();
|
||||
// Is the reference self?
|
||||
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for using `x.get(0)` instead of
|
||||
/// `x.first()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `x.first()` is easier to read and has the same
|
||||
/// result.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.get(0);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = vec![2, 3, 5];
|
||||
/// let first_element = x.first();
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub GET_FIRST,
|
||||
style,
|
||||
"Using `x.get(0)` when `x.first()` is simpler"
|
||||
}
|
||||
declare_lint_pass!(GetFirst => [GET_FIRST]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for GetFirst {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
|
||||
|
||||
if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(
|
||||
cx,
|
||||
struct_calling_on.span, "..",
|
||||
&mut applicability,
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::intravisit::{self as visit, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
let mut arm_visit = ArmVisitor {
|
||||
mutex_lock_called: false,
|
||||
found_mutex: None,
|
||||
cx,
|
||||
};
|
||||
let mut op_visit = OppVisitor {
|
||||
mutex_lock_called: false,
|
||||
found_mutex: None,
|
||||
cx,
|
||||
};
|
||||
let mut arm_visit = ArmVisitor { found_mutex: None, cx };
|
||||
let mut op_visit = OppVisitor { found_mutex: None, cx };
|
||||
if let Some(higher::IfLet {
|
||||
let_expr,
|
||||
if_then,
|
||||
|
@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
|||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
op_visit.visit_expr(let_expr);
|
||||
if op_visit.mutex_lock_called {
|
||||
if let Some(op_mutex) = op_visit.found_mutex {
|
||||
arm_visit.visit_expr(if_then);
|
||||
arm_visit.visit_expr(if_else);
|
||||
|
||||
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
|
||||
span_lint_and_help(
|
||||
if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
|
||||
let diag = |diag: &mut Diagnostic| {
|
||||
diag.span_label(
|
||||
op_mutex.span,
|
||||
"this Mutex will remain locked for the entire `if let`-block...",
|
||||
);
|
||||
diag.span_label(
|
||||
arm_mutex.span,
|
||||
"... and is tried to lock again here, which will always deadlock.",
|
||||
);
|
||||
diag.help("move the lock call outside of the `if let ...` expression");
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IF_LET_MUTEX,
|
||||
expr.span,
|
||||
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
|
||||
None,
|
||||
"move the lock call outside of the `if let ...` expression",
|
||||
diag,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
|||
|
||||
/// Checks if `Mutex::lock` is called in the `if let` expr.
|
||||
pub struct OppVisitor<'a, 'tcx> {
|
||||
mutex_lock_called: bool,
|
||||
found_mutex: Option<&'tcx Expr<'tcx>>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||
self.found_mutex = Some(mutex);
|
||||
self.mutex_lock_called = true;
|
||||
return;
|
||||
}
|
||||
visit::walk_expr(self, expr);
|
||||
|
@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
|
|||
|
||||
/// Checks if `Mutex::lock` is called in any of the branches.
|
||||
pub struct ArmVisitor<'a, 'tcx> {
|
||||
mutex_lock_called: bool,
|
||||
found_mutex: Option<&'tcx Expr<'tcx>>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||
self.found_mutex = Some(mutex);
|
||||
self.mutex_lock_called = true;
|
||||
return;
|
||||
}
|
||||
visit::walk_expr(self, expr);
|
||||
|
@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
|
||||
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
|
||||
self.found_mutex
|
||||
.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
|
||||
fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
|
||||
self.found_mutex.and_then(|arm_mutex| {
|
||||
SpanlessEq::new(self.cx)
|
||||
.eq_expr(op_mutex, arm_mutex)
|
||||
.then_some(arm_mutex)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt
|
|||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
||||
if path.ident.as_str() == "lock";
|
||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if is_type_diagnostic_item(cx, ty, sym::Mutex);
|
||||
then {
|
||||
Some(self_arg)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for if-else that could be written to `bool::then`.
|
||||
/// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Looks a little redundant. Using `bool::then` helps it have less lines of code.
|
||||
/// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
|
||||
/// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
|
||||
/// in comparison to `bool::then`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
|
@ -39,7 +41,7 @@ declare_clippy_lint! {
|
|||
#[clippy::version = "1.53.0"]
|
||||
pub IF_THEN_SOME_ELSE_NONE,
|
||||
restriction,
|
||||
"Finds if-else that could be written using `bool::then`"
|
||||
"Finds if-else that could be written using either `bool::then` or `bool::then_some`"
|
||||
}
|
||||
|
||||
pub struct IfThenSomeElseNone {
|
||||
|
@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
|
|||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
|
||||
return;
|
||||
}
|
||||
|
@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
|
||||
if let ExprKind::Block(then_block, _) = then.kind;
|
||||
if let Some(then_expr) = then_block.expr;
|
||||
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
|
||||
if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
|
||||
if is_lang_ctor(cx, then_call_qpath, OptionSome);
|
||||
if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
|
||||
if is_lang_ctor(cx, qpath, OptionNone);
|
||||
if !stmts_contains_early_return(then_block.stmts);
|
||||
then {
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({})", cond_snip)
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
|
||||
let closure_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} else {
|
||||
format!("{{ /* snippet */ {} }}", arg_snip)
|
||||
};
|
||||
let help = format!(
|
||||
"consider using `bool::then` like: `{}.then(|| {})`",
|
||||
cond_snip,
|
||||
closure_body,
|
||||
);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_THEN_SOME_ELSE_NONE,
|
||||
expr.span,
|
||||
"this could be simplified with `bool::then`",
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
}
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
|
||||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind
|
||||
&& is_lang_ctor(cx, then_call_qpath, OptionSome)
|
||||
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
|
||||
&& is_lang_ctor(cx, qpath, OptionNone)
|
||||
&& !stmts_contains_early_return(then_block.stmts)
|
||||
{
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({})", cond_snip)
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
|
||||
let mut method_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} else {
|
||||
format!("{{ /* snippet */ {} }}", arg_snip)
|
||||
};
|
||||
let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
|
||||
"then_some"
|
||||
} else {
|
||||
method_body.insert_str(0, "|| ");
|
||||
"then"
|
||||
};
|
||||
|
||||
let help = format!(
|
||||
"consider using `bool::{}` like: `{}.{}({})`",
|
||||
method_name, cond_snip, method_name, method_body,
|
||||
);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_THEN_SOME_ELSE_NONE,
|
||||
expr.span,
|
||||
&format!("this could be simplified with `bool::{}`", method_name),
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
|
@ -82,7 +81,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
|
||||
LintId::of(infinite_iter::INFINITE_ITER),
|
||||
|
@ -128,7 +126,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
|
@ -150,17 +147,20 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::BYTES_NTH),
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::CLONE_ON_COPY),
|
||||
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::EXPECT_FUN_CALL),
|
||||
LintId::of(methods::EXTEND_WITH_DRAIN),
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::GET_LAST_WITH_LEN),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
|
@ -178,13 +178,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MANUAL_SPLIT_ONCE),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
|
@ -193,6 +196,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
|
@ -202,14 +207,18 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(methods::STRING_EXTEND_CHARS),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::SUSPICIOUS_TO_OWNED),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FOLD),
|
||||
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::UNNECESSARY_TO_OWNED),
|
||||
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::WRONG_SELF_CONVENTION),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
|
@ -224,8 +233,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
|
||||
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
|
||||
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(needless_bool::BOOL_COMPARISON),
|
||||
|
@ -245,7 +254,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
|
@ -275,7 +284,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
|
@ -286,7 +294,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(reference::DEREF_ADDROF),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(returns::LET_AND_RETURN),
|
||||
LintId::of(returns::NEEDLESS_RETURN),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
|
@ -314,10 +321,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
LintId::of(types::REDUNDANT_ALLOCATION),
|
||||
|
@ -325,7 +332,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
|
@ -333,9 +339,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unused_peekable::UNUSED_PEEKABLE),
|
||||
LintId::of(unused_unit::UNUSED_UNIT),
|
||||
LintId::of(unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(unwrap::UNNECESSARY_UNWRAP),
|
||||
|
@ -343,7 +349,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(vec::USELESS_VEC),
|
||||
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
|
||||
LintId::of(write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(write::PRINT_LITERAL),
|
||||
LintId::of(write::PRINT_WITH_NEWLINE),
|
||||
|
|
|
@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
|
||||
|
@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::CLONE_ON_COPY),
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
|
@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
|
||||
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
|
@ -69,6 +72,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(no_effect::NO_EFFECT),
|
||||
LintId::of(no_effect::UNNECESSARY_OPERATION),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(operators::DOUBLE_COMPARISONS),
|
||||
LintId::of(operators::DURATION_SUBSEC),
|
||||
LintId::of(operators::IDENTITY_OP),
|
||||
|
@ -76,11 +80,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(reference::DEREF_ADDROF),
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
|
||||
LintId::of(swap::MANUAL_SWAP),
|
||||
|
@ -99,7 +101,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
|||
LintId::of(types::TYPE_COMPLEXITY),
|
||||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
|
||||
|
|
|
@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
|||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
LintId::of(operators::CMP_NAN),
|
||||
|
@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
|||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::UNIT_CMP),
|
||||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
])
|
||||
|
|
|
@ -38,7 +38,6 @@ store.register_lints(&[
|
|||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
as_underscore::AS_UNDERSCORE,
|
||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
|
@ -59,16 +58,14 @@ store.register_lints(&[
|
|||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
bytes_count_to_len::BYTES_COUNT_TO_LEN,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
cargo::MULTIPLE_CRATE_VERSIONS,
|
||||
cargo::NEGATIVE_FEATURE_NAMES,
|
||||
cargo::REDUNDANT_FEATURE_NAMES,
|
||||
cargo::WILDCARD_DEPENDENCIES,
|
||||
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
casts::AS_UNDERSCORE,
|
||||
casts::BORROW_AS_PTR,
|
||||
casts::CAST_ABS_TO_UNSIGNED,
|
||||
casts::CAST_ENUM_CONSTRUCTOR,
|
||||
casts::CAST_ENUM_TRUNCATION,
|
||||
|
@ -177,7 +174,6 @@ store.register_lints(&[
|
|||
functions::TOO_MANY_ARGUMENTS,
|
||||
functions::TOO_MANY_LINES,
|
||||
future_not_send::FUTURE_NOT_SEND,
|
||||
get_first::GET_FIRST,
|
||||
if_let_mutex::IF_LET_MUTEX,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
|
@ -246,12 +242,10 @@ store.register_lints(&[
|
|||
manual_bits::MANUAL_BITS,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
manual_rem_euclid::MANUAL_REM_EUCLID,
|
||||
manual_retain::MANUAL_RETAIN,
|
||||
manual_string_new::MANUAL_STRING_NEW,
|
||||
manual_strip::MANUAL_STRIP,
|
||||
map_clone::MAP_CLONE,
|
||||
map_err_ignore::MAP_ERR_IGNORE,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
match_result_ok::MATCH_RESULT_OK,
|
||||
|
@ -284,13 +278,16 @@ store.register_lints(&[
|
|||
mem_replace::MEM_REPLACE_WITH_DEFAULT,
|
||||
mem_replace::MEM_REPLACE_WITH_UNINIT,
|
||||
methods::BIND_INSTEAD_OF_MAP,
|
||||
methods::BYTES_COUNT_TO_LEN,
|
||||
methods::BYTES_NTH,
|
||||
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::CHARS_NEXT_CMP,
|
||||
methods::CLONED_INSTEAD_OF_COPIED,
|
||||
methods::CLONE_DOUBLE_REF,
|
||||
methods::CLONE_ON_COPY,
|
||||
methods::CLONE_ON_REF_PTR,
|
||||
methods::COLLAPSIBLE_STR_REPLACE,
|
||||
methods::ERR_EXPECT,
|
||||
methods::EXPECT_FUN_CALL,
|
||||
methods::EXPECT_USED,
|
||||
|
@ -302,6 +299,7 @@ store.register_lints(&[
|
|||
methods::FLAT_MAP_IDENTITY,
|
||||
methods::FLAT_MAP_OPTION,
|
||||
methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
methods::GET_FIRST,
|
||||
methods::GET_LAST_WITH_LEN,
|
||||
methods::GET_UNWRAP,
|
||||
methods::IMPLICIT_CLONE,
|
||||
|
@ -315,22 +313,30 @@ store.register_lints(&[
|
|||
methods::ITER_NEXT_SLICE,
|
||||
methods::ITER_NTH,
|
||||
methods::ITER_NTH_ZERO,
|
||||
methods::ITER_ON_EMPTY_COLLECTIONS,
|
||||
methods::ITER_ON_SINGLE_ITEMS,
|
||||
methods::ITER_OVEREAGER_CLONED,
|
||||
methods::ITER_SKIP_NEXT,
|
||||
methods::ITER_WITH_DRAIN,
|
||||
methods::MANUAL_FILTER_MAP,
|
||||
methods::MANUAL_FIND_MAP,
|
||||
methods::MANUAL_OK_OR,
|
||||
methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
methods::MANUAL_SPLIT_ONCE,
|
||||
methods::MANUAL_STR_REPEAT,
|
||||
methods::MAP_CLONE,
|
||||
methods::MAP_COLLECT_RESULT_UNIT,
|
||||
methods::MAP_ERR_IGNORE,
|
||||
methods::MAP_FLATTEN,
|
||||
methods::MAP_IDENTITY,
|
||||
methods::MAP_UNWRAP_OR,
|
||||
methods::MUT_MUTEX_LOCK,
|
||||
methods::NAIVE_BYTECOUNT,
|
||||
methods::NEEDLESS_OPTION_AS_DEREF,
|
||||
methods::NEEDLESS_OPTION_TAKE,
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::NONSENSICAL_OPEN_OPTIONS,
|
||||
methods::NO_EFFECT_REPLACE,
|
||||
methods::OBFUSCATED_IF_ELSE,
|
||||
methods::OK_EXPECT,
|
||||
|
@ -339,25 +345,34 @@ store.register_lints(&[
|
|||
methods::OPTION_MAP_OR_NONE,
|
||||
methods::OR_FUN_CALL,
|
||||
methods::OR_THEN_UNWRAP,
|
||||
methods::PATH_BUF_PUSH_OVERWRITE,
|
||||
methods::RANGE_ZIP_WITH_LEN,
|
||||
methods::REPEAT_ONCE,
|
||||
methods::RESULT_MAP_OR_INTO_OPTION,
|
||||
methods::SEARCH_IS_SOME,
|
||||
methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
methods::SINGLE_CHAR_ADD_STR,
|
||||
methods::SINGLE_CHAR_PATTERN,
|
||||
methods::SKIP_WHILE_NEXT,
|
||||
methods::STABLE_SORT_PRIMITIVE,
|
||||
methods::STRING_EXTEND_CHARS,
|
||||
methods::SUSPICIOUS_MAP,
|
||||
methods::SUSPICIOUS_SPLITN,
|
||||
methods::SUSPICIOUS_TO_OWNED,
|
||||
methods::UNINIT_ASSUMED_INIT,
|
||||
methods::UNIT_HASH,
|
||||
methods::UNNECESSARY_FILTER_MAP,
|
||||
methods::UNNECESSARY_FIND_MAP,
|
||||
methods::UNNECESSARY_FOLD,
|
||||
methods::UNNECESSARY_JOIN,
|
||||
methods::UNNECESSARY_LAZY_EVALUATIONS,
|
||||
methods::UNNECESSARY_SORT_BY,
|
||||
methods::UNNECESSARY_TO_OWNED,
|
||||
methods::UNWRAP_OR_ELSE_DEFAULT,
|
||||
methods::UNWRAP_USED,
|
||||
methods::USELESS_ASREF,
|
||||
methods::VEC_RESIZE_TO_ZERO,
|
||||
methods::VERBOSE_FILE_READS,
|
||||
methods::WRONG_SELF_CONVENTION,
|
||||
methods::ZST_OFFSET,
|
||||
minmax::MIN_MAX,
|
||||
|
@ -384,9 +399,9 @@ store.register_lints(&[
|
|||
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
|
||||
module_style::MOD_MODULE_FILES,
|
||||
module_style::SELF_NAMED_MODULE_FILES,
|
||||
multi_assignments::MULTI_ASSIGNMENTS,
|
||||
mut_key::MUTABLE_KEY_TYPE,
|
||||
mut_mut::MUT_MUT,
|
||||
mut_mutex_lock::MUT_MUTEX_LOCK,
|
||||
mut_reference::UNNECESSARY_MUT_PASSED,
|
||||
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
mutex_atomic::MUTEX_ATOMIC,
|
||||
|
@ -418,7 +433,6 @@ store.register_lints(&[
|
|||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ARITHMETIC,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
|
@ -457,7 +471,6 @@ store.register_lints(&[
|
|||
partialeq_to_none::PARTIALEQ_TO_NONE,
|
||||
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
|
||||
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
|
||||
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
precedence::PRECEDENCE,
|
||||
ptr::CMP_NULL,
|
||||
|
@ -470,7 +483,6 @@ store.register_lints(&[
|
|||
ranges::MANUAL_RANGE_CONTAINS,
|
||||
ranges::RANGE_MINUS_ONE,
|
||||
ranges::RANGE_PLUS_ONE,
|
||||
ranges::RANGE_ZIP_WITH_LEN,
|
||||
ranges::REVERSED_EMPTY_RANGES,
|
||||
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
|
||||
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
|
||||
|
@ -486,7 +498,6 @@ store.register_lints(&[
|
|||
reference::DEREF_ADDROF,
|
||||
regex::INVALID_REGEX,
|
||||
regex::TRIVIAL_REGEX,
|
||||
repeat_once::REPEAT_ONCE,
|
||||
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
|
||||
returns::LET_AND_RETURN,
|
||||
returns::NEEDLESS_RETURN,
|
||||
|
@ -501,7 +512,6 @@ store.register_lints(&[
|
|||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
|
||||
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
|
||||
std_instead_of_core::STD_INSTEAD_OF_CORE,
|
||||
|
@ -537,10 +547,10 @@ store.register_lints(&[
|
|||
transmute::TRANSMUTE_PTR_TO_PTR,
|
||||
transmute::TRANSMUTE_PTR_TO_REF,
|
||||
transmute::TRANSMUTE_UNDEFINED_REPR,
|
||||
transmute::TRANSMUTING_NULL,
|
||||
transmute::UNSOUND_COLLECTION_TRANSMUTE,
|
||||
transmute::USELESS_TRANSMUTE,
|
||||
transmute::WRONG_TRANSMUTE,
|
||||
transmuting_null::TRANSMUTING_NULL,
|
||||
types::BORROWED_BOX,
|
||||
types::BOX_COLLECTION,
|
||||
types::LINKEDLIST,
|
||||
|
@ -555,7 +565,6 @@ store.register_lints(&[
|
|||
unicode::NON_ASCII_LITERAL,
|
||||
unicode::UNICODE_NOT_NFC,
|
||||
uninit_vec::UNINIT_VEC,
|
||||
unit_hash::UNIT_HASH,
|
||||
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
|
||||
unit_types::LET_UNIT_VALUE,
|
||||
unit_types::UNIT_ARG,
|
||||
|
@ -564,12 +573,12 @@ store.register_lints(&[
|
|||
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
|
||||
unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
unnecessary_wraps::UNNECESSARY_WRAPS,
|
||||
unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
unused_async::UNUSED_ASYNC,
|
||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
unused_peekable::UNUSED_PEEKABLE,
|
||||
unused_rounding::UNUSED_ROUNDING,
|
||||
unused_self::UNUSED_SELF,
|
||||
unused_unit::UNUSED_UNIT,
|
||||
|
@ -581,10 +590,9 @@ store.register_lints(&[
|
|||
useless_conversion::USELESS_CONVERSION,
|
||||
vec::USELESS_VEC,
|
||||
vec_init_then_push::VEC_INIT_THEN_PUSH,
|
||||
vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
verbose_file_reads::VERBOSE_FILE_READS,
|
||||
wildcard_imports::ENUM_GLOB_USE,
|
||||
wildcard_imports::WILDCARD_IMPORTS,
|
||||
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
|
||||
write::PRINTLN_EMPTY_STRING,
|
||||
write::PRINT_LITERAL,
|
||||
write::PRINT_STDERR,
|
||||
|
|
|
@ -14,21 +14,24 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
|
||||
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
|
||||
LintId::of(methods::ITER_ON_SINGLE_ITEMS),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(mutex_atomic::MUTEX_INTEGER),
|
||||
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
|
||||
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
|
||||
LintId::of(regex::TRIVIAL_REGEX),
|
||||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||
LintId::of(unused_rounding::UNUSED_ROUNDING),
|
||||
LintId::of(use_self::USE_SELF),
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(attrs::INLINE_ALWAYS),
|
||||
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
|
||||
LintId::of(bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(casts::BORROW_AS_PTR),
|
||||
LintId::of(casts::CAST_LOSSLESS),
|
||||
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
|
||||
LintId::of(casts::CAST_POSSIBLE_WRAP),
|
||||
|
@ -50,20 +48,24 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(macro_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(manual_assert::MANUAL_ASSERT),
|
||||
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
|
||||
LintId::of(manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(manual_string_new::MANUAL_STRING_NEW),
|
||||
LintId::of(matches::MATCH_BOOL),
|
||||
LintId::of(matches::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(matches::MATCH_SAME_ARMS),
|
||||
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(matches::MATCH_WILD_ERR_ARM),
|
||||
LintId::of(matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
|
||||
LintId::of(methods::FILTER_MAP_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_OPTION),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MANUAL_OK_OR),
|
||||
LintId::of(methods::MAP_UNWRAP_OR),
|
||||
LintId::of(methods::NAIVE_BYTECOUNT),
|
||||
LintId::of(methods::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
|
||||
|
@ -85,10 +87,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
|
||||
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(types::LINKEDLIST),
|
||||
LintId::of(types::OPTION_OPTION),
|
||||
|
|
|
@ -13,6 +13,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
|||
LintId::of(loops::MISSING_SPIN_LOOP),
|
||||
LintId::of(loops::NEEDLESS_COLLECT),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
|
||||
LintId::of(methods::EXPECT_FUN_CALL),
|
||||
LintId::of(methods::EXTEND_WITH_DRAIN),
|
||||
LintId::of(methods::ITER_NTH),
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(as_conversions::AS_CONVERSIONS),
|
||||
LintId::of(as_underscore::AS_UNDERSCORE),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
|
||||
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
|
||||
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
|
||||
LintId::of(casts::AS_UNDERSCORE),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
|
||||
LintId::of(create_dir::CREATE_DIR),
|
||||
LintId::of(dbg_macro::DBG_MACRO),
|
||||
|
@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(matches::TRY_ERR),
|
||||
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
|
@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(methods::EXPECT_USED),
|
||||
LintId::of(methods::FILETYPE_IS_FILE),
|
||||
LintId::of(methods::GET_UNWRAP),
|
||||
LintId::of(methods::MAP_ERR_IGNORE),
|
||||
LintId::of(methods::UNWRAP_USED),
|
||||
LintId::of(methods::VERBOSE_FILE_READS),
|
||||
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
|
||||
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
|
||||
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
|
||||
|
@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(unicode::NON_ASCII_LITERAL),
|
||||
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
|
||||
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(write::PRINT_STDERR),
|
||||
LintId::of(write::PRINT_STDOUT),
|
||||
LintId::of(write::USE_DEBUG),
|
||||
|
|
|
@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
LintId::of(functions::MUST_USE_UNIT),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
|
||||
LintId::of(len_zero::COMPARISON_TO_EMPTY),
|
||||
|
@ -45,7 +44,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
LintId::of(matches::COLLAPSIBLE_MATCH),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
|
@ -61,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
|
@ -68,7 +67,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(methods::ITER_NTH_ZERO),
|
||||
LintId::of(methods::ITER_SKIP_NEXT),
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
|
@ -88,7 +89,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
|
||||
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
|
||||
LintId::of(misc_early::REDUNDANT_PATTERN),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
|
||||
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
|
||||
|
|
|
@ -24,6 +24,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
|||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::SUSPICIOUS_TO_OWNED),
|
||||
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
|
@ -32,4 +34,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
|||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
|
||||
LintId::of(unused_peekable::UNUSED_PEEKABLE),
|
||||
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
|
||||
])
|
||||
|
|
|
@ -170,7 +170,6 @@ mod renamed_lints;
|
|||
mod almost_complete_letter_range;
|
||||
mod approx_const;
|
||||
mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assertions_on_result_states;
|
||||
|
@ -180,12 +179,8 @@ mod await_holding_invalid;
|
|||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod borrow_deref_ref;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod cargo;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod casts;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
|
@ -238,7 +233,6 @@ mod from_over_into;
|
|||
mod from_str_radix_10;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_first;
|
||||
mod if_let_mutex;
|
||||
mod if_not_else;
|
||||
mod if_then_some_else_none;
|
||||
|
@ -275,12 +269,10 @@ mod manual_async_fn;
|
|||
mod manual_bits;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_rem_euclid;
|
||||
mod manual_retain;
|
||||
mod manual_string_new;
|
||||
mod manual_strip;
|
||||
mod map_clone;
|
||||
mod map_err_ignore;
|
||||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
|
@ -297,9 +289,9 @@ mod missing_enforced_import_rename;
|
|||
mod missing_inline;
|
||||
mod mixed_read_write_in_expression;
|
||||
mod module_style;
|
||||
mod multi_assignments;
|
||||
mod mut_key;
|
||||
mod mut_mut;
|
||||
mod mut_mutex_lock;
|
||||
mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
|
@ -324,7 +316,6 @@ mod non_send_fields_in_send_ty;
|
|||
mod nonstandard_macro_braces;
|
||||
mod octal_escapes;
|
||||
mod only_used_in_recursion;
|
||||
mod open_options;
|
||||
mod operators;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
|
@ -334,7 +325,6 @@ mod panic_unimplemented;
|
|||
mod partialeq_ne_impl;
|
||||
mod partialeq_to_none;
|
||||
mod pass_by_ref_or_value;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
|
@ -354,7 +344,6 @@ mod redundant_static_lifetimes;
|
|||
mod ref_option_ref;
|
||||
mod reference;
|
||||
mod regex;
|
||||
mod repeat_once;
|
||||
mod return_self_not_must_use;
|
||||
mod returns;
|
||||
mod same_name_method;
|
||||
|
@ -366,7 +355,6 @@ mod single_char_lifetime_names;
|
|||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod std_instead_of_core;
|
||||
mod strings;
|
||||
mod strlen_on_c_strings;
|
||||
|
@ -380,23 +368,21 @@ mod to_digit_is_some;
|
|||
mod trailing_empty_array;
|
||||
mod trait_bounds;
|
||||
mod transmute;
|
||||
mod transmuting_null;
|
||||
mod types;
|
||||
mod undocumented_unsafe_blocks;
|
||||
mod unicode;
|
||||
mod uninit_vec;
|
||||
mod unit_hash;
|
||||
mod unit_return_expecting_ord;
|
||||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_wraps;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_async;
|
||||
mod unused_io_amount;
|
||||
mod unused_peekable;
|
||||
mod unused_rounding;
|
||||
mod unused_self;
|
||||
mod unused_unit;
|
||||
|
@ -407,8 +393,6 @@ mod use_self;
|
|||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_init_then_push;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
|
@ -596,7 +580,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
|
||||
store.register_late_pass(|| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
|
||||
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
|
||||
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
|
||||
store.register_late_pass(|| Box::new(strings::StringAdd));
|
||||
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
|
||||
|
@ -634,8 +617,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
|
||||
store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
|
||||
|
||||
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
|
||||
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
|
||||
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
||||
|
@ -645,7 +626,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
msrv,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
|
||||
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
|
||||
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(|| Box::new(loops::Loops));
|
||||
|
@ -653,7 +633,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(|| Box::new(entry::HashMapPass));
|
||||
store.register_late_pass(|| Box::new(minmax::MinMaxPass));
|
||||
store.register_late_pass(|| Box::new(open_options::OpenOptions));
|
||||
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
|
||||
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
|
||||
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
|
||||
|
@ -719,7 +698,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
);
|
||||
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
|
||||
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
|
||||
store.register_late_pass(|| Box::new(bytecount::ByteCount));
|
||||
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
|
||||
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
|
||||
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
|
||||
|
@ -737,12 +715,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
|
||||
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
|
||||
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
|
||||
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
|
||||
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
|
||||
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
||||
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
||||
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
||||
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
|
||||
|
@ -818,18 +793,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
|
||||
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
|
||||
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||
store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
|
||||
store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
|
||||
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
|
||||
store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
|
||||
store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
|
||||
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
|
||||
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
|
||||
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
|
||||
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
|
||||
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
|
||||
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
|
||||
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
|
||||
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || {
|
||||
|
@ -841,10 +813,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
|
||||
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
|
||||
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
|
||||
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
|
||||
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
|
||||
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
|
||||
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let disallowed_methods = conf.disallowed_methods.clone();
|
||||
|
@ -856,9 +825,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(strings::StringToString));
|
||||
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
|
||||
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
|
||||
store.register_late_pass(|| {
|
||||
Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
|
||||
});
|
||||
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
|
||||
|
@ -893,11 +859,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
|
||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
|
||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
|
||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
||||
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
||||
let cargo_ignore_publish = conf.cargo_ignore_publish;
|
||||
|
@ -911,18 +876,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
|
||||
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
|
||||
let max_include_file_size = conf.max_include_file_size;
|
||||
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
|
||||
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
|
||||
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
|
||||
store.register_late_pass(|| Box::new(get_first::GetFirst));
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
|
||||
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
|
||||
|
@ -933,6 +895,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
|
||||
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
|
||||
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
|
||||
store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
|
||||
store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
|
||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::NEEDLESS_COLLECT;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
|
@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
|
|||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
|
||||
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
|
||||
if check_loop_kind(expr).is_some() {
|
||||
continue;
|
||||
}
|
||||
self.visit_block_expr(expr, hir_id);
|
||||
}
|
||||
if let Some(expr) = block.expr {
|
||||
self.visit_block_expr(expr, None);
|
||||
if let Some(loop_kind) = check_loop_kind(expr) {
|
||||
if let LoopKind::Conditional(block_expr) = loop_kind {
|
||||
self.visit_block_expr(block_expr, None);
|
||||
}
|
||||
} else {
|
||||
self.visit_block_expr(expr, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
enum LoopKind<'tcx> {
|
||||
Conditional(&'tcx Expr<'tcx>),
|
||||
Loop,
|
||||
}
|
||||
|
||||
fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
|
||||
if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
|
||||
return Some(LoopKind::Conditional(let_expr));
|
||||
}
|
||||
if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
|
||||
return Some(LoopKind::Conditional(condition));
|
||||
}
|
||||
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
|
||||
return Some(LoopKind::Conditional(arg));
|
||||
}
|
||||
if let ExprKind::Loop { .. } = expr.kind {
|
||||
return Some(LoopKind::Loop);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
|
||||
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
|
||||
self.current_statement_hir_id = hir_id;
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_lint::LateContext;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, stmts @ ..], expr) => {
|
||||
if let StmtKind::Local(&Local { init: Some(e), els: None, .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
|
||||
if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
|
||||
(e, !stmts.is_empty() || expr.is_some())
|
||||
} else {
|
||||
return;
|
||||
|
|
|
@ -35,7 +35,8 @@ struct PathAndSpan {
|
|||
span: Span,
|
||||
}
|
||||
|
||||
/// `MacroRefData` includes the name of the macro.
|
||||
/// `MacroRefData` includes the name of the macro
|
||||
/// and the path from `SourceMap::span_to_filename`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MacroRefData {
|
||||
name: String,
|
||||
|
|
|
@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
|
|||
match output.kind {
|
||||
TyKind::Tup(tys) if tys.is_empty() => {
|
||||
let sugg = "remove the return type";
|
||||
Some((sugg, "".into()))
|
||||
Some((sugg, String::new()))
|
||||
},
|
||||
_ => {
|
||||
let sugg = "return the output of the future directly";
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Finds patterns that reimplement `Option::ok_or`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Concise code helps focusing on behavior instead of boilerplate.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.map_or(Err("error"), |v| Ok(v));
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let foo: Option<i32> = None;
|
||||
/// foo.ok_or("error");
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_OK_OR,
|
||||
pedantic,
|
||||
"finds patterns that can be encoded more concisely with `Option::ok_or`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), scrutinee.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
|
||||
if method_segment.ident.name == sym!(map_or);
|
||||
let ty = cx.typeck_results().expr_ty(receiver);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, scrutinee.span);
|
||||
then {
|
||||
let reindented_err_arg_snippet =
|
||||
reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
scrutinee.span,
|
||||
"this pattern reimplements `Option::ok_or`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.ok_or({})",
|
||||
method_receiver_snippet,
|
||||
reindented_err_arg_snippet
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
140
clippy_lints/src/manual_string_new.rs
Normal file
140
clippy_lints/src/manual_string_new.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability::MachineApplicable;
|
||||
use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, symbol, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
|
||||
/// `String::from("")` and others.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Different ways of creating an empty string makes your code less standardized, which can
|
||||
/// be confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let a = "".to_string();
|
||||
/// let b: String = "".into();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = String::new();
|
||||
/// let b = String::new();
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub MANUAL_STRING_NEW,
|
||||
pedantic,
|
||||
"empty String is being created manually"
|
||||
}
|
||||
declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
|
||||
|
||||
impl LateLintPass<'_> for ManualStringNew {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, _) if adt_def.is_struct() => {
|
||||
if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, args) => {
|
||||
parse_call(cx, expr.span, func, args);
|
||||
},
|
||||
ExprKind::MethodCall(path_segment, args, _) => {
|
||||
parse_method_call(cx, expr.span, path_segment, args);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an expression's kind corresponds to an empty &str.
|
||||
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
|
||||
if let ExprKind::Lit(lit) = expr_kind &&
|
||||
let LitKind::Str(value, _) = lit.node &&
|
||||
value == symbol::kw::Empty
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_STRING_NEW,
|
||||
span,
|
||||
"empty String is being created manually",
|
||||
"consider using",
|
||||
"String::new()".into(),
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tries to parse an expression as a method call, emitting the warning if necessary.
|
||||
fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
|
||||
if args.is_empty() {
|
||||
// When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
|
||||
return;
|
||||
}
|
||||
|
||||
let ident = path_segment.ident.as_str();
|
||||
let method_arg_kind = &args[0].kind;
|
||||
if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
|
||||
warn_then_suggest(cx, span);
|
||||
} else if let ExprKind::Call(func, args) = method_arg_kind {
|
||||
// If our first argument is a function call itself, it could be an `unwrap`-like function.
|
||||
// E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
|
||||
parse_call(cx, span, func, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse an expression as a function call, emitting the warning if necessary.
|
||||
fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if args.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let arg_kind = &args[0].kind;
|
||||
if let ExprKind::Path(qpath) = &func.kind {
|
||||
if let QPath::TypeRelative(_, _) = qpath {
|
||||
// String::from(...) or String::try_from(...)
|
||||
if let QPath::TypeRelative(ty, path_seg) = qpath &&
|
||||
[sym::from, sym::try_from].contains(&path_seg.ident.name) &&
|
||||
let TyKind::Path(qpath) = &ty.kind &&
|
||||
let QPath::Resolved(_, path) = qpath &&
|
||||
let [path_seg] = path.segments &&
|
||||
path_seg.ident.name == sym::String &&
|
||||
is_expr_kind_empty_str(arg_kind)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
} else if let QPath::Resolved(_, path) = qpath {
|
||||
// From::from(...) or TryFrom::try_from(...)
|
||||
if let [path_seg1, path_seg2] = path.segments &&
|
||||
is_expr_kind_empty_str(arg_kind) && (
|
||||
(path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
|
||||
(path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
|
||||
)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `map(|x| x.clone())` or
|
||||
/// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
|
||||
/// and suggests `cloned()` or `copied()` instead
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.map(|i| *i);
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.cloned();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MAP_CLONE,
|
||||
style,
|
||||
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
|
||||
}
|
||||
|
||||
pub struct MapClone {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(MapClone => [MAP_CLONE]);
|
||||
|
||||
impl MapClone {
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MapClone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
|
||||
if args.len() == 2;
|
||||
if method.ident.name == sym::map;
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::Unannotated, .., name, None
|
||||
) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, true);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone;
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
|
||||
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
|
||||
// no autoderefs
|
||||
if !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
|
||||
then {
|
||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||
if matches!(mutability, Mutability::Not) {
|
||||
let copy = is_copy(cx, *ty);
|
||||
self.lint_explicit_closure(cx, e.span, args[0].span, copy);
|
||||
}
|
||||
} else {
|
||||
lint_needless_cloning(cx, e.span, args[0].span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||
path.segments.len() == 1 && path.segments[0].ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
root.trim_start(receiver).unwrap(),
|
||||
"you are needlessly cloning iterator elements",
|
||||
"remove the `map` call",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
impl MapClone {
|
||||
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
message,
|
||||
&format!("consider calling the dedicated `{}` method", sugg_method),
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
sugg_method,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for instances of `map_err(|_| Some::Enum)`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
|
||||
///
|
||||
/// ### Example
|
||||
/// Before:
|
||||
/// ```rust
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible,
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {}
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(|_| Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// After:
|
||||
/// ```rust
|
||||
/// use std::{fmt, num::ParseIntError};
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum Error {
|
||||
/// Indivisible(ParseIntError),
|
||||
/// Remainder(u8),
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for Error {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// match self {
|
||||
/// Error::Indivisible(_) => write!(f, "could not divide input by three"),
|
||||
/// Error::Remainder(remainder) => write!(
|
||||
/// f,
|
||||
/// "input is not divisible by three, remainder = {}",
|
||||
/// remainder
|
||||
/// ),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::error::Error for Error {
|
||||
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
/// match self {
|
||||
/// Error::Indivisible(source) => Some(source),
|
||||
/// _ => None,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
|
||||
/// input
|
||||
/// .parse::<i32>()
|
||||
/// .map_err(Error::Indivisible)
|
||||
/// .map(|v| v % 3)
|
||||
/// .and_then(|remainder| {
|
||||
/// if remainder == 0 {
|
||||
/// Ok(())
|
||||
/// } else {
|
||||
/// Err(Error::Remainder(remainder as u8))
|
||||
/// }
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub MAP_ERR_IGNORE,
|
||||
restriction,
|
||||
"`map_err` should not ignore the original error"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
||||
// do not try to lint if this is from a macro or desugaring
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this is a method call (e.g. x.foo())
|
||||
if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
|
||||
// only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
|
||||
// Enum::Variant[2]))
|
||||
if method.ident.name == sym!(map_err) {
|
||||
// make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
|
||||
// fields
|
||||
if let ExprKind::Closure(&Closure {
|
||||
capture_clause,
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = arg.kind
|
||||
{
|
||||
// check if this is by Reference (meaning there's no move statement)
|
||||
if capture_clause == CaptureBy::Ref {
|
||||
// Get the closure body to check the parameters and values
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
// make sure there's only one parameter (`|_|`)
|
||||
if closure_body.params.len() == 1 {
|
||||
// make sure that parameter is the wild token (`_`)
|
||||
if let PatKind::Wild = closure_body.params[0].pat.kind {
|
||||
// span the area of the closure capture and warn that the
|
||||
// original error will be thrown away
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
fn_decl_span,
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_wild;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::span_contains_comment;
|
||||
use rustc_ast::{Attribute, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
|
@ -76,6 +77,7 @@ where
|
|||
>,
|
||||
{
|
||||
if_chain! {
|
||||
if !span_contains_comment(cx.sess().source_map(), expr.span);
|
||||
if iter.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
|
||||
|
|
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
|||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
|
|||
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
|
||||
for arm in arms {
|
||||
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||
|
||||
if let Some(guard_expr) = &arm.guard {
|
||||
match guard_expr {
|
||||
// gives up if `pat if expr` can have side effects
|
||||
Guard::If(if_cond) => {
|
||||
if if_cond.can_have_side_effects() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// gives up `pat if let ...` arm
|
||||
Guard::IfLet(_) => {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if let PatKind::Wild = arm.pat.kind {
|
||||
return eq_expr_value(cx, match_expr, strip_return(arm_expr));
|
||||
if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
|
||||
return false;
|
||||
}
|
||||
} else if !pat_same_as_expr(arm.pat, arm_expr) {
|
||||
return false;
|
||||
}
|
||||
|
|
70
clippy_lints/src/methods/bytecount.rs
Normal file
70
clippy_lints/src/methods/bytecount.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::NAIVE_BYTECOUNT;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
filter_recv: &'tcx Expr<'_>,
|
||||
filter_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, args, _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
filter_recv
|
||||
}
|
||||
} else {
|
||||
filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::BYTES_COUNT_TO_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
count_recv: &'tcx hir::Expr<'_>,
|
||||
bytes_recv: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
|
||||
if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{source_map::Spanned, symbol::sym, Span};
|
||||
|
||||
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
call_span: Span,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
let ext_str = ext_literal.as_str();
|
||||
if ext_str.starts_with('.');
|
||||
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
call_span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
96
clippy_lints/src/methods/collapsible_str_replace.rs
Normal file
96
clippy_lints/src/methods/collapsible_str_replace.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{eq_expr_value, get_parent_expr};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::method_call;
|
||||
use super::COLLAPSIBLE_STR_REPLACE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
from: &'tcx hir::Expr<'tcx>,
|
||||
to: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let replace_methods = collect_replace_calls(cx, expr, to);
|
||||
if replace_methods.methods.len() > 1 {
|
||||
let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
|
||||
// If the parent node's `to` argument is the same as the `to` argument
|
||||
// of the last replace call in the current chain, don't lint as it was already linted
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
|
||||
&& eq_expr_value(cx, to, current_to)
|
||||
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
check_consecutive_replace_calls(cx, expr, &replace_methods, to);
|
||||
}
|
||||
}
|
||||
|
||||
struct ReplaceMethods<'tcx> {
|
||||
methods: VecDeque<&'tcx hir::Expr<'tcx>>,
|
||||
from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
|
||||
}
|
||||
|
||||
fn collect_replace_calls<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
to_arg: &'tcx hir::Expr<'tcx>,
|
||||
) -> ReplaceMethods<'tcx> {
|
||||
let mut methods = VecDeque::new();
|
||||
let mut from_args = VecDeque::new();
|
||||
|
||||
let _: Option<()> = for_each_expr(expr, |e| {
|
||||
if let Some(("replace", [_, from, to], _)) = method_call(e) {
|
||||
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||
methods.push_front(e);
|
||||
from_args.push_front(from);
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::BREAK
|
||||
}
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
});
|
||||
|
||||
ReplaceMethods { methods, from_args }
|
||||
}
|
||||
|
||||
/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
|
||||
fn check_consecutive_replace_calls<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
replace_methods: &ReplaceMethods<'tcx>,
|
||||
to_arg: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let from_args = &replace_methods.from_args;
|
||||
let from_arg_reprs: Vec<String> = from_args
|
||||
.iter()
|
||||
.map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
|
||||
.collect();
|
||||
let app = Applicability::MachineApplicable;
|
||||
let earliest_replace_call = replace_methods.methods.front().unwrap();
|
||||
if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_STR_REPLACE,
|
||||
expr.span.with_lo(span_lo.lo()),
|
||||
"used consecutive `str::replace` call",
|
||||
"replace with",
|
||||
format!(
|
||||
"replace([{}], {})",
|
||||
from_arg_reprs.join(", "),
|
||||
snippet(cx, to_arg.span, ".."),
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,18 +7,26 @@ use rustc_span::sym;
|
|||
|
||||
use super::EXPECT_USED;
|
||||
|
||||
/// lint use of `expect()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
|
||||
/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_expect_in_tests: bool,
|
||||
) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
|
||||
Some((EXPECT_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((EXPECT_USED, "a Result", "Err", "an "))
|
||||
Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let method = if is_err { "expect_err" } else { "expect" };
|
||||
|
||||
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `expect()` on `{kind}` value"),
|
||||
&format!("used `{method}()` on `{kind}` value"),
|
||||
None,
|
||||
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
|
||||
);
|
||||
|
|
39
clippy_lints/src/methods/get_first.rs
Normal file
39
clippy_lints/src/methods/get_first.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::GET_FIRST;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let Some(_) = is_slice_of_primitives(cx, recv);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
107
clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
Normal file
107
clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
|
||||
|
||||
enum IterType {
|
||||
Iter,
|
||||
IterMut,
|
||||
IntoIter,
|
||||
}
|
||||
|
||||
impl IterType {
|
||||
fn ref_prefix(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Iter => "&",
|
||||
Self::IterMut => "&mut ",
|
||||
Self::IntoIter => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
|
||||
let item = match &recv.kind {
|
||||
ExprKind::Array(v) if v.len() <= 1 => v.first(),
|
||||
ExprKind::Path(p) => {
|
||||
if is_lang_ctor(cx, p, OptionNone) {
|
||||
None
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Call(f, some_args) if some_args.len() == 1 => {
|
||||
if let ExprKind::Path(p) = &f.kind {
|
||||
if is_lang_ctor(cx, p, OptionSome) {
|
||||
Some(&some_args[0])
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
let iter_type = match method_name {
|
||||
"iter" => IterType::Iter,
|
||||
"iter_mut" => IterType::IterMut,
|
||||
"into_iter" => IterType::IntoIter,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
|
||||
Some((Node::Expr(parent), child_id)) => match parent.kind {
|
||||
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
|
||||
ExprKind::If(_, _, _)
|
||||
| ExprKind::Match(_, _, _)
|
||||
| ExprKind::Closure(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Break(_, _) => true,
|
||||
_ => false,
|
||||
},
|
||||
Some((Node::Stmt(_) | Node::Local(_), _)) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if is_unified {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(i) = item {
|
||||
let sugg = format!(
|
||||
"{}::iter::once({}{})",
|
||||
if is_no_std_crate(cx) { "core" } else { "std" },
|
||||
iter_type.ref_prefix(),
|
||||
snippet(cx, i.span, "...")
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_ON_SINGLE_ITEMS,
|
||||
expr.span,
|
||||
&format!("`{method_name}` call on a collection with only one item"),
|
||||
"try",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_ON_EMPTY_COLLECTIONS,
|
||||
expr.span,
|
||||
&format!("`{method_name}` call on an empty collection"),
|
||||
"try",
|
||||
if is_no_std_crate(cx) {
|
||||
"core::iter::empty()".to_string()
|
||||
} else {
|
||||
"std::iter::empty()".to_string()
|
||||
},
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::MANUAL_OK_OR;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
or_expr: &'tcx Expr<'_>,
|
||||
map_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
then {
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
expr.span,
|
||||
"this pattern reimplements `Option::ok_or`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.ok_or({})",
|
||||
recv_snippet,
|
||||
reindented_err_arg_snippet
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
122
clippy_lints/src/methods/map_clone.rs
Normal file
122
clippy_lints/src/methods/map_clone.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MAP_CLONE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
e: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||
if cx.tcx.impl_of_method(method_id)
|
||||
.map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
|
||||
|| is_diag_trait_item(cx, method_id, sym::Iterator);
|
||||
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::Unannotated, .., name, None
|
||||
) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone;
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
|
||||
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
|
||||
// no autoderefs
|
||||
if !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
|
||||
then {
|
||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||
if matches!(mutability, Mutability::Not) {
|
||||
let copy = is_copy(cx, *ty);
|
||||
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
|
||||
}
|
||||
} else {
|
||||
lint_needless_cloning(cx, e.span, recv.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||
path.segments.len() == 1 && path.segments[0].ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
root.trim_start(receiver).unwrap(),
|
||||
"you are needlessly cloning iterator elements",
|
||||
"remove the `map` call",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
message,
|
||||
&format!("consider calling the dedicated `{}` method", sugg_method),
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
sugg_method,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MAP_ERR_IGNORE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
|
||||
&& let ExprKind::Closure(&Closure {
|
||||
capture_clause: CaptureBy::Ref,
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = arg.kind
|
||||
&& let closure_body = cx.tcx.hir().body(body)
|
||||
&& let [param] = closure_body.params
|
||||
&& let PatKind::Wild = param.pat.kind
|
||||
{
|
||||
// span the area of the closure capture and warn that the
|
||||
// original error will be thrown away
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
fn_decl_span,
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MUT_MUTEX_LOCK;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MUT_MUTEX_LOCK,
|
||||
name_span,
|
||||
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
|
||||
"change this to",
|
||||
"get_mut".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,43 +3,19 @@ use clippy_utils::paths;
|
|||
use clippy_utils::ty::match_type;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for duplicate open options as well as combinations
|
||||
/// that make no sense.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In the best case, the code will be harder to read than
|
||||
/// necessary. I don't know the worst case.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::fs::OpenOptions;
|
||||
///
|
||||
/// OpenOptions::new().read(true).truncate(true);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NONSENSICAL_OPEN_OPTIONS,
|
||||
correctness,
|
||||
"nonsensical combination of options for opening a file"
|
||||
}
|
||||
use super::NONSENSICAL_OPEN_OPTIONS;
|
||||
|
||||
declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for OpenOptions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &e.kind {
|
||||
let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, self_arg, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
|
||||
{
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, recv, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
|
|||
map_span,
|
||||
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none {
|
||||
|
|
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
use super::PATH_BUF_PUSH_OVERWRITE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
if pushed_path.has_root();
|
||||
if let Some(root) = pushed_path.components().next();
|
||||
if root == Component::RootDir;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use clippy_utils::{is_integer_const, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::RANGE_ZIP_WITH_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, recv.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
52
clippy_lints/src/methods/repeat_once.rs
Normal file
52
clippy_lints/src/methods/repeat_once.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::REPEAT_ONCE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
repeat_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::String) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::STABLE_SORT_PRIMITIVE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& cx.tcx.type_of(impl_id).is_slice()
|
||||
&& let Some(slice_type) = is_slice_of_primitives(cx, recv)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
e.span,
|
||||
&format!("used `sort` on primitive type `{}`", slice_type),
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
|
||||
diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
|
||||
diag.note(
|
||||
"an unstable sort typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
36
clippy_lints/src/methods/suspicious_to_owned.rs
Normal file
36
clippy_lints/src/methods/suspicious_to_owned.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::SUSPICIOUS_TO_OWNED;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
|
||||
let input_type = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
|
||||
then {
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUSPICIOUS_TO_OWNED,
|
||||
expr.span,
|
||||
&format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
|
||||
"consider using, depending on intent",
|
||||
format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
|
||||
app,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
|
||||
use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
if_chain! {
|
||||
if let hir::ExprKind::Call(callee, args) = recv.kind;
|
||||
if args.is_empty();
|
||||
if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
|
||||
if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
|
||||
if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
|
||||
then {
|
||||
span_lint(
|
||||
|
|
29
clippy_lints/src/methods/unit_hash.rs
Normal file
29
clippy_lints/src/methods/unit_hash.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNIT_HASH;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNIT_HASH,
|
||||
expr.span,
|
||||
"this call to `hash` on the unit type will do nothing",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"remove the call to `hash` or consider using",
|
||||
format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("the implementation of `Hash` for `()` is a no-op");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,51 +1,17 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects uses of `Vec::sort_by` passing in a closure
|
||||
/// which compares the two arguments, either directly or indirectly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
|
||||
/// possible) than to use `Vec::sort_by` and a more complicated
|
||||
/// closure.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
|
||||
/// imported by a use statement, then it will need to be added manually.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct A;
|
||||
/// # impl A { fn foo(&self) {} }
|
||||
/// # let mut vec: Vec<A> = Vec::new();
|
||||
/// vec.sort_by_key(|a| a.foo());
|
||||
/// ```
|
||||
#[clippy::version = "1.46.0"]
|
||||
pub UNNECESSARY_SORT_BY,
|
||||
complexity,
|
||||
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
|
||||
use super::UNNECESSARY_SORT_BY;
|
||||
|
||||
enum LintTrigger {
|
||||
Sort(SortDetection),
|
||||
|
@ -54,7 +20,6 @@ enum LintTrigger {
|
|||
|
||||
struct SortDetection {
|
||||
vec_name: String,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
struct SortByKeyDetection {
|
||||
|
@ -62,7 +27,6 @@ struct SortByKeyDetection {
|
|||
closure_arg: String,
|
||||
closure_body: String,
|
||||
reverse: bool,
|
||||
unstable: bool,
|
||||
}
|
||||
|
||||
/// Detect if the two expressions are mirrored (identical, except one
|
||||
|
@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
|
|||
}
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind;
|
||||
if let name = name_ident.ident.name.to_ident_string();
|
||||
if name == "sort_by" || name == "sort_unstable_by";
|
||||
if let [vec, Expr { kind: ExprKind::Closure(Closure { body: closure_body_id, .. }), .. }] = args;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
|
||||
if let closure_body = cx.tcx.hir().body(*closure_body_id);
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
|
||||
if let closure_body = cx.tcx.hir().body(body);
|
||||
if let &[
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
|
||||
] = &closure_body.params;
|
||||
if let ExprKind::MethodCall(method_path, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
|
||||
if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
|
||||
if method_path.ident.name == sym::cmp;
|
||||
if is_trait_method(cx, &closure_body.value, sym::Ord);
|
||||
then {
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
|
||||
left_expr,
|
||||
|
@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
|||
} else {
|
||||
return None;
|
||||
};
|
||||
let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
|
||||
let unstable = name == "sort_unstable_by";
|
||||
let vec_name = Sugg::hir(cx, recv, "..").to_string();
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
});
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
});
|
||||
then {
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
|
|||
closure_arg,
|
||||
closure_body,
|
||||
reverse,
|
||||
unstable,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for UnnecessarySortBy {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
match detect_lint(cx, expr) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
format!("std::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
is_unstable: bool,
|
||||
) {
|
||||
match detect_lint(cx, expr, recv, arg) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
format!("std::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if trigger.unstable { "_unstable" } else { "" },
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
}
|
|
@ -3,11 +3,10 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{
|
||||
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
|
||||
get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -279,7 +278,19 @@ fn check_other_call_arg<'tcx>(
|
|||
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
|
||||
call_substs,
|
||||
);
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
// if `expr` is a `String` and generic target is [u8], skip
|
||||
// (https://github.com/rust-lang/rust-clippy/issues/9317).
|
||||
if let [subst] = composed_substs[..]
|
||||
&& let GenericArgKind::Type(arg_ty) = subst.unpack()
|
||||
&& arg_ty.is_slice()
|
||||
&& let inner_ty = arg_ty.builtin_index().unwrap()
|
||||
&& let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
|
||||
&& let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
|
||||
&& is_type_diagnostic_item(cx, self_ty, sym::String) {
|
||||
false
|
||||
} else {
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
@ -292,7 +303,7 @@ fn check_other_call_arg<'tcx>(
|
|||
// (https://github.com/rust-lang/rust-clippy/issues/8507).
|
||||
if (n_refs == 0 && !receiver_ty.is_ref())
|
||||
|| trait_predicate.def_id() != as_ref_trait_id
|
||||
|| !contains_ty(fn_sig.output(), input);
|
||||
|| !fn_sig.output().contains(input);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
|
@ -360,25 +371,15 @@ fn get_input_traits_and_projections<'tcx>(
|
|||
) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
|
||||
let mut trait_predicates = Vec::new();
|
||||
let mut projection_predicates = Vec::new();
|
||||
for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
|
||||
// `substs` should have 1 + n elements. The first is the type on the left hand side of an
|
||||
// `as`. The remaining n are trait parameters.
|
||||
let is_input_substs = |substs: SubstsRef<'tcx>| {
|
||||
if_chain! {
|
||||
if let Some(arg) = substs.iter().next();
|
||||
if let GenericArgKind::Type(arg_ty) = arg.unpack();
|
||||
if arg_ty == input;
|
||||
then { true } else { false }
|
||||
}
|
||||
};
|
||||
for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
|
||||
match predicate.kind().skip_binder() {
|
||||
PredicateKind::Trait(trait_predicate) => {
|
||||
if is_input_substs(trait_predicate.trait_ref.substs) {
|
||||
if trait_predicate.trait_ref.self_ty() == input {
|
||||
trait_predicates.push(trait_predicate);
|
||||
}
|
||||
},
|
||||
PredicateKind::Projection(projection_predicate) => {
|
||||
if is_input_substs(projection_predicate.projection_ty.substs) {
|
||||
if projection_predicate.projection_ty.self_ty() == input {
|
||||
projection_predicates.push(projection_predicate);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,18 +7,26 @@ use rustc_span::sym;
|
|||
|
||||
use super::{EXPECT_USED, UNWRAP_USED};
|
||||
|
||||
/// lint use of `unwrap()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
|
||||
/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
|
||||
Some((UNWRAP_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((UNWRAP_USED, "a Result", "Err", "an "))
|
||||
Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let method_suffix = if is_err { "_err" } else { "" };
|
||||
|
||||
if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
@ -27,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
|
||||
format!(
|
||||
"if you don't want to handle the `{none_value}` case gracefully, consider \
|
||||
using `expect()` to provide a better panic message"
|
||||
using `expect{method_suffix}()` to provide a better panic message"
|
||||
)
|
||||
} else {
|
||||
format!("if this value is {none_prefix}`{none_value}`, it will panic")
|
||||
|
@ -37,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `unwrap()` on `{kind}` value"),
|
||||
&format!("used `unwrap{method_suffix}()` on `{kind}` value"),
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
|
|
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::VEC_RESIZE_TO_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
count_arg: &'tcx Expr<'_>,
|
||||
default_arg: &'tcx Expr<'_>,
|
||||
name_span: Span,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
|
||||
then {
|
||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
expr.span,
|
||||
"emptying a vector with `resize`",
|
||||
|db| {
|
||||
db.help("the arguments may be inverted...");
|
||||
db.span_suggestion(
|
||||
method_call_span,
|
||||
"...or you can empty the vector with",
|
||||
"clear()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::VERBOSE_FILE_READS;
|
||||
|
||||
pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
|
||||
pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
|
||||
"use of `File::read_to_string`",
|
||||
"consider using `fs::read_to_string` instead",
|
||||
);
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
(msg, help): (&str, &str),
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::IoRead)
|
||||
&& matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
|
||||
{
|
||||
span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
|
|||
"these patterns are unneeded as the `..` pattern can match those elements"
|
||||
},
|
||||
if only_one { "remove it" } else { "remove them" },
|
||||
"".to_string(),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// impl<A, B> Foo<A, B> {}
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub MISMATCHING_TYPE_PARAM_ORDER,
|
||||
pedantic,
|
||||
"type parameter positioned inconsistently between type def and impl block"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
65
clippy_lints/src/multi_assignments.rs
Normal file
65
clippy_lints/src/multi_assignments.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for nested assignments.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While this is in most cases already a type mismatch,
|
||||
/// the result of an assignment being `()` can throw off people coming from languages like python or C,
|
||||
/// where such assignments return a copy of the assigned value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
///# let (a, b);
|
||||
/// a = b = 42;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
///# let (a, b);
|
||||
/// b = 42;
|
||||
/// a = b;
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub MULTI_ASSIGNMENTS,
|
||||
suspicious,
|
||||
"instead of using `a = b = c;` use `a = c; b = c;`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
|
||||
|
||||
fn strip_paren_blocks(expr: &Expr) -> &Expr {
|
||||
match &expr.kind {
|
||||
ExprKind::Paren(e) => strip_paren_blocks(e),
|
||||
ExprKind::Block(b, _) => {
|
||||
if let [
|
||||
Stmt {
|
||||
kind: StmtKind::Expr(e),
|
||||
..
|
||||
},
|
||||
] = &b.stmts[..]
|
||||
{
|
||||
strip_paren_blocks(e)
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
},
|
||||
_ => expr,
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for MultiAssignments {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Assign(target, source, _) = &expr.kind {
|
||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
|
||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||
};
|
||||
if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
|
||||
span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `&mut Mutex::lock` calls
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Mutex::lock` is less efficient than
|
||||
/// calling `Mutex::get_mut`. In addition you also have a statically
|
||||
/// guarantee that the mutex isn't locked, instead of just a runtime
|
||||
/// guarantee.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let mut value = value_mutex.lock().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
///
|
||||
/// let mut value_rc = Arc::new(Mutex::new(42_u8));
|
||||
/// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
|
||||
///
|
||||
/// let value = value_mutex.get_mut().unwrap();
|
||||
/// *value += 1;
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MUT_MUTEX_LOCK,
|
||||
style,
|
||||
"`&mut Mutex::lock` does unnecessary locking"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &ex.kind;
|
||||
if path.ident.name == sym!(lock);
|
||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
|
||||
if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MUT_MUTEX_LOCK,
|
||||
path.ident.span,
|
||||
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
|
||||
"change this to",
|
||||
"get_mut".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -95,10 +95,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -1,25 +1,16 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use itertools::{izip, Itertools};
|
||||
use rustc_ast::{walk_list, Label, Mutability};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
|
||||
use core::cell::Cell;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
|
||||
use rustc_hir::{
|
||||
Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
|
||||
PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_hir::hir_id::HirIdMap;
|
||||
use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::{self, ConstKind};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -89,572 +80,323 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ONLY_USED_IN_RECURSION,
|
||||
nursery,
|
||||
complexity,
|
||||
"arguments that is only used in recursion can be removed"
|
||||
}
|
||||
declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
|
||||
impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum FnKind {
|
||||
Fn,
|
||||
TraitFn,
|
||||
// This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
|
||||
// Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
|
||||
// equality.
|
||||
ImplTraitFn(usize),
|
||||
}
|
||||
|
||||
struct Param {
|
||||
/// The function this is a parameter for.
|
||||
fn_id: DefId,
|
||||
fn_kind: FnKind,
|
||||
/// The index of this parameter.
|
||||
idx: usize,
|
||||
ident: Ident,
|
||||
/// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
|
||||
apply_lint: Cell<bool>,
|
||||
/// All the uses of this parameter.
|
||||
uses: Vec<Usage>,
|
||||
}
|
||||
impl Param {
|
||||
fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
|
||||
Self {
|
||||
fn_id,
|
||||
fn_kind,
|
||||
idx,
|
||||
ident,
|
||||
apply_lint: Cell::new(true),
|
||||
uses: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Usage {
|
||||
span: Span,
|
||||
idx: usize,
|
||||
}
|
||||
impl Usage {
|
||||
fn new(span: Span, idx: usize) -> Self {
|
||||
Self { span, idx }
|
||||
}
|
||||
}
|
||||
|
||||
/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
|
||||
/// `DefId` of the function paired with the parameter's index.
|
||||
#[derive(Default)]
|
||||
struct Params {
|
||||
params: Vec<Param>,
|
||||
by_id: HirIdMap<usize>,
|
||||
by_fn: FxHashMap<(DefId, usize), usize>,
|
||||
}
|
||||
impl Params {
|
||||
fn insert(&mut self, param: Param, id: HirId) {
|
||||
let idx = self.params.len();
|
||||
self.by_id.insert(id, idx);
|
||||
self.by_fn.insert((param.fn_id, param.idx), idx);
|
||||
self.params.push(param);
|
||||
}
|
||||
|
||||
fn remove_by_id(&mut self, id: HirId) {
|
||||
if let Some(param) = self.get_by_id_mut(id) {
|
||||
param.uses = Vec::new();
|
||||
let key = (param.fn_id, param.idx);
|
||||
self.by_fn.remove(&key);
|
||||
self.by_id.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
|
||||
self.params.get_mut(*self.by_id.get(&id)?)
|
||||
}
|
||||
|
||||
fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
|
||||
self.params.get(*self.by_fn.get(&(id, idx))?)
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.params.clear();
|
||||
self.by_id.clear();
|
||||
self.by_fn.clear();
|
||||
}
|
||||
|
||||
/// Sets the `apply_lint` flag on each parameter.
|
||||
fn flag_for_linting(&mut self) {
|
||||
// Stores the list of parameters currently being resolved. Needed to avoid cycles.
|
||||
let mut eval_stack = Vec::new();
|
||||
for param in &self.params {
|
||||
self.try_disable_lint_for_param(param, &mut eval_stack);
|
||||
}
|
||||
}
|
||||
|
||||
// Use by calling `flag_for_linting`.
|
||||
fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
|
||||
if !param.apply_lint.get() {
|
||||
true
|
||||
} else if param.uses.is_empty() {
|
||||
// Don't lint on unused parameters.
|
||||
param.apply_lint.set(false);
|
||||
true
|
||||
} else if eval_stack.contains(¶m.idx) {
|
||||
// Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
|
||||
false
|
||||
} else {
|
||||
eval_stack.push(param.idx);
|
||||
// Check all cases when used at a different parameter index.
|
||||
// Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
|
||||
for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
|
||||
if self
|
||||
.get_by_fn(param.fn_id, usage.idx)
|
||||
// If the parameter can't be found, then it's used for more than just recursion.
|
||||
.map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
|
||||
{
|
||||
param.apply_lint.set(false);
|
||||
eval_stack.pop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
eval_stack.pop();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OnlyUsedInRecursion {
|
||||
/// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
|
||||
entered_body: Option<HirId>,
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
decl: &'tcx rustc_hir::FnDecl<'tcx>,
|
||||
body: &'tcx Body<'tcx>,
|
||||
_: Span,
|
||||
id: HirId,
|
||||
) {
|
||||
if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
|
||||
if body.value.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
|
||||
let def_id = id.owner.to_def_id();
|
||||
let data = cx.tcx.def_path(def_id).data;
|
||||
|
||||
if data.len() > 1 {
|
||||
match data.get(data.len() - 2) {
|
||||
Some(DisambiguatedDefPathData {
|
||||
data: DefPathData::Impl,
|
||||
disambiguator,
|
||||
}) if *disambiguator != 0 => return,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
|
||||
|
||||
let ty_res = cx.typeck_results();
|
||||
let param_span = body
|
||||
.params
|
||||
.iter()
|
||||
.flat_map(|param| {
|
||||
let mut v = Vec::new();
|
||||
param.pat.each_binding(|_, hir_id, span, ident| {
|
||||
v.push((hir_id, span, ident));
|
||||
});
|
||||
v
|
||||
})
|
||||
.skip(if has_self { 1 } else { 0 })
|
||||
.filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
|
||||
.collect_vec();
|
||||
|
||||
let params = body.params.iter().map(|param| param.pat).collect();
|
||||
|
||||
let mut visitor = SideEffectVisit {
|
||||
graph: FxHashMap::default(),
|
||||
has_side_effect: FxHashSet::default(),
|
||||
ret_vars: Vec::new(),
|
||||
contains_side_effect: false,
|
||||
break_vars: FxHashMap::default(),
|
||||
params,
|
||||
fn_ident: ident,
|
||||
fn_def_id: def_id,
|
||||
is_method: matches!(kind, FnKind::Method(..)),
|
||||
has_self,
|
||||
ty_res,
|
||||
tcx: cx.tcx,
|
||||
visited_exprs: FxHashSet::default(),
|
||||
};
|
||||
|
||||
visitor.visit_expr(&body.value);
|
||||
let vars = std::mem::take(&mut visitor.ret_vars);
|
||||
// this would set the return variables to side effect
|
||||
visitor.add_side_effect(vars);
|
||||
|
||||
let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
|
||||
|
||||
// a simple BFS to check all the variables that have side effect
|
||||
while let Some(id) = queue.pop_front() {
|
||||
if let Some(next) = visitor.graph.get(&id) {
|
||||
for i in next {
|
||||
if !visitor.has_side_effect.contains(i) {
|
||||
visitor.has_side_effect.insert(*i);
|
||||
queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (id, span, ident) in param_span {
|
||||
// if the variable is not used in recursion, it would be marked as unused
|
||||
if !visitor.has_side_effect.contains(&id) {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut visited = FxHashSet::default();
|
||||
|
||||
queue.push_back(id);
|
||||
|
||||
// a simple BFS to check the graph can reach to itself
|
||||
// if it can't, it means the variable is never used in recursion
|
||||
while let Some(id) = queue.pop_front() {
|
||||
if let Some(next) = visitor.graph.get(&id) {
|
||||
for i in next {
|
||||
if !visited.contains(i) {
|
||||
visited.insert(id);
|
||||
queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if visited.contains(&id) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ONLY_USED_IN_RECURSION,
|
||||
span,
|
||||
"parameter is only used in recursion",
|
||||
"if this is intentional, prefix with an underscore",
|
||||
format!("_{}", ident.name.as_str()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_primitive(ty: Ty<'_>) -> bool {
|
||||
let ty = ty.peel_refs();
|
||||
ty.is_primitive() || ty.is_str()
|
||||
}
|
||||
|
||||
pub fn is_array(ty: Ty<'_>) -> bool {
|
||||
let ty = ty.peel_refs();
|
||||
ty.is_array() || ty.is_array_slice()
|
||||
}
|
||||
|
||||
/// This builds the graph of side effect.
|
||||
/// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
|
||||
///
|
||||
/// There are some example in following code:
|
||||
/// ```rust, ignore
|
||||
/// let b = 1;
|
||||
/// let a = b; // a -> b
|
||||
/// let (c, d) = (a, b); // c -> b, d -> b
|
||||
///
|
||||
/// let e = if a == 0 { // e -> a
|
||||
/// c // e -> c
|
||||
/// } else {
|
||||
/// d // e -> d
|
||||
/// };
|
||||
/// ```
|
||||
pub struct SideEffectVisit<'tcx> {
|
||||
graph: FxHashMap<HirId, FxHashSet<HirId>>,
|
||||
has_side_effect: FxHashSet<HirId>,
|
||||
// bool for if the variable was dereferenced from mutable reference
|
||||
ret_vars: Vec<(HirId, bool)>,
|
||||
contains_side_effect: bool,
|
||||
// break label
|
||||
break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
|
||||
params: Vec<&'tcx Pat<'tcx>>,
|
||||
fn_ident: Ident,
|
||||
fn_def_id: DefId,
|
||||
is_method: bool,
|
||||
has_self: bool,
|
||||
ty_res: &'tcx TypeckResults<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
visited_exprs: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
|
||||
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
|
||||
match s.kind {
|
||||
StmtKind::Local(Local {
|
||||
pat, init: Some(init), ..
|
||||
}) => {
|
||||
self.visit_pat_expr(pat, init, false);
|
||||
},
|
||||
StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
|
||||
walk_stmt(self, s);
|
||||
},
|
||||
StmtKind::Local(_) => {},
|
||||
}
|
||||
self.ret_vars.clear();
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
if !self.visited_exprs.insert(ex.hir_id) {
|
||||
return;
|
||||
}
|
||||
match ex.kind {
|
||||
ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
|
||||
self.ret_vars = exprs
|
||||
.iter()
|
||||
.flat_map(|expr| {
|
||||
self.visit_expr(expr);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect();
|
||||
},
|
||||
ExprKind::Call(callee, args) => self.visit_fn(callee, args),
|
||||
ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
|
||||
ExprKind::Binary(_, lhs, rhs) => {
|
||||
self.visit_bin_op(lhs, rhs);
|
||||
},
|
||||
ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
|
||||
ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
|
||||
ExprKind::If(bind, then_expr, else_expr) => {
|
||||
self.visit_if(bind, then_expr, else_expr);
|
||||
},
|
||||
ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
|
||||
// since analysing the closure is not easy, just set all variables in it to side-effect
|
||||
ExprKind::Closure(&Closure { body, .. }) => {
|
||||
let body = self.tcx.hir().body(body);
|
||||
self.visit_body(body);
|
||||
let vars = std::mem::take(&mut self.ret_vars);
|
||||
self.add_side_effect(vars);
|
||||
},
|
||||
ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
|
||||
self.visit_block_label(block, label);
|
||||
},
|
||||
ExprKind::Assign(bind, expr, _) => {
|
||||
self.visit_assign(bind, expr);
|
||||
},
|
||||
ExprKind::AssignOp(_, bind, expr) => {
|
||||
self.visit_assign(bind, expr);
|
||||
self.visit_bin_op(bind, expr);
|
||||
},
|
||||
ExprKind::Field(expr, _) => {
|
||||
self.visit_expr(expr);
|
||||
if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
|
||||
// `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
|
||||
// It can't be renamed, and it can't be removed without removing it from multiple functions.
|
||||
let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
|
||||
Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
|
||||
Some(Node::TraitItem(&TraitItem {
|
||||
kind: TraitItemKind::Fn(ref sig, _),
|
||||
def_id,
|
||||
..
|
||||
})) => (
|
||||
def_id.to_def_id(),
|
||||
FnKind::TraitFn,
|
||||
if sig.decl.implicit_self.has_implicit_self() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
),
|
||||
Some(Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(ref sig, _),
|
||||
def_id,
|
||||
..
|
||||
})) => {
|
||||
#[allow(trivial_casts)]
|
||||
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
|
||||
&& let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
|
||||
&& let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
|
||||
{
|
||||
(
|
||||
trait_item_id,
|
||||
FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
|
||||
if sig.decl.implicit_self.has_implicit_self() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(def_id.to_def_id(), FnKind::Fn, 0)
|
||||
}
|
||||
},
|
||||
ExprKind::Index(expr, index) => {
|
||||
self.visit_expr(expr);
|
||||
let mut vars = std::mem::take(&mut self.ret_vars);
|
||||
self.visit_expr(index);
|
||||
self.ret_vars.append(&mut vars);
|
||||
|
||||
if !is_array(self.ty_res.expr_ty(expr)) {
|
||||
self.add_side_effect(self.ret_vars.clone());
|
||||
} else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
|
||||
}
|
||||
},
|
||||
ExprKind::Break(dest, Some(expr)) => {
|
||||
self.visit_expr(expr);
|
||||
if let Some(label) = dest.label {
|
||||
self.break_vars
|
||||
.entry(label.ident)
|
||||
.or_insert(Vec::new())
|
||||
.append(&mut self.ret_vars);
|
||||
}
|
||||
self.contains_side_effect = true;
|
||||
},
|
||||
ExprKind::Ret(Some(expr)) => {
|
||||
self.visit_expr(expr);
|
||||
let vars = std::mem::take(&mut self.ret_vars);
|
||||
self.add_side_effect(vars);
|
||||
self.contains_side_effect = true;
|
||||
},
|
||||
ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
|
||||
self.contains_side_effect = true;
|
||||
},
|
||||
ExprKind::Struct(_, exprs, expr) => {
|
||||
let mut ret_vars = exprs
|
||||
.iter()
|
||||
.flat_map(|field| {
|
||||
self.visit_expr(field.expr);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect();
|
||||
|
||||
walk_list!(self, visit_expr, expr);
|
||||
self.ret_vars.append(&mut ret_vars);
|
||||
},
|
||||
_ => walk_expr(self, ex),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
|
||||
if let Res::Local(id) = path.res {
|
||||
self.ret_vars.push((id, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> SideEffectVisit<'tcx> {
|
||||
fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
|
||||
// Just support array and tuple unwrapping for now.
|
||||
//
|
||||
// ex) `(a, b) = (c, d);`
|
||||
// The graph would look like this:
|
||||
// a -> c
|
||||
// b -> d
|
||||
//
|
||||
// This would minimize the connection of the side-effect graph.
|
||||
match (&lhs.kind, &rhs.kind) {
|
||||
(ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
|
||||
// if not, it is a compile error
|
||||
debug_assert!(lhs.len() == rhs.len());
|
||||
izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
|
||||
},
|
||||
// in other assigns, we have to connect all each other
|
||||
// because they can be connected somehow
|
||||
_ => {
|
||||
self.visit_expr(lhs);
|
||||
let lhs_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.visit_expr(rhs);
|
||||
let rhs_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.connect_assign(&lhs_vars, &rhs_vars, false);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
|
||||
self.visit_block(block);
|
||||
let _ = label.and_then(|label| {
|
||||
self.break_vars
|
||||
.remove(&label.ident)
|
||||
.map(|mut break_vars| self.ret_vars.append(&mut break_vars))
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
|
||||
self.visit_expr(lhs);
|
||||
let mut ret_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.visit_expr(rhs);
|
||||
self.ret_vars.append(&mut ret_vars);
|
||||
|
||||
// the binary operation between non primitive values are overloaded operators
|
||||
// so they can have side-effects
|
||||
if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
|
||||
self.ret_vars.iter().for_each(|id| {
|
||||
self.has_side_effect.insert(id.0);
|
||||
});
|
||||
self.contains_side_effect = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
|
||||
self.visit_expr(expr);
|
||||
let ty = self.ty_res.expr_ty(expr);
|
||||
// dereferencing a reference has no side-effect
|
||||
if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
|
||||
self.add_side_effect(self.ret_vars.clone());
|
||||
}
|
||||
|
||||
if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
|
||||
self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
|
||||
match (&pat.kind, &expr.kind) {
|
||||
(PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
|
||||
self.ret_vars = izip!(*pats, *exprs)
|
||||
.flat_map(|(pat, expr)| {
|
||||
self.visit_pat_expr(pat, expr, connect_self);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect();
|
||||
},
|
||||
(PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
|
||||
let mut vars = izip!(*front_exprs, *exprs)
|
||||
.flat_map(|(pat, expr)| {
|
||||
self.visit_pat_expr(pat, expr, connect_self);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect();
|
||||
self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
|
||||
.flat_map(|(pat, expr)| {
|
||||
self.visit_pat_expr(pat, expr, connect_self);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect();
|
||||
self.ret_vars.append(&mut vars);
|
||||
},
|
||||
_ => {
|
||||
let mut lhs_vars = Vec::new();
|
||||
pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
|
||||
self.visit_expr(expr);
|
||||
let rhs_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
|
||||
self.ret_vars = rhs_vars;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
|
||||
self.visit_expr(callee);
|
||||
let mut ret_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.add_side_effect(ret_vars.clone());
|
||||
|
||||
let mut is_recursive = false;
|
||||
|
||||
if_chain! {
|
||||
if !self.has_self;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
|
||||
if let Res::Def(DefKind::Fn, def_id) = path.res;
|
||||
if self.fn_def_id == def_id;
|
||||
then {
|
||||
is_recursive = true;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if !self.has_self && self.is_method;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
|
||||
if segment.ident == self.fn_ident;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
|
||||
if let Res::SelfTy{ .. } = path.res;
|
||||
then {
|
||||
is_recursive = true;
|
||||
}
|
||||
}
|
||||
|
||||
if is_recursive {
|
||||
izip!(self.params.clone(), args).for_each(|(pat, expr)| {
|
||||
self.visit_pat_expr(pat, expr, true);
|
||||
self.ret_vars.clear();
|
||||
});
|
||||
} else {
|
||||
// This would set arguments used in closure that does not have side-effect.
|
||||
// Closure itself can be detected whether there is a side-effect, but the
|
||||
// value of variable that is holding closure can change.
|
||||
// So, we just check the variables.
|
||||
self.ret_vars = args
|
||||
.iter()
|
||||
.flat_map(|expr| {
|
||||
self.visit_expr(expr);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect_vec()
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
self.has_side_effect.insert(id.0);
|
||||
id
|
||||
})
|
||||
.collect();
|
||||
self.contains_side_effect = true;
|
||||
}
|
||||
|
||||
self.ret_vars.append(&mut ret_vars);
|
||||
}
|
||||
|
||||
fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
|
||||
if_chain! {
|
||||
if self.is_method;
|
||||
if path.ident == self.fn_ident;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
|
||||
if let Res::Local(..) = path.res;
|
||||
let ident = path.segments.last().unwrap().ident;
|
||||
if ident.name == kw::SelfLower;
|
||||
then {
|
||||
izip!(self.params.clone(), args.iter())
|
||||
.for_each(|(pat, expr)| {
|
||||
self.visit_pat_expr(pat, expr, true);
|
||||
self.ret_vars.clear();
|
||||
});
|
||||
} else {
|
||||
self.ret_vars = args
|
||||
.iter()
|
||||
.flat_map(|expr| {
|
||||
self.visit_expr(expr);
|
||||
std::mem::take(&mut self.ret_vars)
|
||||
})
|
||||
.collect_vec()
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
self.has_side_effect.insert(a.0);
|
||||
a
|
||||
})
|
||||
.collect();
|
||||
self.contains_side_effect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
|
||||
let contains_side_effect = self.contains_side_effect;
|
||||
self.contains_side_effect = false;
|
||||
self.visit_expr(bind);
|
||||
let mut vars = std::mem::take(&mut self.ret_vars);
|
||||
self.visit_expr(then_expr);
|
||||
let mut then_vars = std::mem::take(&mut self.ret_vars);
|
||||
walk_list!(self, visit_expr, else_expr);
|
||||
if self.contains_side_effect {
|
||||
self.add_side_effect(vars.clone());
|
||||
}
|
||||
self.contains_side_effect |= contains_side_effect;
|
||||
self.ret_vars.append(&mut vars);
|
||||
self.ret_vars.append(&mut then_vars);
|
||||
}
|
||||
|
||||
fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||
self.visit_expr(expr);
|
||||
let mut expr_vars = std::mem::take(&mut self.ret_vars);
|
||||
self.ret_vars = arms
|
||||
_ => return,
|
||||
};
|
||||
body.params
|
||||
.iter()
|
||||
.flat_map(|arm| {
|
||||
let contains_side_effect = self.contains_side_effect;
|
||||
self.contains_side_effect = false;
|
||||
// this would visit `expr` multiple times
|
||||
// but couldn't think of a better way
|
||||
self.visit_pat_expr(arm.pat, expr, false);
|
||||
let mut vars = std::mem::take(&mut self.ret_vars);
|
||||
let _ = arm.guard.as_ref().map(|guard| {
|
||||
self.visit_expr(match guard {
|
||||
Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
|
||||
});
|
||||
vars.append(&mut self.ret_vars);
|
||||
});
|
||||
self.visit_expr(arm.body);
|
||||
if self.contains_side_effect {
|
||||
self.add_side_effect(vars.clone());
|
||||
self.add_side_effect(expr_vars.clone());
|
||||
}
|
||||
self.contains_side_effect |= contains_side_effect;
|
||||
vars.append(&mut self.ret_vars);
|
||||
vars
|
||||
.enumerate()
|
||||
.skip(skip_params)
|
||||
.filter_map(|(idx, p)| match p.pat.kind {
|
||||
PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
|
||||
Some((id, Param::new(fn_id, fn_kind, idx, ident)))
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
self.ret_vars.append(&mut expr_vars);
|
||||
.for_each(|(id, param)| self.params.insert(param, id));
|
||||
if self.entered_body.is_none() {
|
||||
self.entered_body = Some(body.value.hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
|
||||
// if mutable dereference is on assignment it can have side-effect
|
||||
// (this can lead to parameter mutable dereference and change the original value)
|
||||
// too hard to detect whether this value is from parameter, so this would all
|
||||
// check mutable dereference assignment to side effect
|
||||
lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
|
||||
self.has_side_effect.insert(*id);
|
||||
self.contains_side_effect = true;
|
||||
});
|
||||
|
||||
// there is no connection
|
||||
if lhs.is_empty() || rhs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// by connected rhs in cycle, the connections would decrease
|
||||
// from `n * m` to `n + m`
|
||||
// where `n` and `m` are length of `lhs` and `rhs`.
|
||||
|
||||
// unwrap is possible since rhs is not empty
|
||||
let rhs_first = rhs.first().unwrap();
|
||||
for (id, _) in lhs.iter() {
|
||||
if connect_self || *id != rhs_first.0 {
|
||||
self.graph
|
||||
.entry(*id)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
.insert(rhs_first.0);
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
|
||||
if let Some(id) = path_to_local(e)
|
||||
&& let Some(param) = self.params.get_by_id_mut(id)
|
||||
{
|
||||
let typeck = cx.typeck_results();
|
||||
let span = e.span;
|
||||
let mut e = e;
|
||||
loop {
|
||||
match get_expr_use_or_unification_node(cx.tcx, e) {
|
||||
None | Some((Node::Stmt(_), _)) => return,
|
||||
Some((Node::Expr(parent), child_id)) => match parent.kind {
|
||||
// Recursive call. Track which index the parameter is used in.
|
||||
ExprKind::Call(callee, args)
|
||||
if path_def_id(cx, callee).map_or(false, |id| {
|
||||
id == param.fn_id
|
||||
&& has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
|
||||
}) =>
|
||||
{
|
||||
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
|
||||
param.uses.push(Usage::new(span, idx));
|
||||
}
|
||||
return;
|
||||
},
|
||||
ExprKind::MethodCall(_, args, _)
|
||||
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
|
||||
id == param.fn_id
|
||||
&& has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
|
||||
}) =>
|
||||
{
|
||||
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
|
||||
param.uses.push(Usage::new(span, idx));
|
||||
}
|
||||
return;
|
||||
},
|
||||
// Assignment to a parameter is fine.
|
||||
ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
|
||||
return;
|
||||
},
|
||||
// Parameter update e.g. `x = x + 1`
|
||||
ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
|
||||
if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
|
||||
{
|
||||
return;
|
||||
},
|
||||
// Side-effect free expressions. Walk to the parent expression.
|
||||
ExprKind::Binary(_, lhs, rhs)
|
||||
if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
|
||||
{
|
||||
e = parent;
|
||||
continue;
|
||||
},
|
||||
ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
|
||||
e = parent;
|
||||
continue;
|
||||
},
|
||||
ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
|
||||
e = parent;
|
||||
continue;
|
||||
},
|
||||
// Only allow field accesses without auto-deref
|
||||
ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
|
||||
e = parent;
|
||||
continue
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
self.params.remove_by_id(id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let rhs = rhs.iter();
|
||||
izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
|
||||
if connect_self || from.0 != to.0 {
|
||||
self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
|
||||
for (id, _) in v {
|
||||
self.has_side_effect.insert(id);
|
||||
self.contains_side_effect = true;
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
|
||||
if self.entered_body == Some(body.value.hir_id) {
|
||||
self.entered_body = None;
|
||||
self.params.flag_for_linting();
|
||||
for param in &self.params.params {
|
||||
if param.apply_lint.get() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ONLY_USED_IN_RECURSION,
|
||||
param.ident.span,
|
||||
"parameter is only used in recursion",
|
||||
|diag| {
|
||||
if param.ident.name != kw::SelfLower {
|
||||
diag.span_suggestion(
|
||||
param.ident.span,
|
||||
"if this is intentional, prefix it with an underscore",
|
||||
format!("_{}", param.ident.name),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
diag.span_note(
|
||||
param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
|
||||
"parameter used here",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
self.params.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
|
||||
match kind {
|
||||
FnKind::Fn => true,
|
||||
FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
|
||||
GenericArgKind::Lifetime(_) => true,
|
||||
GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
|
||||
GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
|
||||
}),
|
||||
#[allow(trivial_casts)]
|
||||
FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
|
||||
peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
|
||||
/// Lints usage of `if let Some(v) = ... { y } else { x }` and
|
||||
/// `match .. { Some(v) => y, None/_ => x }` which are more
|
||||
/// idiomatically done with `Option::map_or` (if the else bit is a pure
|
||||
/// expression) or `Option::map_or_else` (if the else bit is an impure
|
||||
/// expression).
|
||||
|
@ -39,6 +40,10 @@ declare_clippy_lint! {
|
|||
/// } else {
|
||||
/// 5
|
||||
/// };
|
||||
/// let _ = match optional {
|
||||
/// Some(val) => val + 1,
|
||||
/// None => 5
|
||||
/// };
|
||||
/// let _ = if let Some(foo) = optional {
|
||||
/// foo
|
||||
/// } else {
|
||||
|
@ -53,11 +58,14 @@ declare_clippy_lint! {
|
|||
/// # let optional: Option<u32> = Some(0);
|
||||
/// # fn do_complicated_function() -> u32 { 5 };
|
||||
/// let _ = optional.map_or(5, |foo| foo);
|
||||
/// let _ = optional.map_or(5, |val| val + 1);
|
||||
/// let _ = optional.map_or_else(||{
|
||||
/// let y = do_complicated_function();
|
||||
/// y*y
|
||||
/// }, |foo| foo);
|
||||
/// ```
|
||||
// FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
|
||||
// covers matches and `Result`.
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub OPTION_IF_LET_ELSE,
|
||||
nursery,
|
||||
|
@ -66,19 +74,21 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
|
||||
|
||||
/// Returns true iff the given expression is the result of calling `Result::ok`
|
||||
fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
|
||||
path.ident.name.as_str() == "ok"
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct containing information about occurrences of the
|
||||
/// `if let Some(..) = .. else` construct that this lint detects.
|
||||
struct OptionIfLetElseOccurrence {
|
||||
/// A struct containing information about occurrences of construct that this lint detects
|
||||
///
|
||||
/// Such as:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if let Some(..) = {..} else {..}
|
||||
/// ```
|
||||
/// or
|
||||
/// ```ignore
|
||||
/// match x {
|
||||
/// Some(..) => {..},
|
||||
/// None/_ => {..}
|
||||
/// }
|
||||
/// ```
|
||||
struct OptionOccurence {
|
||||
option: String,
|
||||
method_sugg: String,
|
||||
some_expr: String,
|
||||
|
@ -99,43 +109,38 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
|
|||
)
|
||||
}
|
||||
|
||||
/// If this expression is the option if let/else construct we're detecting, then
|
||||
/// this function returns an `OptionIfLetElseOccurrence` struct with details if
|
||||
/// this construct is found, or None if this construct is not found.
|
||||
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
|
||||
fn try_get_option_occurence<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
expr: &Expr<'_>,
|
||||
if_then: &'tcx Expr<'_>,
|
||||
if_else: &'tcx Expr<'_>,
|
||||
) -> Option<OptionOccurence> {
|
||||
let cond_expr = match expr.kind {
|
||||
ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
|
||||
_ => expr,
|
||||
};
|
||||
let inner_pat = try_get_inner_pat(cx, pat)?;
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
|
||||
if !in_constant(cx, expr.hir_id);
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
|
||||
= higher::IfLet::hir(cx, expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
|
||||
if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
|
||||
if is_lang_ctor(cx, struct_qpath, OptionSome);
|
||||
if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
|
||||
if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
|
||||
if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
|
||||
if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
|
||||
if some_captures
|
||||
.iter()
|
||||
.filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
|
||||
.all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
|
||||
|
||||
then {
|
||||
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
|
||||
let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" };
|
||||
let some_body = peel_blocks(if_then);
|
||||
let none_body = peel_blocks(if_else);
|
||||
let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
|
||||
let capture_name = id.name.to_ident_string();
|
||||
let (as_ref, as_mut) = match &let_expr.kind {
|
||||
let (as_ref, as_mut) = match &expr.kind {
|
||||
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
|
||||
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
|
||||
_ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
|
||||
};
|
||||
let cond_expr = match let_expr.kind {
|
||||
// Pointer dereferencing happens automatically, so we can omit it in the suggestion
|
||||
ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
|
||||
_ => let_expr,
|
||||
_ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut),
|
||||
};
|
||||
|
||||
// Check if captures the closure will need conflict with borrows made in the scrutinee.
|
||||
// TODO: check all the references made in the scrutinee expression. This will require interacting
|
||||
// with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
|
||||
|
@ -154,30 +159,100 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
|
|||
}
|
||||
}
|
||||
}
|
||||
Some(OptionIfLetElseOccurrence {
|
||||
|
||||
return Some(OptionOccurence {
|
||||
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
|
||||
method_sugg: method_sugg.to_string(),
|
||||
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
|
||||
none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
|
||||
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return Some(inner_pat);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// If this expression is the option if let/else construct we're detecting, then
|
||||
/// this function returns an `OptionOccurence` struct with details if
|
||||
/// this construct is found, or None if this construct is not found.
|
||||
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
|
||||
if let Some(higher::IfLet {
|
||||
let_pat,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else: Some(if_else),
|
||||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
if !is_else_clause(cx.tcx, expr) {
|
||||
return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
|
||||
if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
|
||||
if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
|
||||
return try_get_option_occurence(cx, let_pat, ex, if_then, if_else);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn try_convert_match<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
arms: &[Arm<'tcx>],
|
||||
) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
if arms.len() == 2 {
|
||||
return if is_none_or_err_arm(cx, &arms[1]) {
|
||||
Some((arms[0].pat, arms[0].body, arms[1].body))
|
||||
} else if is_none_or_err_arm(cx, &arms[0]) {
|
||||
Some((arms[1].pat, arms[1].body, arms[0].body))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [first_pat], _) => {
|
||||
is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
|
||||
},
|
||||
PatKind::Wild => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let Some(detection) = detect_option_if_let_else(cx, expr) {
|
||||
// Don't lint macros and constants
|
||||
if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
|
||||
if let Some(det) = detection {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_IF_LET_ELSE,
|
||||
expr.span,
|
||||
format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
|
||||
format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}({}, {})",
|
||||
detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
|
||||
det.option, det.method_sugg, det.none_expr, det.some_expr
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
|
||||
/// calls on `PathBuf` that can cause overwrites.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calling `push` with a root path at the start can overwrite the
|
||||
/// previous defined path.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("/bar");
|
||||
/// assert_eq!(x, PathBuf::from("/bar"));
|
||||
/// ```
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::path::PathBuf;
|
||||
///
|
||||
/// let mut x = PathBuf::from("/foo");
|
||||
/// x.push("bar");
|
||||
/// assert_eq!(x, PathBuf::from("/foo/bar"));
|
||||
/// ```
|
||||
#[clippy::version = "1.36.0"]
|
||||
pub PATH_BUF_PUSH_OVERWRITE,
|
||||
nursery,
|
||||
"calling `push` with file system root on `PathBuf` can overwrite it"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
|
||||
if path.ident.name == sym!(push);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = get_index_arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
if pushed_path.has_root();
|
||||
if let Some(root) = pushed_path.components().next();
|
||||
if root == Component::RootDir;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
|
|||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
|
||||
if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
|
||||
if let PatKind::Binding(annot, bind_id, ident, None) = field.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
||||
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
|
||||
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
||||
|
|
|
@ -1,46 +1,20 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::sym;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for zipping a collection with the range of
|
||||
/// `0.._.len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The code is better expressed with `.enumerate()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().zip(0..x.len());
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = vec![1];
|
||||
/// let _ = x.iter().enumerate();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub RANGE_ZIP_WITH_LEN,
|
||||
complexity,
|
||||
"zipping iterator with a range when `enumerate()` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for exclusive ranges where 1 is added to the
|
||||
|
@ -198,7 +172,6 @@ impl Ranges {
|
|||
}
|
||||
|
||||
impl_lint_pass!(Ranges => [
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
RANGE_PLUS_ONE,
|
||||
RANGE_MINUS_ONE,
|
||||
REVERSED_EMPTY_RANGES,
|
||||
|
@ -207,16 +180,10 @@ impl_lint_pass!(Ranges => [
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ranges {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(path, args, _) => {
|
||||
check_range_zip_with_len(cx, path, args, expr.span);
|
||||
},
|
||||
ExprKind::Binary(ref op, l, r) => {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
if let ExprKind::Binary(ref op, l, r) = expr.kind {
|
||||
if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
|
||||
check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
|
||||
}
|
||||
}
|
||||
|
||||
check_exclusive_range_plus_one(cx, expr);
|
||||
|
@ -380,34 +347,6 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
|||
None
|
||||
}
|
||||
|
||||
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
|
||||
if_chain! {
|
||||
if path.ident.as_str() == "zip";
|
||||
if let [iter, zip_arg] = args;
|
||||
// `.iter()` call
|
||||
if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
|
||||
if iter_path.ident.name == sym::iter;
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, iter_caller.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exclusive range plus one: `x..(y+1)`
|
||||
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
|
|
|
@ -41,7 +41,7 @@ declare_clippy_lint! {
|
|||
/// let data = std::rc::Rc::new("some data".to_string());
|
||||
/// let v = vec![data; 100];
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub RC_CLONE_IN_VEC_INIT,
|
||||
suspicious,
|
||||
"initializing reference-counted pointer in `vec![elem; len]`"
|
||||
|
|
|
@ -11,6 +11,8 @@ use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
|
|||
use rustc_middle::ty::subst::GenericArg;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for redundant slicing expressions which use the full range, and
|
||||
|
@ -134,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
|
|||
} else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
|
||||
if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
|
||||
cx.param_env,
|
||||
cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
|
||||
cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))),
|
||||
) {
|
||||
if deref_ty == expr_ty {
|
||||
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
|
||||
|
|
|
@ -48,15 +48,15 @@ impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
|
|||
|
||||
impl RedundantStaticLifetimes {
|
||||
// Recursively visit types
|
||||
fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
|
||||
fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
|
||||
match ty.kind {
|
||||
// Be careful of nested structures (arrays and tuples)
|
||||
TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
|
||||
self.visit_type(ty, cx, reason);
|
||||
Self::visit_type(ty, cx, reason);
|
||||
},
|
||||
TyKind::Tup(ref tup) => {
|
||||
for tup_ty in tup {
|
||||
self.visit_type(tup_ty, cx, reason);
|
||||
Self::visit_type(tup_ty, cx, reason);
|
||||
}
|
||||
},
|
||||
// This is what we are looking for !
|
||||
|
@ -87,7 +87,7 @@ impl RedundantStaticLifetimes {
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
self.visit_type(&borrow_type.ty, cx, reason);
|
||||
Self::visit_type(&borrow_type.ty, cx, reason);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
@ -102,13 +102,13 @@ impl EarlyLintPass for RedundantStaticLifetimes {
|
|||
|
||||
if !item.span.from_expansion() {
|
||||
if let ItemKind::Const(_, ref var_type, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
|
||||
// Don't check associated consts because `'static` cannot be elided on those (issue
|
||||
// #2438)
|
||||
}
|
||||
|
||||
if let ItemKind::Static(ref var_type, _, _) = item.kind {
|
||||
self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
|
||||
Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `.repeat(1)` and suggest the following method for each types.
|
||||
/// - `.to_string()` for `str`
|
||||
/// - `.clone()` for `String`
|
||||
/// - `.to_vec()` for `slice`
|
||||
///
|
||||
/// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
|
||||
/// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
|
||||
/// the string is the intention behind this, `clone()` should be used.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").repeat(1);
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = String::from("hello world").clone();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub REPEAT_ONCE,
|
||||
complexity,
|
||||
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
|
||||
}
|
||||
|
||||
declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [receiver, count], _) = &expr.kind;
|
||||
if path.ident.name == sym!(repeat);
|
||||
if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
|
||||
if !receiver.span.from_expansion();
|
||||
then {
|
||||
let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::String) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
|
|||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||
use clippy_utils::{fn_def_id, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
|
||||
|
@ -11,7 +10,6 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -152,10 +150,6 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
|||
}
|
||||
}
|
||||
|
||||
fn attr_is_cfg(attr: &Attribute) -> bool {
|
||||
attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
|
||||
}
|
||||
|
||||
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
if let Some(expr) = block.expr {
|
||||
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
|
||||
|
@ -178,9 +172,7 @@ fn check_final_expr<'tcx>(
|
|||
match expr.kind {
|
||||
// simple return is always "bad"
|
||||
ExprKind::Ret(ref inner) => {
|
||||
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
|
||||
let attrs = cx.tcx.hir().attrs(expr.hir_id);
|
||||
if !attrs.iter().any(attr_is_cfg) {
|
||||
if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
|
||||
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
|
||||
if !borrows {
|
||||
emit_return_lint(
|
||||
|
|
|
@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
|||
if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl)
|
||||
&& let item = cx.tcx.hir().item(id)
|
||||
&& let ItemKind::Impl(Impl {
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
||||
{
|
||||
if !map.contains_key(res) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::return_ty;
|
||||
use clippy_utils::ty::{contains_adt_constructor, contains_ty};
|
||||
use clippy_utils::ty::contains_adt_constructor;
|
||||
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
|
|||
if !contains_adt_constructor(ret_ty, self_adt) {
|
||||
return;
|
||||
}
|
||||
} else if !contains_ty(ret_ty, self_ty) {
|
||||
} else if !ret_ty.contains(self_ty) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
/// as arrays, slices, and tuples of such items), it is typically better to
|
||||
/// use an unstable sort than a stable sort.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Typically, using a stable sort consumes more memory and cpu cycles.
|
||||
/// Because values which compare equal are identical, preserving their
|
||||
/// relative order (the guarantee that a stable sort provides) means
|
||||
/// nothing, while the extra costs still apply.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// As pointed out in
|
||||
/// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
|
||||
/// a stable sort can instead be significantly faster for certain scenarios
|
||||
/// (eg. when a sorted vector is extended with new data and resorted).
|
||||
///
|
||||
/// For more information and benchmarking results, please refer to the
|
||||
/// issue linked above.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
/// vec.sort_unstable();
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub STABLE_SORT_PRIMITIVE,
|
||||
pedantic,
|
||||
"use of sort() when sort_unstable() is equivalent"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]);
|
||||
|
||||
/// The three "kinds" of sorts
|
||||
enum SortingKind {
|
||||
Vanilla,
|
||||
/* The other kinds of lint are currently commented out because they
|
||||
* can map distinct values to equal ones. If the key function is
|
||||
* provably one-to-one, or if the Cmp function conserves equality,
|
||||
* then they could be linted on, but I don't know if we can check
|
||||
* for that. */
|
||||
|
||||
/* ByKey,
|
||||
* ByCmp, */
|
||||
}
|
||||
impl SortingKind {
|
||||
/// The name of the stable version of this kind of sort
|
||||
fn stable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort",
|
||||
/* SortingKind::ByKey => "sort_by_key",
|
||||
* SortingKind::ByCmp => "sort_by", */
|
||||
}
|
||||
}
|
||||
/// The name of the unstable version of this kind of sort
|
||||
fn unstable_name(&self) -> &str {
|
||||
match self {
|
||||
SortingKind::Vanilla => "sort_unstable",
|
||||
/* SortingKind::ByKey => "sort_unstable_by_key",
|
||||
* SortingKind::ByCmp => "sort_unstable_by", */
|
||||
}
|
||||
}
|
||||
/// Takes the name of a function call and returns the kind of sort
|
||||
/// that corresponds to that function name (or None if it isn't)
|
||||
fn from_stable_name(name: &str) -> Option<SortingKind> {
|
||||
match name {
|
||||
"sort" => Some(SortingKind::Vanilla),
|
||||
// "sort_by" => Some(SortingKind::ByCmp),
|
||||
// "sort_by_key" => Some(SortingKind::ByKey),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A detected instance of this lint
|
||||
struct LintDetection {
|
||||
slice_name: String,
|
||||
method: SortingKind,
|
||||
method_args: String,
|
||||
slice_type: String,
|
||||
}
|
||||
|
||||
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
|
||||
if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
|
||||
if let Some(slice_type) = is_slice_of_primitives(cx, slice);
|
||||
then {
|
||||
let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
|
||||
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for StableSortPrimitive {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(detection) = detect_stable_sort_primitive(cx, expr) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
expr.span,
|
||||
format!(
|
||||
"used `{}` on primitive type `{}`",
|
||||
detection.method.stable_name(),
|
||||
detection.slice_type,
|
||||
)
|
||||
.as_str(),
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
detection.slice_name,
|
||||
detection.method.unstable_name(),
|
||||
detection.method_args,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.note(
|
||||
"an unstable sort typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use clippy_utils::{SpanlessEq, SpanlessHash};
|
|||
use core::hash::{Hash, Hasher};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -103,7 +103,6 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
|||
fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
|
||||
self.check_type_repetition(cx, gen);
|
||||
check_trait_bound_duplication(cx, gen);
|
||||
check_bounds_or_where_duplication(cx, gen);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
|
@ -234,35 +233,61 @@ impl TraitBounds {
|
|||
}
|
||||
|
||||
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
|
||||
if gen.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut map = FxHashMap::<_, Vec<_>>::default();
|
||||
for predicate in gen.predicates {
|
||||
// Explanation:
|
||||
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
|
||||
// where T: Clone + Default, { unimplemented!(); }
|
||||
// ^^^^^^^^^^^^^^^^^^
|
||||
// |
|
||||
// collects each of these where clauses into a set keyed by generic name and comparable trait
|
||||
// eg. (T, Clone)
|
||||
let where_predicates = gen
|
||||
.predicates
|
||||
.iter()
|
||||
.filter_map(|pred| {
|
||||
if_chain! {
|
||||
if pred.in_where_clause();
|
||||
if let WherePredicate::BoundPredicate(bound_predicate) = pred;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
|
||||
then {
|
||||
return Some(
|
||||
rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
|
||||
.into_keys().map(|trait_ref| (path.res, trait_ref)))
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
.collect::<FxHashSet<_>>();
|
||||
|
||||
// Explanation:
|
||||
// fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
|
||||
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^
|
||||
// |
|
||||
// compare trait bounds keyed by generic name and comparable trait to collected where
|
||||
// predicates eg. (T, Clone)
|
||||
for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
|
||||
if_chain! {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||
if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
|
||||
if bound_predicate.origin != PredicateOrigin::ImplTrait;
|
||||
if !bound_predicate.span.from_expansion();
|
||||
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
||||
if let Some(segment) = segments.first();
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind;
|
||||
then {
|
||||
for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
|
||||
let trait_resolutions_direct = map.entry(segment.ident).or_default();
|
||||
if let Some((_, span_direct)) = trait_resolutions_direct
|
||||
.iter()
|
||||
.find(|(res_direct, _)| *res_direct == res_where) {
|
||||
let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
|
||||
for (trait_ref, span) in traits {
|
||||
let key = (path.res, trait_ref);
|
||||
if where_predicates.contains(&key) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
*span_direct,
|
||||
span,
|
||||
"this trait bound is already specified in the where clause",
|
||||
None,
|
||||
"consider removing this trait bound",
|
||||
);
|
||||
}
|
||||
else {
|
||||
trait_resolutions_direct.push((res_where, span_where));
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,23 +298,6 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
|||
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||
struct ComparableTraitRef(Res, Vec<Res>);
|
||||
|
||||
fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
if gen.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
for predicate in gen.predicates {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
|
||||
let msg = if predicate.in_where_clause() {
|
||||
"these where clauses contain repeated elements"
|
||||
} else {
|
||||
"these bounds contain repeated elements"
|
||||
};
|
||||
rollup_traits(cx, bound_predicate.bounds, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
|
||||
if let GenericBound::Trait(t, tbm) = bound {
|
||||
let trait_path = t.trait_ref.path;
|
||||
|
@ -331,7 +339,7 @@ fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
|
|||
)
|
||||
}
|
||||
|
||||
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
|
||||
fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> FxHashMap<ComparableTraitRef, Span> {
|
||||
let mut map = FxHashMap::default();
|
||||
let mut repeated_res = false;
|
||||
|
||||
|
@ -373,4 +381,6 @@ fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ mod transmute_ptr_to_ref;
|
|||
mod transmute_ref_to_ref;
|
||||
mod transmute_undefined_repr;
|
||||
mod transmutes_expressible_as_ptr_casts;
|
||||
mod transmuting_null;
|
||||
mod unsound_collection_transmute;
|
||||
mod useless_transmute;
|
||||
mod utils;
|
||||
|
@ -386,6 +387,28 @@ declare_clippy_lint! {
|
|||
"transmute to or from a type with an undefined representation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmute calls which would receive a null pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmuting a null pointer is undefined behavior.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Not all cases can be detected at the moment of this writing.
|
||||
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||
/// call, aren't detectable yet.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
|
||||
/// ```
|
||||
#[clippy::version = "1.35.0"]
|
||||
pub TRANSMUTING_NULL,
|
||||
correctness,
|
||||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||
}
|
||||
|
||||
pub struct Transmute {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [
|
|||
UNSOUND_COLLECTION_TRANSMUTE,
|
||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
TRANSMUTING_NULL,
|
||||
]);
|
||||
impl Transmute {
|
||||
#[must_use]
|
||||
|
@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
|
||||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||
| transmuting_null::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
|
|
|
@ -5,9 +5,9 @@ use rustc_hir::Expr;
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
|
@ -18,116 +18,89 @@ pub(super) fn check<'tcx>(
|
|||
let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
|
||||
|
||||
while from_ty != to_ty {
|
||||
match reduce_refs(cx, e.span, from_ty, to_ty) {
|
||||
ReducedTys::FromFatPtr {
|
||||
unsized_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match reduce_ty(cx, to_sub_ty) {
|
||||
ReducedTy::TypeErasure => break,
|
||||
ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
|
||||
ReducedTy::Ref(to_sub_ty) => {
|
||||
from_ty = unsized_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
_ => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != unsized_ty {
|
||||
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
let reduced_tys = reduce_refs(cx, from_ty, to_ty);
|
||||
match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
|
||||
// Various forms of type erasure.
|
||||
(ReducedTy::TypeErasure { raw_ptr_only: false }, _)
|
||||
| (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
|
||||
(ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
|
||||
(_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
|
||||
|
||||
// `Repr(C)` <-> unordered type.
|
||||
// If the first field of the `Repr(C)` type matches then the transmute is ok
|
||||
(ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
|
||||
| (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
ReducedTys::ToFatPtr {
|
||||
unsized_ty,
|
||||
from_ty: from_sub_ty,
|
||||
} => match reduce_ty(cx, from_sub_ty) {
|
||||
ReducedTy::TypeErasure => break,
|
||||
ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
|
||||
ReducedTy::Ref(from_sub_ty) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = unsized_ty;
|
||||
continue;
|
||||
},
|
||||
_ => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != unsized_ty {
|
||||
diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
ReducedTys::ToPtr {
|
||||
from_ty: from_sub_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match reduce_ty(cx, from_sub_ty) {
|
||||
ReducedTy::UnorderedFields(from_ty) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
ReducedTy::Ref(from_sub_ty) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
_ => break,
|
||||
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
|
||||
if reduced_tys.from_fat_ptr =>
|
||||
{
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
ReducedTys::FromPtr {
|
||||
from_ty: from_sub_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match reduce_ty(cx, to_sub_ty) {
|
||||
ReducedTy::UnorderedFields(to_ty) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
ReducedTy::Ref(to_sub_ty) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
_ => break,
|
||||
|
||||
// ptr <-> ptr
|
||||
(ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
|
||||
if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
|
||||
&& matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
|
||||
{
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
ReducedTys::Other {
|
||||
from_ty: from_sub_ty,
|
||||
to_ty: to_sub_ty,
|
||||
} => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
|
||||
(ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
|
||||
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
|
||||
let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
|
||||
|
||||
// fat ptr <-> (*size, *size)
|
||||
(ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
|
||||
if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
|
||||
{
|
||||
return false;
|
||||
},
|
||||
(ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
|
||||
if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
|
||||
{
|
||||
return false;
|
||||
},
|
||||
|
||||
// fat ptr -> some struct | some struct -> fat ptr
|
||||
(ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != from_ty.peel_refs() {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute to `{}` which has an undefined layout", to_ty_orig),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty.peel_refs() {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
|
||||
(ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
|
||||
let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
|
||||
= (from_ty.kind(), to_ty.kind())
|
||||
&& from_def == to_def
|
||||
{
|
||||
|
@ -138,79 +111,72 @@ pub(super) fn check<'tcx>(
|
|||
} else {
|
||||
None
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from `{}` to `{}`, both of which have an undefined layout",
|
||||
from_ty_orig, to_ty_orig
|
||||
),
|
||||
|diag| {
|
||||
if let Some(same_adt_did) = same_adt_did {
|
||||
diag.note(&format!(
|
||||
"two instances of the same generic type (`{}`) may have different layouts",
|
||||
cx.tcx.item_name(same_adt_did)
|
||||
));
|
||||
} else {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(
|
||||
ReducedTy::UnorderedFields(from_ty),
|
||||
ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
|
||||
) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||
|diag| {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from `{}` to `{}`, both of which have an undefined layout",
|
||||
from_ty_orig, to_ty_orig
|
||||
),
|
||||
|diag| {
|
||||
if let Some(same_adt_did) = same_adt_did {
|
||||
diag.note(&format!(
|
||||
"two instances of the same generic type (`{}`) may have different layouts",
|
||||
cx.tcx.item_name(same_adt_did)
|
||||
));
|
||||
} else {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(
|
||||
ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
|
||||
ReducedTy::UnorderedFields(to_ty),
|
||||
) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute into `{}` which has an undefined layout", to_ty_orig),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
|
||||
from_ty = from_sub_ty;
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
(
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
|
||||
ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
|
||||
)
|
||||
| (
|
||||
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
|
||||
ReducedTy::UnorderedFields(_) | ReducedTy::Param,
|
||||
) => break,
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(
|
||||
ReducedTy::UnorderedFields(from_ty),
|
||||
ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
|
||||
) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute from `{}` which has an undefined layout", from_ty_orig),
|
||||
|diag| {
|
||||
if from_ty_orig.peel_refs() != from_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(
|
||||
ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
|
||||
ReducedTy::UnorderedFields(to_ty),
|
||||
) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
e.span,
|
||||
&format!("transmute into `{}` which has an undefined layout", to_ty_orig),
|
||||
|diag| {
|
||||
if to_ty_orig.peel_refs() != to_ty {
|
||||
diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
|
||||
}
|
||||
},
|
||||
);
|
||||
return true;
|
||||
},
|
||||
(
|
||||
ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
|
||||
ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
|
||||
)
|
||||
| (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -218,64 +184,64 @@ pub(super) fn check<'tcx>(
|
|||
false
|
||||
}
|
||||
|
||||
enum ReducedTys<'tcx> {
|
||||
FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||
ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
|
||||
ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||
FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||
Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
struct ReducedTys<'tcx> {
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
from_raw_ptr: bool,
|
||||
to_raw_ptr: bool,
|
||||
from_fat_ptr: bool,
|
||||
to_fat_ptr: bool,
|
||||
}
|
||||
|
||||
/// Remove references so long as both types are references.
|
||||
fn reduce_refs<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
mut from_ty: Ty<'tcx>,
|
||||
mut to_ty: Ty<'tcx>,
|
||||
) -> ReducedTys<'tcx> {
|
||||
loop {
|
||||
return match (from_ty.kind(), to_ty.kind()) {
|
||||
fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
|
||||
let mut from_raw_ptr = false;
|
||||
let mut to_raw_ptr = false;
|
||||
let (from_fat_ptr, to_fat_ptr) = loop {
|
||||
break match (from_ty.kind(), to_ty.kind()) {
|
||||
(
|
||||
&(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
|
||||
&(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
|
||||
) => {
|
||||
from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
|
||||
from_ty = from_sub_ty;
|
||||
to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
|
||||
to_ty = to_sub_ty;
|
||||
continue;
|
||||
},
|
||||
(&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
|
||||
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
|
||||
if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
|
||||
{
|
||||
ReducedTys::FromFatPtr { unsized_ty, to_ty }
|
||||
(true, false)
|
||||
},
|
||||
(_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
|
||||
if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
|
||||
if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
|
||||
{
|
||||
ReducedTys::ToFatPtr { unsized_ty, from_ty }
|
||||
(false, true)
|
||||
},
|
||||
(&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
|
||||
ReducedTys::FromPtr { from_ty, to_ty }
|
||||
},
|
||||
(_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
|
||||
ReducedTys::ToPtr { from_ty, to_ty }
|
||||
},
|
||||
_ => ReducedTys::Other { from_ty, to_ty },
|
||||
_ => (false, false),
|
||||
};
|
||||
};
|
||||
ReducedTys {
|
||||
from_ty,
|
||||
to_ty,
|
||||
from_raw_ptr,
|
||||
to_raw_ptr,
|
||||
from_fat_ptr,
|
||||
to_fat_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
enum ReducedTy<'tcx> {
|
||||
/// The type can be used for type erasure.
|
||||
TypeErasure,
|
||||
TypeErasure { raw_ptr_only: bool },
|
||||
/// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
|
||||
/// sized fields with a defined order.
|
||||
OrderedFields(Ty<'tcx>),
|
||||
/// The second value is the first non-zero sized type.
|
||||
OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
|
||||
/// The type is a struct containing multiple non-zero sized fields with no defined order.
|
||||
UnorderedFields(Ty<'tcx>),
|
||||
/// The type is a reference to the contained type.
|
||||
Ref(Ty<'tcx>),
|
||||
/// The type is a generic parameter.
|
||||
Param,
|
||||
/// Any other type.
|
||||
Other(Ty<'tcx>),
|
||||
}
|
||||
|
@ -285,16 +251,18 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
|
|||
loop {
|
||||
ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
|
||||
return match *ty.kind() {
|
||||
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
|
||||
ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
|
||||
ReducedTy::TypeErasure { raw_ptr_only: false }
|
||||
},
|
||||
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
|
||||
ty = sub_ty;
|
||||
continue;
|
||||
},
|
||||
ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
|
||||
ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
|
||||
ty::Tuple(args) => {
|
||||
let mut iter = args.iter();
|
||||
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
||||
return ReducedTy::OrderedFields(ty);
|
||||
return ReducedTy::OrderedFields(ty, None);
|
||||
};
|
||||
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||
ty = sized_ty;
|
||||
|
@ -309,27 +277,25 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
|
|||
.iter()
|
||||
.map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
|
||||
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
||||
return ReducedTy::TypeErasure;
|
||||
return ReducedTy::TypeErasure { raw_ptr_only: false };
|
||||
};
|
||||
if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
|
||||
ty = sized_ty;
|
||||
continue;
|
||||
}
|
||||
if def.repr().inhibit_struct_field_reordering_opt() {
|
||||
ReducedTy::OrderedFields(ty)
|
||||
ReducedTy::OrderedFields(ty, Some(sized_ty))
|
||||
} else {
|
||||
ReducedTy::UnorderedFields(ty)
|
||||
}
|
||||
},
|
||||
ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
|
||||
ReducedTy::TypeErasure
|
||||
ReducedTy::TypeErasure { raw_ptr_only: false }
|
||||
},
|
||||
// TODO: Check if the conversion to or from at least one of a union's fields is valid.
|
||||
ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
|
||||
ty::Foreign(_) => ReducedTy::TypeErasure,
|
||||
ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
|
||||
ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
|
||||
ty::Param(_) => ReducedTy::Param,
|
||||
ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
|
||||
ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
|
||||
ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
|
||||
_ => ReducedTy::Other(ty),
|
||||
};
|
||||
}
|
||||
|
|
61
clippy_lints/src/transmute/transmuting_null.rs
Normal file
61
clippy_lints/src/transmute/transmuting_null.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_path_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::TRANSMUTING_NULL;
|
||||
|
||||
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
|
||||
if !to_ty.is_ref() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
|
||||
if x == 0;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_path_diagnostic_item(cx, func1, sym::ptr_null);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// Also catch transmutations of variables which are known nulls.
|
||||
// To do this, MIR const propagation seems to be the better tool.
|
||||
// Whenever MIR const prop routines are more developed, this will
|
||||
// become available. As of this writing (25/03/19) it is not yet.
|
||||
false
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_expr_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmute calls which would receive a null pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmuting a null pointer is undefined behavior.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Not all cases can be detected at the moment of this writing.
|
||||
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||
/// call, aren't detectable yet.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
|
||||
/// ```
|
||||
#[clippy::version = "1.35.0"]
|
||||
pub TRANSMUTING_NULL,
|
||||
correctness,
|
||||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||
}
|
||||
|
||||
declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
|
||||
|
||||
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind;
|
||||
if is_expr_diagnostic_item(cx, func, sym::transmute);
|
||||
|
||||
then {
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
|
||||
if x == 0;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// Also catch transmutations of variables which are known nulls.
|
||||
// To do this, MIR const propagation seems to be the better tool.
|
||||
// Whenever MIR const prop routines are more developed, this will
|
||||
// become available. As of this writing (25/03/19) it is not yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use clippy_utils::macros::span_is_local;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -98,6 +99,10 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
|
|||
}
|
||||
|
||||
fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
|
||||
if !span_is_local(span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let string = snippet(cx, span, "");
|
||||
if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
|
||||
span_lint_and_sugg(
|
||||
|
@ -113,6 +118,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
if string.chars().any(|c| c as u32 > 0x7F) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -128,6 +134,7 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -45,7 +45,7 @@ declare_clippy_lint! {
|
|||
/// let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
|
||||
/// vec.set_len(1000); // `MaybeUninit` can be uninitialized
|
||||
/// ```
|
||||
/// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
|
||||
/// 3. If you are on 1.60.0 or later, `Vec::spare_capacity_mut()` is available:
|
||||
/// ```rust,ignore
|
||||
/// let mut vec: Vec<u8> = Vec::with_capacity(1000);
|
||||
/// let remaining = vec.spare_capacity_mut(); // `&mut [MaybeUninit<u8>]`
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue