Merge commit 'cb7915b00c235e9b5861564f3be78dba330980ee' into clippyup

This commit is contained in:
flip1995 2021-09-28 18:03:12 +01:00
parent 067bfe3618
commit 23d5457e6d
142 changed files with 2227 additions and 1008 deletions

View file

@ -1,9 +1,10 @@
[alias]
uitest = "test --test compile-test"
dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
[build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
target-dir = "target"

View file

@ -8,7 +8,7 @@ about: Create a blank issue.
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
`@rustbot label +<label>`
@ rustbot label +<label>
Common labels for this issue type are:
* C-an-interesting-project

View file

@ -36,7 +36,7 @@ LLVM version: 10.0
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
`@rustbot label +<label>`
@ rustbot label +<label>
Common labels for this issue type are:
* `I-suggestion-causes-error`

View file

@ -37,7 +37,7 @@ LLVM version: 10.0
Additional labels can be added to this issue by including the following command
(without the space after the @ symbol):
`@rustbot label +<label>`
@ rustbot label +<label>
Common labels for this issue type are:
* I-suggestion-causes-error

View file

@ -6,11 +6,81 @@ document.
## Unreleased / In Rust Nightly
[74d1561...master](https://github.com/rust-lang/rust-clippy/compare/74d1561...master)
[7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...master)
## Rust 1.56
Current beta, release 2021-10-21
[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
### New Lints
* [`unwrap_or_else_default`]
[#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
### Enhancements
* [`needless_continue`]: Now also lints in `loop { continue; }` case
[#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
* [`disallowed_type`]: Now also primitive types can be disallowed
[#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
* [`manual_swap`]: Now also lints on xor swaps
[#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
* [`map_flatten`]: Now also lints on the `Result` type
[#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
* [`no_effect`]: Now also lints on inclusive ranges
[#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
### False Positive Fixes
* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
[#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
[#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
* [`similar_names`]: No longer complains about `iter` and `item` being too
similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
### Suggestion Fixes/Improvements
* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
[#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
* [`new_without_default`]: No longer shows the full qualified type path when
suggesting adding a `Default` implementation
[#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
[#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
[#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
### Documentation Improvements
* Clippy now uses a lint to generate its lint documentation. [Lints all the way
down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
[#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
* Reworked Clippy's website:
[#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
[#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
* Added applicability information about lints
* Added a link to jump into the implementation
* Improved loading times
* Adapted some styling
* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
[#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
### New Lints
* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
## Rust 1.55
Current beta, release 2021-09-09
Current stable, released 2021-09-09
[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
@ -126,21 +196,9 @@ Current beta, release 2021-09-09
* [`use_self`]
[#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
### Documentation Improvements
* Reworked Clippy's website:
[#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
[#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
* Added applicability information about lints
* Added a link to jump into the implementation
* Improved loading times
* Adapted some styling
* Clippy now uses a lint to generate its documentation
[#7298](https://github.com/rust-lang/rust-clippy/pull/7298)
## Rust 1.54
Current stable, released 2021-07-29
Released 2021-07-29
[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
@ -1050,7 +1108,7 @@ Released 2020-11-19
[#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
* Add example of false positive to [`ptr_arg`] docs.
[#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
[#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
## Rust 1.47
@ -1491,7 +1549,7 @@ Released 2020-03-12
* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
### ICE fixes
@ -2570,7 +2628,7 @@ Released 2018-09-13
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
@ -2685,9 +2743,9 @@ Released 2018-09-13
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
[`if_then_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
@ -2722,6 +2780,7 @@ Released 2018-09-13
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
[`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_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
@ -2773,6 +2832,7 @@ Released 2018-09-13
[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
@ -2905,6 +2965,7 @@ Released 2018-09-13
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors

View file

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
categories = ["development-tools", "development-tools::cargo-plugins"]
build = "build.rs"
edition = "2018"
edition = "2021"
publish = false
[[bin]]

View file

@ -18,7 +18,7 @@ You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_dev"
version = "0.0.1"
edition = "2018"
edition = "2021"
[dependencies]
bytecount = "0.6"

View file

@ -1,7 +1,6 @@
//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
@ -10,17 +9,9 @@ use walkdir::WalkDir;
use crate::clippy_project_root;
// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
Some(v) => v.into(),
None => env::current_dir().unwrap().join("target"),
});
static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
let mut path = PathBuf::from(&**CARGO_TARGET_DIR);
path.push(profile);
path.push("cargo-clippy");
let mut path = std::env::current_exe().unwrap();
path.set_file_name("cargo-clippy");
fs::metadata(path).ok()?.modified().ok()
});
@ -94,10 +85,7 @@ fn updated_since_clippy_build(path: &Path) -> Option<bool> {
}
fn build_dir() -> PathBuf {
let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
let mut path = PathBuf::new();
path.push(CARGO_TARGET_DIR.clone());
path.push(profile);
path.push("test_build_base");
let mut path = std::env::current_exe().unwrap();
path.set_file_name("test");
path
}

View file

@ -6,7 +6,7 @@ repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
license = "MIT OR Apache-2.0"
keywords = ["clippy", "lint", "plugin"]
edition = "2018"
edition = "2021"
[dependencies]
cargo_metadata = "0.12"

View file

@ -2,10 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
use rustc_hir::{
def::{DefKind, Res},
Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath,
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeFoldable;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@ -68,6 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref),
items: [child],
self_ty,
..
}) = item.kind;
if let attrs = cx.tcx.hir().attrs(item.hir_id());
@ -80,9 +80,18 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
then {
if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) {
return;
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
for arg in a.args {
if !matches!(arg, GenericArg::Lifetime(_)) {
return;
}
}
}
}
let should_emit = match remove_blocks(func_expr).kind {
ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),

View file

@ -1,33 +1,44 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fn_def_id;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
use rustc_hir::{def::Res, def_id::DefIdMap, Crate, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
use crate::utils::conf;
declare_clippy_lint! {
/// ### What it does
/// Denies the configured methods and functions in clippy.toml
///
/// ### Why is this bad?
/// Some methods are undesirable in certain contexts,
/// and it's beneficial to lint for them as needed.
/// Some methods are undesirable in certain contexts, and it's beneficial to
/// lint for them as needed.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
/// disallowed-methods = [
/// # Can use a string as the path of the disallowed method.
/// "std::boxed::Box::new",
/// # Can also use an inline table with a `path` key.
/// { path = "std::time::Instant::now" },
/// # When using an inline table, can add a `reason` for why the method
/// # is disallowed.
/// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
/// ]
/// ```
///
/// ```rust,ignore
/// // Example code where clippy issues a warning
/// let xs = vec![1, 2, 3, 4];
/// xs.leak(); // Vec::leak is disallowed in the config.
/// // The diagnostic contains the message "no leaking memory".
///
/// let _now = Instant::now(); // Instant::now is disallowed in the config.
///
/// let _box = Box::new(3); // Box::new is disallowed in the config.
/// ```
///
/// Use instead:
@ -43,18 +54,15 @@ declare_clippy_lint! {
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
conf_disallowed: Vec<conf::DisallowedMethod>,
disallowed: DefIdMap<Option<String>>,
}
impl DisallowedMethod {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
conf_disallowed,
disallowed: DefIdMap::default(),
}
}
}
@ -63,32 +71,36 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for path in &self.disallowed {
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
{
self.def_ids.insert((id, path.clone()));
for conf in &self.conf_disallowed {
let (path, reason) = match conf {
conf::DisallowedMethod::Simple(path) => (path, None),
conf::DisallowedMethod::WithReason { path, reason } => (
path,
reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
),
};
let segs: Vec<_> = path.split("::").collect();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
self.disallowed.insert(id, reason);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(def_id) = fn_def_id(cx, expr) {
if self.def_ids.iter().any(|(id, _)| def_id == *id) {
let func_path = cx.get_def_path(def_id);
let func_path_string = func_path
.into_iter()
.map(Symbol::to_ident_string)
.collect::<Vec<_>>()
.join("::");
span_lint(
cx,
DISALLOWED_METHOD,
expr.span,
&format!("use of a disallowed method `{}`", func_path_string),
);
let def_id = match fn_def_id(cx, expr) {
Some(def_id) => def_id,
None => return,
};
let reason = match self.disallowed.get(&def_id) {
Some(reason) => reason,
None => return,
};
let func_path = cx.tcx.def_path_str(def_id);
let msg = format!("use of a disallowed method `{}`", func_path);
span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
if let Some(reason) = reason {
diag.note(reason);
}
}
});
}
}

View file

@ -48,7 +48,7 @@ impl DisallowedType {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
prim_tys: FxHashSet::default(),

View file

@ -91,8 +91,11 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
if trait_item.id.hir_id() == hir_id {
// be sure we have `self` parameter in this function
if let AssocItemKind::Fn { has_self: true } = trait_item.kind {
trait_self_ty =
Some(TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id()).self_ty().skip_binder());
trait_self_ty = Some(
TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id())
.self_ty()
.skip_binder(),
);
}
}
}

View file

@ -1,16 +1,16 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
use clippy_utils::usage::UsedAfterExprVisitor;
use clippy_utils::{get_enclosing_loop_or_closure, higher};
use clippy_utils::{is_adjusted, iter_input_pats};
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, ClosureKind, Ty};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@ -52,12 +52,6 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// It's unnecessary to create the closure.
///
/// ### Known problems
/// [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),
/// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),
/// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)
///
///
/// ### Example
/// ```rust,ignore
/// Some('a').map(|s| s.to_uppercase());
@ -75,32 +69,16 @@ declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_MET
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
if expr.span.from_expansion() {
return;
}
match expr.kind {
ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
for arg in args {
// skip `foo(macro!())`
if arg.span.ctxt() == expr.span.ctxt() {
check_closure(cx, arg);
}
}
},
_ => (),
}
}
}
fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind {
let body = cx.tcx.hir().body(eid);
let ex = &body.value;
if ex.span.ctxt() != expr.span.ctxt() {
if decl.inputs.is_empty() {
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
let body = match expr.kind {
ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
_ => return,
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
@ -117,33 +95,30 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
return;
}
let closure_ty = cx.typeck_results().expr_ty(expr);
if_chain!(
if let ExprKind::Call(caller, args) = ex.kind;
if let ExprKind::Path(_) = caller.kind;
// Not the same number of arguments, there is no way the closure is the same as the function return;
if args.len() == decl.inputs.len();
// Are the expression or the arguments type-adjusted? Then we need the closure
if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg)));
let fn_ty = cx.typeck_results().expr_ty(caller);
if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _));
if !type_is_unsafe_function(cx, fn_ty);
if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
if let ExprKind::Call(callee, args) = body.value.kind;
if let ExprKind::Path(_) = callee.kind;
if check_inputs(cx, body.params, args);
let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
.map_or(callee_ty, |id| cx.tcx.type_of(id));
if check_sig(cx, closure_ty, call_ty);
let substs = cx.typeck_results().node_substs(callee.hir_id);
// This fixes some false positives that I don't entirely understand
if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
// A type param function ref like `T::f` is not 'static, however
// it is if cast like `T::f as fn()`. This seems like a rustc bug.
if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, caller.span) {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if_chain! {
if let ty::Closure(_, substs) = fn_ty.kind();
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
if let ClosureKind::FnMut = substs.as_closure().kind();
if UsedAfterExprVisitor::is_found(cx, caller)
|| get_enclosing_loop_or_closure(cx.tcx, expr).is_some();
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
|| UsedAfterExprVisitor::is_found(cx, callee);
then {
// Mutable closure is used after current expr; we cannot consume it.
@ -162,110 +137,79 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
);
if_chain!(
if let ExprKind::MethodCall(path, _, args, _) = ex.kind;
// Not the same number of arguments, there is no way the closure is the same as the function return;
if args.len() == decl.inputs.len();
// Are the expression or the arguments type-adjusted? Then we need the closure
if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg)));
let method_def_id = cx.typeck_results().type_dependent_def_id(ex.hir_id).unwrap();
if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id));
if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter());
if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]);
if let ExprKind::MethodCall(path, _, args, _) = body.value.kind;
if check_inputs(cx, body.params, args);
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(body.value.hir_id);
let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
if check_sig(cx, closure_ty, call_ty);
then {
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
expr.span,
"redundant closure",
"replace the closure with the method itself",
format!("{}::{}", name, path.ident.name),
Applicability::MachineApplicable,
);
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
let name = get_ufcs_type_name(cx, method_def_id);
diag.span_suggestion(
expr.span,
"replace the closure with the method itself",
format!("{}::{}", name, path.ident.name),
Applicability::MachineApplicable,
);
})
}
);
}
}
/// Tries to determine the type for universal function call to be used instead of the closure
fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option<String> {
let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0];
let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id);
if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
if match_borrow_depth(expected_type_of_self, actual_type_of_self)
&& implements_trait(cx, actual_type_of_self, trait_id, &[])
{
return Some(cx.tcx.def_path_str(trait_id));
}
fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
if params.len() != call_args.len() {
return false;
}
cx.tcx.impl_of_method(method_def_id).and_then(|_| {
//a type may implicitly implement other type's methods (e.g. Deref)
if match_types(expected_type_of_self, actual_type_of_self) {
Some(get_type_name(cx, actual_type_of_self))
} else {
None
std::iter::zip(params, call_args).all(|(param, arg)| {
match param.pat.kind {
PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
_ => return false,
}
match *cx.typeck_results().expr_adjustments(arg) {
[] => true,
[Adjustment {
kind: Adjust::Deref(None),
..
}, Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
..
}] => {
// re-borrow with the same mutability is allowed
let ty = cx.typeck_results().expr_ty(arg);
matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
},
_ => false,
}
})
}
fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
match (&lhs.kind(), &rhs.kind()) {
(ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2),
(l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))),
fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
let call_sig = call_ty.fn_sig(cx.tcx);
if call_sig.unsafety() == Unsafety::Unsafe {
return false;
}
if !closure_ty.has_late_bound_regions() {
return true;
}
let substs = match closure_ty.kind() {
ty::Closure(_, substs) => substs,
_ => return false,
};
let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
}
fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool {
match (&lhs.kind(), &rhs.kind()) {
(ty::Bool, ty::Bool)
| (ty::Char, ty::Char)
| (ty::Int(_), ty::Int(_))
| (ty::Uint(_), ty::Uint(_))
| (ty::Str, ty::Str) => true,
(ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2),
(ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2),
(ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2,
(_, _) => false,
}
}
fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String {
match ty.kind() {
ty::Adt(t, _) => cx.tcx.def_path_str(t.did),
ty::Ref(_, r, _) => get_type_name(cx, r),
_ => ty.to_string(),
}
}
fn compare_inputs(
closure_inputs: &mut dyn Iterator<Item = &Param<'_>>,
call_args: &mut dyn Iterator<Item = &Expr<'_>>,
) -> bool {
for (closure_input, function_arg) in closure_inputs.zip(call_args) {
if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind {
// XXXManishearth Should I be checking the binding mode here?
if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind {
if p.segments.len() != 1 {
// If it's a proper path, it can't be a local variable
return false;
}
if p.segments[0].ident.name != ident.name {
// The two idents should be the same
return false;
}
} else {
return false;
fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
match cx.tcx.associated_item(method_def_id).container {
ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
ty::ImplContainer(def_id) => {
let ty = cx.tcx.type_of(def_id);
match ty.kind() {
ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
_ => ty.to_string(),
}
} else {
return false;
}
},
}
true
}

View file

@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
Applicability::MachineApplicable,
);
}
} else if digits > max as usize && sym_str != float_str {
} else if digits > max as usize && float_str.len() < sym_str.len() {
span_lint_and_sugg(
cx,
EXCESSIVE_PRECISION,

View file

@ -69,8 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
ty::Str => true,
_ => false,
};
if format_args.args.iter().all(|e| is_display_arg(e));
if format_args.fmt_expr.map_or(true, |e| check_unformatted(e));
if format_args.args.iter().all(is_display_arg);
if format_args.fmt_expr.map_or(true, check_unformatted);
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,

View file

@ -286,34 +286,39 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
}
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
if !differing_macro_contexts(first.span, second.span)
&& !first.span.from_expansion()
&& is_if(first)
&& (is_block(second) || is_if(second))
{
// where the else would be
if_chain! {
if !differing_macro_contexts(first.span, second.span);
if !first.span.from_expansion();
if let ExprKind::If(cond_expr, ..) = &first.kind;
if is_block(second) || is_if(second);
// Proc-macros can give weird spans. Make sure this is actually an `if`.
if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
if if_snip.starts_with("if");
// If there is a line break between the two expressions, don't lint.
// If there is a non-whitespace character, this span came from a proc-macro.
let else_span = first.span.between(second.span);
if let Some(else_snippet) = snippet_opt(cx, else_span);
if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
then {
let (looks_like, next_thing) = if is_if(second) {
("an `else if`", "the second `if`")
} else {
("an `else {..}`", "the next block")
};
if let Some(else_snippet) = snippet_opt(cx, else_span) {
if !else_snippet.contains('\n') {
let (looks_like, next_thing) = if is_if(second) {
("an `else if`", "the second `if`")
} else {
("an `else {..}`", "the next block")
};
span_lint_and_note(
cx,
SUSPICIOUS_ELSE_FORMATTING,
else_span,
&format!("this looks like {} but the `else` is missing", looks_like),
None,
&format!(
"to remove this lint, add the missing `else` or add a new line before {}",
next_thing,
),
);
}
span_lint_and_note(
cx,
SUSPICIOUS_ELSE_FORMATTING,
else_span,
&format!("this looks like {} but the `else` is missing", looks_like),
None,
&format!(
"to remove this lint, add the missing `else` or add a new line before {}",
next_thing,
),
);
}
}
}

View file

@ -0,0 +1,97 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::PanicExpn;
use clippy_utils::is_expn_of;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Detects `if`-then-`panic!` that can be replaced with `assert!`.
///
/// ### Why is this bad?
/// `assert!` is simpler than `if`-then-`panic!`.
///
/// ### Example
/// ```rust
/// let sad_people: Vec<&str> = vec![];
/// if !sad_people.is_empty() {
/// panic!("there are sad people: {:?}", sad_people);
/// }
/// ```
/// Use instead:
/// ```rust
/// let sad_people: Vec<&str> = vec![];
/// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
/// ```
pub IF_THEN_PANIC,
style,
"`panic!` and only a `panic!` in `if`-then statement"
}
declare_lint_pass!(IfThenPanic => [IF_THEN_PANIC]);
impl LateLintPass<'_> for IfThenPanic {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let Expr {
kind: ExprKind:: If(cond, Expr {
kind: ExprKind::Block(
Block {
stmts: [stmt],
..
},
_),
..
}, None),
..
} = &expr;
if is_expn_of(stmt.span, "panic").is_some();
if !matches!(cond.kind, ExprKind::Let(_, _, _));
if let StmtKind::Semi(semi) = stmt.kind;
if !cx.tcx.sess.source_map().is_multiline(cond.span);
then {
let span = if let Some(panic_expn) = PanicExpn::parse(semi) {
match *panic_expn.format_args.value_args {
[] => panic_expn.format_args.format_string_span,
[.., last] => panic_expn.format_args.format_string_span.to(last.span),
}
} else {
if_chain! {
if let ExprKind::Block(block, _) = semi.kind;
if let Some(init) = block.expr;
if let ExprKind::Call(_, [format_args]) = init.kind;
then {
format_args.span
} else {
return
}
}
};
let mut applicability = Applicability::MachineApplicable;
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
let cond_sugg =
if let ExprKind::DropTemps(Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..}) = cond.kind {
snippet_with_applicability(cx, not_expr.span, "..", &mut applicability).to_string()
} else {
format!("!{}", snippet_with_applicability(cx, cond.span, "..", &mut applicability))
};
span_lint_and_sugg(
cx,
IF_THEN_PANIC,
expr.span,
"only a `panic!` in `if`-then statement",
"try",
format!("assert!({}, {});", cond_sugg, sugg),
Applicability::MachineApplicable,
);
}
}
}
}

View file

@ -0,0 +1,64 @@
use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
use rustc_hir::{ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
/// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`.
///
/// ### Why is this bad?
/// Methods named `iter` or `iter_mut` conventionally return an `Iterator`.
///
/// ### Example
/// ```rust
/// // `String` does not implement `Iterator`
/// struct Data {}
/// impl Data {
/// fn iter(&self) -> String {
/// todo!()
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// use std::str::Chars;
/// struct Data {}
/// impl Data {
/// fn iter(&self) -> Chars<'static> {
/// todo!()
/// }
/// }
/// ```
pub ITER_NOT_RETURNING_ITERATOR,
pedantic,
"methods named `iter` or `iter_mut` that do not return an `Iterator`"
}
declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
impl LateLintPass<'_> for IterNotReturningIterator {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
let name: &str = &impl_item.ident.name.as_str();
if_chain! {
if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
let ret_ty = return_ty(cx, impl_item.hir_id());
if matches!(name, "iter" | "iter_mut");
if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
if param.name == kw::SelfLower;
if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
then {
span_lint(
cx,
ITER_NOT_RETURNING_ITERATOR,
fn_sig.span,
&format!("this method is named `{}` but its return type does not implement `Iterator`", name),
);
}
}
}
}

View file

@ -225,8 +225,8 @@ mod future_not_send;
mod get_last_with_len;
mod identity_op;
mod if_let_mutex;
mod if_let_some_result;
mod if_not_else;
mod if_then_panic;
mod if_then_some_else_none;
mod implicit_hasher;
mod implicit_return;
@ -241,6 +241,7 @@ mod int_plus_one;
mod integer_division;
mod invalid_upcast_comparisons;
mod items_after_statements;
mod iter_not_returning_iterator;
mod large_const_arrays;
mod large_enum_variant;
mod large_stack_arrays;
@ -262,6 +263,7 @@ mod map_clone;
mod map_err_ignore;
mod map_unit_fn;
mod match_on_vec_items;
mod match_result_ok;
mod matches;
mod mem_discriminant;
mod mem_forget;
@ -330,6 +332,7 @@ mod reference;
mod regex;
mod repeat_once;
mod returns;
mod same_name_method;
mod self_assignment;
mod self_named_constructors;
mod semicolon_if_nothing_returned;
@ -655,8 +658,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
get_last_with_len::GET_LAST_WITH_LEN,
identity_op::IDENTITY_OP,
if_let_mutex::IF_LET_MUTEX,
if_let_some_result::IF_LET_SOME_RESULT,
if_not_else::IF_NOT_ELSE,
if_then_panic::IF_THEN_PANIC,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
implicit_hasher::IMPLICIT_HASHER,
implicit_return::IMPLICIT_RETURN,
@ -674,6 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
integer_division::INTEGER_DIVISION,
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
items_after_statements::ITEMS_AFTER_STATEMENTS,
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
large_const_arrays::LARGE_CONST_ARRAYS,
large_enum_variant::LARGE_ENUM_VARIANT,
large_stack_arrays::LARGE_STACK_ARRAYS,
@ -723,6 +727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS,
match_result_ok::MATCH_RESULT_OK,
matches::INFALLIBLE_DESTRUCTURING_MATCH,
matches::MATCH_AS_REF,
matches::MATCH_BOOL,
@ -908,6 +913,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
repeat_once::REPEAT_ONCE,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
same_name_method::SAME_NAME_METHOD,
self_assignment::SELF_ASSIGNMENT,
self_named_constructors::SELF_NAMED_CONSTRUCTORS,
semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
@ -952,7 +958,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
transmuting_null::TRANSMUTING_NULL,
try_err::TRY_ERR,
types::BORROWED_BOX,
types::BOX_VEC,
types::BOX_COLLECTION,
types::LINKEDLIST,
types::OPTION_OPTION,
types::RC_BUFFER,
@ -1051,6 +1057,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(panic_unimplemented::UNIMPLEMENTED),
LintId::of(panic_unimplemented::UNREACHABLE),
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
LintId::of(same_name_method::SAME_NAME_METHOD),
LintId::of(shadow::SHADOW_REUSE),
LintId::of(shadow::SHADOW_SAME),
LintId::of(strings::STRING_ADD),
@ -1104,6 +1111,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
LintId::of(let_underscore::LET_UNDERSCORE_DROP),
LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
@ -1126,6 +1134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
LintId::of(mut_mut::MUT_MUT),
@ -1134,6 +1143,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(needless_continue::NEEDLESS_CONTINUE),
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_expressive_names::SIMILAR_NAMES),
LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
@ -1249,7 +1259,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
LintId::of(identity_op::IDENTITY_OP),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
@ -1292,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
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),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
@ -1358,7 +1369,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(minmax::MIN_MAX),
LintId::of(misc::CMP_NAN),
LintId::of(misc::CMP_OWNED),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc::TOPLEVEL_REF_ARG),
@ -1390,7 +1400,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
@ -1448,7 +1457,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(try_err::TRY_ERR),
LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_VEC),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
@ -1504,7 +1513,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(if_then_panic::IF_THEN_PANIC),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
@ -1520,6 +1529,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(manual_map::MANUAL_MAP),
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::INFALLIBLE_DESTRUCTURING_MATCH),
LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
LintId::of(matches::MATCH_OVERLAPPING_ARM),
@ -1564,7 +1574,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
LintId::of(ptr::CMP_NULL),
LintId::of(ptr::PTR_ARG),
LintId::of(ptr_eq::PTR_EQ),
@ -1724,7 +1733,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(misc::CMP_NAN),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::MODULO_ONE),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
@ -1787,7 +1795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(types::BOX_VEC),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
@ -1920,6 +1928,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
store.register_late_pass(|| Box::new(map_clone::MapClone));
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow));
@ -1952,7 +1961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
store.register_late_pass(|| Box::new(regex::Regex::default()));
store.register_late_pass(|| Box::new(regex::Regex));
store.register_late_pass(|| Box::new(copies::CopyAndPaste));
store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
store.register_late_pass(|| Box::new(format::UselessFormat));
@ -1976,7 +1985,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
store.register_late_pass(|| Box::new(missing_inline::MissingInline));
store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
store.register_late_pass(|| Box::new(if_let_some_result::OkIfLet));
store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
let enum_variant_size_threshold = conf.enum_variant_size_threshold;
@ -2105,8 +2114,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
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.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods)));
let disallowed_methods = conf.disallowed_methods.clone();
store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
@ -2131,6 +2140,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
store.register_late_pass(move || Box::new(feature_name::FeatureName));
store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
}
#[rustfmt::skip]
@ -2186,6 +2197,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
ls.register_renamed("clippy::box_vec", "clippy::box_collection");
ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
@ -2200,6 +2212,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
// uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value");

View file

@ -10,12 +10,7 @@ use rustc_middle::ty;
use rustc_span::sym;
/// Checks for the `FOR_KV_MAP` lint.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
) {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
let pat_span = pat.span;
if let PatKind::Tuple(pat, _) = pat.kind {

View file

@ -65,7 +65,7 @@ fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
_ => false,
}
}

View file

@ -8,7 +8,7 @@ use clippy_utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::{symbol::sym, Span, Symbol};
@ -47,13 +47,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
// afterwards a mutable borrow of a field isn't necessary.
let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
if cx.typeck_results().node_type(iter_expr.hir_id).ref_mutability() == Some(Mutability::Mut) {
// Reborrow for mutable references. It may not be possible to get a mutable reference here.
"&mut *"
} else {
"&mut "
}
let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
".by_ref()"
} else {
""
};
@ -65,7 +60,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
expr.span.with_hi(scrutinee_expr.span.hi()),
"this loop could be written as a `for` loop",
"try",
format!("for {} in {}{}", loop_var, ref_mut, iterator),
format!("for {} in {}{}", loop_var, iterator, by_ref),
applicability,
);
}
@ -74,8 +69,6 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
struct IterExpr {
/// The span of the whole expression, not just the path and fields stored here.
span: Span,
/// The HIR id of the whole expression, not just the path and fields stored here.
hir_id: HirId,
/// The fields used, in order of child to parent.
fields: Vec<Symbol>,
/// The path being used.
@ -86,14 +79,12 @@ struct IterExpr {
/// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
let span = e.span;
let hir_id = e.hir_id;
let mut fields = Vec::new();
loop {
match e.kind {
ExprKind::Path(ref path) => {
break Some(IterExpr {
span,
hir_id,
fields,
path: cx.qpath_res(path, e.hir_id),
});

View file

@ -63,29 +63,24 @@ impl MacroUseImports {
fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) {
let call_site = span.source_callsite();
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
if let Some(_callee) = span.source_callee() {
if !self.collected.contains(&call_site) {
let name = if name.contains("::") {
name.split("::").last().unwrap().to_string()
} else {
name.to_string()
};
if span.source_callee().is_some() && !self.collected.contains(&call_site) {
let name = if name.contains("::") {
name.split("::").last().unwrap().to_string()
} else {
name.to_string()
};
self.mac_refs.push(MacroRefData::new(name));
self.collected.insert(call_site);
}
self.mac_refs.push(MacroRefData::new(name));
self.collected.insert(call_site);
}
}
fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) {
let call_site = span.source_callsite();
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
if let Some(_callee) = span.source_callee() {
if !self.collected.contains(&call_site) {
self.mac_refs
.push(MacroRefData::new(name.to_string()));
self.collected.insert(call_site);
}
if span.source_callee().is_some() && !self.collected.contains(&call_site) {
self.mac_refs.push(MacroRefData::new(name.to_string()));
self.collected.insert(call_site);
}
}
}

View file

@ -12,40 +12,51 @@ use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
///* Checks for unnecessary `ok()` in if let.
/// Checks for unnecessary `ok()` in `while let`.
///
/// ### Why is this bad?
/// Calling `ok()` in if let is unnecessary, instead match
/// Calling `ok()` in `while let` is unnecessary, instead match
/// on `Ok(pat)`
///
/// ### Example
/// ```ignore
/// for i in iter {
/// if let Some(value) = i.parse().ok() {
/// vec.push(value)
/// }
/// while let Some(value) = iter.next().ok() {
/// vec.push(value)
/// }
/// ```
/// Could be written:
///
/// ```ignore
/// for i in iter {
/// if let Ok(value) = i.parse() {
/// vec.push(value)
/// }
/// if let Some(valie) = iter.next().ok() {
/// vec.push(value)
/// }
/// ```
pub IF_LET_SOME_RESULT,
/// Use instead:
/// ```ignore
/// while let Ok(value) = iter.next() {
/// vec.push(value)
/// }
///
/// if let Ok(value) = iter.next() {
/// vec.push_value)
/// }
/// ```
pub MATCH_RESULT_OK,
style,
"usage of `ok()` in `if let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
"usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
}
declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
impl<'tcx> LateLintPass<'tcx> for OkIfLet {
impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { //begin checking variables
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr);
let (let_pat, let_expr, ifwhile) =
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
(let_pat, let_expr, "if")
} else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
(let_pat, let_expr, "while")
} else {
return;
};
if_chain! {
if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
@ -53,17 +64,19 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
then {
let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
let sugg = format!(
"if let Ok({}) = {}",
"{} let Ok({}) = {}",
ifwhile,
some_expr_string,
trimmed_ok.trim().trim_end_matches('.'),
);
span_lint_and_sugg(
cx,
IF_LET_SOME_RESULT,
MATCH_RESULT_OK,
expr.span.with_hi(let_expr.span.hi()),
"matching on `Some` with `ok()` is redundant",
&format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),

View file

@ -183,8 +183,8 @@ declare_clippy_lint! {
/// ```rust
/// let x = 5;
/// match x {
/// 1...10 => println!("1 ... 10"),
/// 5...15 => println!("5 ... 15"),
/// 1..=10 => println!("1 ... 10"),
/// 5..=15 => println!("5 ... 15"),
/// _ => (),
/// }
/// ```

View file

@ -17,32 +17,25 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
}
let ctxt = expr.span.ctxt();
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
let (method_name, msg, reverse) = if method_name == "splitn" {
("split_once", "manual implementation of `split_once`", false)
} else {
("rsplit_once", "manual implementation of `rsplit_once`", true)
};
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
Some(x) => x,
None => return,
};
let (method_name, msg) = if method_name == "splitn" {
("split_once", "manual implementation of `split_once`")
} else {
("rsplit_once", "manual implementation of `rsplit_once`")
};
let mut app = Applicability::MachineApplicable;
let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
match usage.kind {
let sugg = match usage.kind {
IterUsageKind::NextTuple => {
span_lint_and_sugg(
cx,
MANUAL_SPLIT_ONCE,
usage.span,
msg,
"try this",
format!("{}.{}({})", self_snip, method_name, pat_snip),
app,
);
format!("{}.{}({})", self_snip, method_name, pat_snip)
},
IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
IterUsageKind::Next => {
let self_deref = {
let adjust = cx.typeck_results().expr_adjustments(self_arg);
@ -58,7 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
"*".repeat(adjust.len() - 2)
}
};
let sugg = if usage.unwrap_kind.is_some() {
if usage.unwrap_kind.is_some() {
format!(
"{}.{}({}).map_or({}{}, |x| x.0)",
&self_snip, method_name, pat_snip, self_deref, &self_snip
@ -68,9 +61,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
"Some({}.{}({}).map_or({}{}, |x| x.0))",
&self_snip, method_name, pat_snip, self_deref, &self_snip
)
};
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
}
},
IterUsageKind::Second => {
let access_str = match usage.unwrap_kind {
@ -78,23 +69,18 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
Some(UnwrapKind::QuestionMark) => "?.1",
None => ".map(|x| x.1)",
};
span_lint_and_sugg(
cx,
MANUAL_SPLIT_ONCE,
usage.span,
msg,
"try this",
format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
app,
);
format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
},
}
};
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
}
enum IterUsageKind {
Next,
Second,
NextTuple,
RNextTuple,
}
enum UnwrapKind {
@ -108,10 +94,12 @@ struct IterUsage {
span: Span,
}
#[allow(clippy::too_many_lines)]
fn parse_iter_usage(
cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
reverse: bool,
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
@ -124,20 +112,30 @@ fn parse_iter_usage(
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
match (&*name.ident.as_str(), args) {
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
if reverse {
(IterUsageKind::Second, e.span)
} else {
(IterUsageKind::Next, e.span)
}
},
("next_tuple", []) => {
if_chain! {
return if_chain! {
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
if let ty::Tuple(subs) = subs.type_at(0).kind();
if subs.len() == 2;
then {
return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
Some(IterUsage {
kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
span: e.span,
unwrap_kind: None
})
} else {
return None;
None
}
}
};
},
("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
@ -158,7 +156,7 @@ fn parse_iter_usage(
}
}
};
match idx {
match if reverse { idx ^ 1 } else { idx } {
0 => (IterUsageKind::Next, span),
1 => (IterUsageKind::Second, span),
_ => return None,

View file

@ -264,6 +264,8 @@ declare_clippy_lint! {
/// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
/// (see e.g. the `std::string::ToString` trait).
///
/// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
///
/// Please find more info here:
/// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
///

View file

@ -113,7 +113,7 @@ declare_clippy_lint! {
/// if (y - x).abs() > error_margin { }
/// ```
pub FLOAT_CMP,
correctness,
pedantic,
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
}

View file

@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut};
use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::sym;
@ -19,10 +19,29 @@ declare_clippy_lint! {
/// so having types with interior mutability is a bad idea.
///
/// ### Known problems
/// It's correct to use a struct, that contains interior mutability
/// as a key, when its `Hash` implementation doesn't access any of the interior mutable types.
/// However, this lint is unable to recognize this, so it causes a false positive in theses cases.
/// The `bytes` crate is a great example of this.
///
/// #### False Positives
/// It's correct to use a struct that contains interior mutability as a key, when its
/// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
/// However, this lint is unable to recognize this, so it will often cause false positives in
/// theses cases. The `bytes` crate is a great example of this.
///
/// #### False Negatives
/// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
/// indirection. For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
/// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
///
/// This lint does check a few cases for indirection. Firstly, using some standard library
/// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
/// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
/// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
/// contained type.
///
/// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
/// apply only to the **address** of the contained value. Therefore, interior mutability
/// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
/// or `Ord`, and therefore will not trigger this link. For more info, see issue
/// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
///
/// ### Example
/// ```rust
@ -103,30 +122,52 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
let ty = ty.peel_refs();
if let Adt(def, substs) = ty.kind() {
if [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
let is_keyed_type = [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeSet]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did))
&& is_mutable_type(cx, substs.type_at(0), span)
{
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
}
}
}
fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
/// Determines if a type contains interior mutability which would affect its implementation of
/// [`Hash`] or [`Ord`].
fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
match *ty.kind() {
RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
},
Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span),
Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span),
Array(inner_ty, size) => {
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
&& is_interior_mutable_type(cx, inner_ty, span)
},
Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
Adt(..) => {
!ty.has_escaping_bound_vars()
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx.at(span), cx.param_env)
Tuple(..) => ty.tuple_fields().any(|ty| is_interior_mutable_type(cx, ty, span)),
Adt(def, substs) => {
// Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
// that of their type parameters. Note: we don't include `HashSet` and `HashMap`
// because they have no impl for `Hash` or `Ord`.
let is_std_collection = [
sym::option_type,
sym::result_type,
sym::LinkedList,
sym::vec_type,
sym::vecdeque_type,
sym::BTreeMap,
sym::BTreeSet,
sym::Rc,
sym::Arc,
]
.iter()
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
let is_box = Some(def.did) == cx.tcx.lang_items().owned_box();
if is_std_collection || is_box {
// The type is mutable if any of its type parameters are
substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
} else {
!ty.has_escaping_bound_vars()
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
&& !ty.is_freeze(cx.tcx.at(span), cx.param_env)
}
},
_ => false,
}

View file

@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
if e.span.from_expansion() {
return;
}
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind {
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
if let [Adjustment {
@ -116,14 +116,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
..
}] = *adj3
{
let help_msg_ty = if matches!(mutability, Mutability::Not) {
format!("&{}", ty)
} else {
format!("&mut {}", ty)
};
span_lint_and_then(
cx,
NEEDLESS_BORROW,
e.span,
&format!(
"this expression borrows a reference (`&{}`) that is immediately dereferenced \
"this expression borrows a reference (`{}`) that is immediately dereferenced \
by the compiler",
ty
help_msg_ty
),
|diag| {
if let Some(snippet) = snippet_opt(cx, inner.span) {

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
/// let (a, b, c, d, e, f, g) = (...);
/// ```
pub MANY_SINGLE_CHAR_NAMES,
style,
pedantic,
"too many single character bindings"
}

View file

@ -372,7 +372,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
for (_, ref mutbl, ref argspan) in decl
.inputs
.iter()
.filter_map(|ty| get_rptr_lm(ty))
.filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name)
{
if *mutbl == Mutability::Mut {

View file

@ -5,7 +5,7 @@ use if_chain::if_chain;
use rustc_ast::ast::{LitKind, StrStyle};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
use std::convert::TryFrom;
@ -51,10 +51,7 @@ declare_clippy_lint! {
"trivial regular expressions"
}
#[derive(Clone, Default)]
pub struct Regex {}
impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
impl<'tcx> LateLintPass<'tcx> for Regex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {

View file

@ -11,6 +11,7 @@ use rustc_middle::hir::map::Map;
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::hygiene::DesugaringKind;
use rustc_span::source_map::Span;
use rustc_span::sym;
@ -199,7 +200,9 @@ fn check_final_expr<'tcx>(
check_block_return(cx, ifblock);
}
if let Some(else_clause) = else_clause_opt {
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
}
}
},
// a match expr, check all arms

View file

@ -0,0 +1,160 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Crate, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::AssocKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
use std::collections::{BTreeMap, BTreeSet};
declare_clippy_lint! {
/// ### What it does
/// It lints if a struct has two method with same time:
/// one from a trait, another not from trait.
///
/// ### Why is this bad?
/// Confusing.
///
/// ### Example
/// ```rust
/// trait T {
/// fn foo(&self) {}
/// }
///
/// struct S;
///
/// impl T for S {
/// fn foo(&self) {}
/// }
///
/// impl S {
/// fn foo(&self) {}
/// }
/// ```
pub SAME_NAME_METHOD,
restriction,
"two method with same name"
}
declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
struct ExistingName {
impl_methods: BTreeMap<Symbol, Span>,
trait_methods: BTreeMap<Symbol, Vec<Span>>,
}
impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'tcx>) {
let mut map = FxHashMap::<Res, ExistingName>::default();
for item in krate.items() {
if let ItemKind::Impl(Impl {
items,
of_trait,
self_ty,
..
}) = &item.kind
{
if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
if !map.contains_key(res) {
map.insert(
*res,
ExistingName {
impl_methods: BTreeMap::new(),
trait_methods: BTreeMap::new(),
},
);
}
let existing_name = map.get_mut(res).unwrap();
match of_trait {
Some(trait_ref) => {
let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
if let Some(Node::TraitRef(TraitRef { path, .. })) =
cx.tcx.hir().find(trait_ref.hir_ref_id);
if let Res::Def(DefKind::Trait, did) = path.res;
then{
// FIXME: if
// `rustc_middle::ty::assoc::AssocItems::items` is public,
// we can iterate its keys instead of `in_definition_order`,
// which's more efficient
cx.tcx
.associated_items(did)
.in_definition_order()
.filter(|assoc_item| {
matches!(assoc_item.kind, AssocKind::Fn)
})
.map(|assoc_item| assoc_item.ident.name)
.collect()
}else{
BTreeSet::new()
}
};
let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
span_lint_and_then(
cx,
SAME_NAME_METHOD,
*impl_span,
"method's name is same to an existing method in a trait",
|diag| {
diag.span_note(
trait_method_span,
&format!("existing `{}` defined here", method_name),
);
},
);
}
if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
v.push(trait_method_span);
} else {
existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
}
};
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
}) {
let method_name = impl_item_ref.ident.name;
methods_in_trait.remove(&method_name);
check_trait_method(method_name, impl_item_ref.span);
}
for method_name in methods_in_trait {
check_trait_method(method_name, item.span);
}
},
None => {
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
}) {
let method_name = impl_item_ref.ident.name;
let impl_span = impl_item_ref.span;
if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
span_lint_and_then(
cx,
SAME_NAME_METHOD,
impl_span,
"method's name is same to an existing method in a trait",
|diag| {
// TODO should we `span_note` on every trait?
// iterate on trait_spans?
diag.span_note(
trait_spans[0],
&format!("existing `{}` defined here", method_name),
);
},
);
}
existing_name.impl_methods.insert(method_name, impl_span);
}
},
}
}
}
}
}
}

View file

@ -0,0 +1,50 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_ty_param_diagnostic_item;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::BOX_COLLECTION;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
if_chain! {
if Some(def_id) == cx.tcx.lang_items().owned_box();
if let Some(item_type) = get_std_collection(cx, qpath);
then {
let generic = if item_type == "String" {
""
} else {
"<..>"
};
span_lint_and_help(
cx,
BOX_COLLECTION,
hir_ty.span,
&format!(
"you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
outer=item_type,
generic = generic),
None,
&format!(
"`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
outer=item_type,
generic = generic)
);
true
} else {
false
}
}
}
fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
if is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some() {
Some("Vec")
} else if is_ty_param_diagnostic_item(cx, qpath, sym::string_type).is_some() {
Some("String")
} else if is_ty_param_diagnostic_item(cx, qpath, sym::hashmap_type).is_some() {
Some("HashMap")
} else {
None
}
}

View file

@ -1,25 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_ty_param_diagnostic_item;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::BOX_VEC;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
if Some(def_id) == cx.tcx.lang_items().owned_box()
&& is_ty_param_diagnostic_item(cx, qpath, sym::vec_type).is_some()
{
span_lint_and_help(
cx,
BOX_VEC,
hir_ty.span,
"you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
None,
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation",
);
true
} else {
false
}
}

View file

@ -1,5 +1,5 @@
mod borrowed_box;
mod box_vec;
mod box_collection;
mod linked_list;
mod option_option;
mod rc_buffer;
@ -21,12 +21,12 @@ use rustc_span::source_map::Span;
declare_clippy_lint! {
/// ### What it does
/// Checks for use of `Box<Vec<_>>` anywhere in the code.
/// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
/// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
///
/// ### Why is this bad?
/// `Vec` already keeps its contents in a separate area on
/// the heap. So if you `Box` it, you just add another level of indirection
/// Collections already keeps their contents in a separate area on
/// the heap. So if you `Box` them, you just add another level of indirection
/// without any benefit whatsoever.
///
/// ### Example
@ -43,7 +43,7 @@ declare_clippy_lint! {
/// values: Vec<Foo>,
/// }
/// ```
pub BOX_VEC,
pub BOX_COLLECTION,
perf,
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
}
@ -298,7 +298,7 @@ pub struct Types {
avoid_breaking_exported_api: bool,
}
impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl<'tcx> LateLintPass<'tcx> for Types {
fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
@ -447,7 +447,7 @@ impl Types {
// in `clippy_lints::utils::conf.rs`
let mut triggered = false;
triggered |= box_vec::check(cx, hir_ty, qpath, def_id);
triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);

View file

@ -15,6 +15,14 @@ pub struct Rename {
pub rename: String,
}
/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum DisallowedMethod {
Simple(String),
WithReason { path: String, reason: Option<String> },
}
/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
@ -128,7 +136,7 @@ macro_rules! define_Conf {
}
define_Conf! {
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
@ -243,7 +251,7 @@ define_Conf! {
/// Lint: DISALLOWED_METHOD.
///
/// The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<String> = Vec::new()),
(disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
/// Lint: DISALLOWED_TYPE.
///
/// The list of disallowed types, written as fully qualified paths.

View file

@ -561,9 +561,7 @@ declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
impl EarlyLintPass for ProduceIce {
fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
if is_trigger_fn(fn_kind) {
panic!("Would you like some help with that?");
}
assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
}
}
@ -894,7 +892,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
}).collect();
if !check_path(cx, &path[..]);
then {
span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
span_lint(cx, INVALID_PATHS, item.span, "invalid path");
}
}
}
@ -1224,5 +1222,10 @@ fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: S
let sm = cx.sess().source_map();
let span = sm.span_extend_to_prev_str(span, "let", false);
let span = sm.span_extend_to_next_char(span, ';', false);
Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
Span::new(
span.lo() - BytePos(3),
span.hi() + BytePos(1),
span.ctxt(),
span.parent(),
)
}

View file

@ -298,6 +298,7 @@ pub struct ClippyConfiguration {
default: String,
lints: Vec<String>,
doc: String,
#[allow(dead_code)]
deprecation_reason: Option<&'static str>,
}

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_utils"
version = "0.1.57"
edition = "2018"
edition = "2021"
publish = false
[dependencies]

View file

@ -46,15 +46,12 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
| (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
},
(Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
lr == rr
&& eq_maybe_qself(lqself, rqself)
&& eq_path(lp, rp)
&& unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
},
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@ -76,7 +73,7 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
&& eq_pat(&l.pat, &r.pat)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
@ -92,7 +89,7 @@ pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
}
pub fn eq_path(l: &Path, r: &Path) -> bool {
over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r))
over(&l.segments, &r.segments, eq_path_seg)
}
pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
@ -101,9 +98,7 @@ pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
match (l, r) {
(GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => {
over(&l.args, &r.args, |l, r| eq_angle_arg(l, r))
},
(GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
(GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
},
@ -142,7 +137,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
use ExprKind::*;
if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) {
if !over(&l.attrs, &r.attrs, eq_attr) {
return false;
}
match (&l.kind, &r.kind) {
@ -173,20 +168,20 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)),
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
(Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => {
lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb)
},
(Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(Struct(lse), Struct(rse)) => {
eq_maybe_qself(&lse.qself, &rse.qself)
&& eq_path(&lse.path, &rse.path)
&& eq_struct_rest(&lse.rest, &rse.rest)
&& unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
&& unordered_over(&lse.fields, &rse.fields, eq_field)
},
_ => false,
}
@ -196,7 +191,7 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
&& eq_expr(&l.expr, &r.expr)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
@ -204,7 +199,7 @@ pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
&& eq_pat(&l.pat, &r.pat)
&& eq_expr(&l.body, &r.body)
&& eq_expr_opt(&l.guard, &r.guard)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
@ -212,7 +207,7 @@ pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
}
pub fn eq_block(l: &Block, r: &Block) -> bool {
l.rules == r.rules && over(&l.stmts, &r.stmts, |l, r| eq_stmt(l, r))
l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
}
pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
@ -222,13 +217,13 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
eq_pat(&l.pat, &r.pat)
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
&& eq_local_kind(&l.kind, &r.kind)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
},
(Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
(Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
(Empty, Empty) => true,
(MacCall(l), MacCall(r)) => {
l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
},
_ => false,
}
@ -245,10 +240,7 @@ pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
}
pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
eq_id(l.ident, r.ident)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& eq_vis(&l.vis, &r.vis)
&& eq_kind(&l.kind, &r.kind)
eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
}
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
@ -272,18 +264,15 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
}
},
(ForeignMod(l), ForeignMod(r)) => {
both(&l.abi, &r.abi, |l, r| eq_str_lit(l, r))
&& over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
},
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
&& over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
(Enum(le, lg), Enum(re, rg)) => {
over(&le.variants, &re.variants, |l, r| eq_variant(l, r)) && eq_generics(lg, rg)
},
(Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
(Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
eq_variant_data(lv, rv) && eq_generics(lg, rg)
},
@ -291,10 +280,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
la == ra
&& matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
&& eq_generics(lg, rg)
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
&& over(lb, rb, eq_generic_bound)
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
},
(TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, |l, r| eq_generic_bound(l, r)),
(TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
(
Impl(box ImplKind {
unsafety: lu,
@ -342,7 +331,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
&& over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@ -360,7 +349,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
(TyAlias(box TyAliasKind(ld, lg, lb, lt)), TyAlias(box TyAliasKind(rd, rg, rb, rt))) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, |l, r| eq_generic_bound(l, r))
&& over(lb, rb, eq_generic_bound)
&& both(lt, rt, |l, r| eq_ty(l, r))
},
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@ -370,7 +359,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
l.is_placeholder == r.is_placeholder
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
&& eq_vis(&l.vis, &r.vis)
&& eq_id(l.ident, r.ident)
&& eq_variant_data(&l.data, &r.data)
@ -381,14 +370,14 @@ pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
use VariantData::*;
match (l, r) {
(Unit(_), Unit(_)) => true,
(Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, |l, r| eq_struct_field(l, r)),
(Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
_ => false,
}
}
pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
l.is_placeholder == r.is_placeholder
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
&& eq_vis(&l.vis, &r.vis)
&& both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
&& eq_ty(&l.ty, &r.ty)
@ -406,7 +395,7 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
}
pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
over(&l.params, &r.params, |l, r| eq_generic_param(l, r))
over(&l.params, &r.params, eq_generic_param)
&& over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
eq_where_predicate(l, r)
})
@ -419,10 +408,10 @@ pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
eq_generic_param(l, r)
}) && eq_ty(&l.bounded_ty, &r.bounded_ty)
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
&& over(&l.bounds, &r.bounds, eq_generic_bound)
},
(RegionPredicate(l), RegionPredicate(r)) => {
eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
},
(EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
_ => false,
@ -469,7 +458,7 @@ pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
l.is_placeholder == r.is_placeholder
&& eq_pat(&l.pat, &r.pat)
&& eq_ty(&l.ty, &r.ty)
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
})
}
@ -496,13 +485,13 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
(BareFn(l), BareFn(r)) => {
l.unsafety == r.unsafety
&& eq_ext(&l.ext, &r.ext)
&& over(&l.generic_params, &r.generic_params, |l, r| eq_generic_param(l, r))
&& over(&l.generic_params, &r.generic_params, eq_generic_param)
&& eq_fn_decl(&l.decl, &r.decl)
},
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, |l, r| eq_generic_bound(l, r)),
(ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, |l, r| eq_generic_bound(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
(ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
(Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
_ => false,
@ -533,7 +522,7 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
use GenericParamKind::*;
l.is_placeholder == r.is_placeholder
&& eq_id(l.ident, r.ident)
&& over(&l.bounds, &r.bounds, |l, r| eq_generic_bound(l, r))
&& over(&l.bounds, &r.bounds, eq_generic_bound)
&& match (&l.kind, &r.kind) {
(Lifetime, Lifetime) => true,
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
@ -548,10 +537,10 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
kw_span: _,
default: rd,
},
) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
_ => false,
}
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
&& over(&l.attrs, &r.attrs, eq_attr)
}
pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
@ -568,7 +557,7 @@ pub fn eq_assoc_constraint(l: &AssocTyConstraint, r: &AssocTyConstraint) -> bool
eq_id(l.ident, r.ident)
&& match (&l.kind, &r.kind) {
(Equality { ty: l }, Equality { ty: r }) => eq_ty(l, r),
(Bound { bounds: l }, Bound { bounds: r }) => over(l, r, |l, r| eq_generic_bound(l, r)),
(Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
_ => false,
}
}

View file

@ -27,10 +27,9 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
ExprKind::Struct(_, fields, expr) => {
fields.iter().all(|f| identify_some_pure_patterns(f.expr))
&& expr.map_or(true, |e| identify_some_pure_patterns(e))
fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
},
ExprKind::Call(
&Expr {
@ -45,7 +44,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
..
},
args,
) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
) => args.iter().all(identify_some_pure_patterns),
ExprKind::Block(
&Block {
stmts,

View file

@ -602,3 +602,33 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
false
}
/// A parsed `panic!` expansion
pub struct PanicExpn<'tcx> {
/// Span of `panic!(..)`
pub call_site: Span,
/// Inner `format_args!` expansion
pub format_args: FormatArgsExpn<'tcx>,
}
impl PanicExpn<'tcx> {
/// Parses an expanded `panic!` invocation
pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
if_chain! {
if let ExprKind::Block(block, _) = expr.kind;
if let Some(init) = block.expr;
if let ExprKind::Call(_, [format_args]) = init.kind;
let expn_data = expr.span.ctxt().outer_expn_data();
if let ExprKind::AddrOf(_, _, format_args) = format_args.kind;
if let Some(format_args) = FormatArgsExpn::parse(format_args);
then {
Some(PanicExpn {
call_site: expn_data.call_site,
format_args,
})
} else {
None
}
}
}
}

View file

@ -540,7 +540,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
std::mem::discriminant(&b.rules).hash(&mut self.s);
}
#[allow(clippy::many_single_char_names, clippy::too_many_lines)]
#[allow(clippy::too_many_lines)]
pub fn hash_expr(&mut self, e: &Expr<'_>) {
let simple_const = self
.maybe_typeck_results

View file

@ -68,6 +68,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal-lints")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];

View file

@ -192,9 +192,8 @@ fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rv
))
}
},
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => Ok(()),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);
if ty.is_integral() || ty.is_bool() {

View file

@ -15,6 +15,8 @@ use std::{
env, fmt,
fs::write,
path::{Path, PathBuf},
thread,
time::Duration,
};
use clap::{App, Arg, ArgMatches};
@ -109,6 +111,22 @@ impl std::fmt::Display for ClippyWarning {
}
}
fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
const MAX_RETRIES: u8 = 4;
let mut retries = 0;
loop {
match ureq::get(path).call() {
Ok(res) => return Ok(res),
Err(e) if retries >= MAX_RETRIES => return Err(e),
Err(ureq::Error::Transport(e)) => eprintln!("Error: {}", e),
Err(e) => return Err(e),
}
eprintln!("retrying in {} seconds...", retries);
thread::sleep(Duration::from_secs(retries as u64));
retries += 1;
}
}
impl CrateSource {
/// Makes the sources available on the disk for clippy to check.
/// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
@ -129,7 +147,7 @@ impl CrateSource {
if !krate_file_path.is_file() {
// create a file path to download and write the crate data into
let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
let mut krate_req = ureq::get(&url).call().unwrap().into_reader();
let mut krate_req = get(&url).unwrap().into_reader();
// copy the crate into the file
std::io::copy(&mut krate_req, &mut krate_dest).unwrap();

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2021-09-08"
channel = "nightly-2021-09-28"
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]

View file

@ -4,7 +4,6 @@
use rustc_tools_util::VersionInfo;
use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::{self, Command};
@ -14,7 +13,7 @@ Usage:
cargo clippy [options] [--] [<opts>...]
Common options:
--no-deps Run Clippy only on the given crate, without linting the dependencies
--no-deps Run Clippy only on the given crate, without linting the dependencies
--fix Automatically apply lint suggestions. This flag implies `--no-deps`
-h, --help Print this message
-V, --version Print version info and exit
@ -116,22 +115,6 @@ impl ClippyCmd {
path
}
fn target_dir() -> Option<(&'static str, OsString)> {
env::var_os("CLIPPY_DOGFOOD")
.map(|_| {
env::var_os("CARGO_MANIFEST_DIR").map_or_else(
|| std::ffi::OsString::from("clippy_dogfood"),
|d| {
std::path::PathBuf::from(d)
.join("target")
.join("dogfood")
.into_os_string()
},
)
})
.map(|p| ("CARGO_TARGET_DIR", p))
}
fn into_std_cmd(self) -> Command {
let mut cmd = Command::new("cargo");
let clippy_args: String = self
@ -141,7 +124,6 @@ impl ClippyCmd {
.collect();
cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
.envs(ClippyCmd::target_dir())
.env("CLIPPY_ARGS", clippy_args)
.arg(self.cargo_subcommand)
.args(&self.args);

View file

@ -1,25 +1,3 @@
use std::env;
use std::lazy::SyncLazy;
use std::path::PathBuf;
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
Some(v) => v.into(),
None => env::current_dir().unwrap().join("target"),
});
pub static TARGET_LIB: SyncLazy<PathBuf> = SyncLazy::new(|| {
if let Some(path) = option_env!("TARGET_LIBS") {
path.into()
} else {
let mut dir = CARGO_TARGET_DIR.clone();
if let Some(target) = env::var_os("CARGO_BUILD_TARGET") {
dir.push(target);
}
dir.push(env!("PROFILE"));
dir
}
});
#[must_use]
pub fn is_rustc_test_suite() -> bool {
option_env!("RUSTC_TEST_SUITE").is_some()

View file

@ -1,5 +1,4 @@
#![feature(test)] // compiletest_rs requires this attribute
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
@ -46,14 +45,6 @@ extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;
fn host_lib() -> PathBuf {
option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
}
fn clippy_driver_path() -> PathBuf {
option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from)
}
/// Produces a string with an `--extern` flag for all UI test crate
/// dependencies.
///
@ -99,12 +90,14 @@ fn extern_flags() -> String {
.copied()
.filter(|n| !crates.contains_key(n))
.collect();
if !not_found.is_empty() {
panic!("dependencies not found in depinfo: {:?}", not_found);
}
assert!(
not_found.is_empty(),
"dependencies not found in depinfo: {:?}",
not_found
);
crates
.into_iter()
.map(|(name, path)| format!("--extern {}={} ", name, path))
.map(|(name, path)| format!(" --extern {}={}", name, path))
.collect()
}
@ -120,19 +113,29 @@ fn default_config() -> compiletest::Config {
config.run_lib_path = path.clone();
config.compile_lib_path = path;
}
let current_exe_path = std::env::current_exe().unwrap();
let deps_path = current_exe_path.parent().unwrap();
let profile_path = deps_path.parent().unwrap();
// Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
// This is valuable because a) it allows us to monitor what external dependencies are used
// and b) it ensures that conflicting rlibs are resolved properly.
let host_libs = option_env!("HOST_LIBS")
.map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
.unwrap_or_default();
config.target_rustcflags = Some(format!(
"--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}",
host_lib().join("deps").display(),
cargo::TARGET_LIB.join("deps").display(),
"--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
deps_path.display(),
host_libs,
extern_flags(),
));
config.build_base = host_lib().join("test_build_base");
config.rustc_path = clippy_driver_path();
config.build_base = profile_path.join("test");
config.rustc_path = profile_path.join(if cfg!(windows) {
"clippy-driver.exe"
} else {
"clippy-driver"
});
config
}

View file

@ -15,7 +15,12 @@ use std::process::Command;
mod cargo;
static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy"));
static CLIPPY_PATH: SyncLazy<PathBuf> = SyncLazy::new(|| {
let mut path = std::env::current_exe().unwrap();
assert!(path.pop()); // deps
path.set_file_name("cargo-clippy");
path
});
#[test]
fn dogfood_clippy() {
@ -28,7 +33,6 @@ fn dogfood_clippy() {
let mut command = Command::new(&*CLIPPY_PATH);
command
.current_dir(root_dir)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.arg("--all-targets")
@ -74,7 +78,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
// Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
@ -94,7 +97,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
// Test that without the `--no-deps` argument, `path_dep` is linted.
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
@ -121,7 +123,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() {
let successful_build = || {
let output = Command::new(&*CLIPPY_PATH)
.current_dir(&cwd)
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.args(&["-p", "subcrate"])
@ -223,7 +224,6 @@ fn run_clippy_for_project(project: &str) {
command
.current_dir(root_dir.join(project))
.env("CLIPPY_DOGFOOD", "1")
.env("CARGO_INCREMENTAL", "0")
.arg("clippy")
.arg("--all-targets")

View file

@ -74,8 +74,11 @@ fn integration_test() {
panic!("incompatible crate versions");
} else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
} else if stderr.contains("toolchain") && stderr.contains("is not installed") {
panic!("missing required toolchain");
} else {
assert!(
!stderr.contains("toolchain") || !stderr.contains("is not installed"),
"missing required toolchain"
);
}
match output.status.code() {

View file

@ -4,7 +4,7 @@ error: invalid path
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
= note: `-D clippy::invalid-paths` implied by `-D warnings`
error: invalid path
--> $DIR/invalid_paths.rs:20:5

View file

@ -1,5 +1,8 @@
disallowed-methods = [
# just a string is shorthand for path only
"std::iter::Iterator::sum",
"regex::Regex::is_match",
"regex::Regex::new"
# can give path and reason with an inline table
{ path = "regex::Regex::is_match", reason = "no matching allowed" },
# can use an inline table but omit reason
{ path = "regex::Regex::new" },
]

View file

@ -1,4 +1,4 @@
error: use of a disallowed method `regex::re_unicode::Regex::new`
error: use of a disallowed method `regex::Regex::new`
--> $DIR/conf_disallowed_method.rs:7:14
|
LL | let re = Regex::new(r"ab.*c").unwrap();
@ -6,13 +6,15 @@ LL | let re = Regex::new(r"ab.*c").unwrap();
|
= note: `-D clippy::disallowed-method` implied by `-D warnings`
error: use of a disallowed method `regex::re_unicode::Regex::is_match`
error: use of a disallowed method `regex::Regex::is_match`
--> $DIR/conf_disallowed_method.rs:8:5
|
LL | re.is_match("abc");
| ^^^^^^^^^^^^^^^^^^
|
= note: no matching allowed (from clippy.toml)
error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum`
error: use of a disallowed method `std::iter::Iterator::sum`
--> $DIR/conf_disallowed_method.rs:11:5
|
LL | a.iter().sum::<i32>();

View file

@ -2,7 +2,6 @@
// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
#![deny(clippy::trivially_copy_pass_by_ref)]
#![allow(clippy::many_single_char_names)]
#[derive(Copy, Clone)]
struct Foo(u8);

View file

@ -1,5 +1,5 @@
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> $DIR/test.rs:15:11
--> $DIR/test.rs:14:11
|
LL | fn bad(x: &u16, y: &Foo) {}
| ^^^^ help: consider passing by value instead: `u16`
@ -11,7 +11,7 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> $DIR/test.rs:15:20
--> $DIR/test.rs:14:20
|
LL | fn bad(x: &u16, y: &Foo) {}
| ^^^^ help: consider passing by value instead: `Foo`

View file

@ -0,0 +1,75 @@
// compile-flags: --emit=link
// no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use std::iter::FromIterator;
fn read_ident(iter: &mut token_stream::IntoIter) -> Ident {
match iter.next() {
Some(TokenTree::Ident(i)) => i,
_ => panic!("expected ident"),
}
}
#[proc_macro_derive(DeriveBadSpan)]
pub fn derive_bad_span(input: TokenStream) -> TokenStream {
let mut input = input.into_iter();
assert_eq!(read_ident(&mut input).to_string(), "struct");
let ident = read_ident(&mut input);
let mut tys = match input.next() {
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => g.stream().into_iter(),
_ => panic!(),
};
let field1 = read_ident(&mut tys);
tys.next();
let field2 = read_ident(&mut tys);
<TokenStream as FromIterator<TokenTree>>::from_iter(
[
Ident::new("impl", Span::call_site()).into(),
ident.into(),
Group::new(
Delimiter::Brace,
<TokenStream as FromIterator<TokenTree>>::from_iter(
[
Ident::new("fn", Span::call_site()).into(),
Ident::new("_foo", Span::call_site()).into(),
Group::new(Delimiter::Parenthesis, TokenStream::new()).into(),
Group::new(
Delimiter::Brace,
<TokenStream as FromIterator<TokenTree>>::from_iter(
[
Ident::new("if", field1.span()).into(),
Ident::new("true", field1.span()).into(),
{
let mut group = Group::new(Delimiter::Brace, TokenStream::new());
group.set_span(field1.span());
group.into()
},
Ident::new("if", field2.span()).into(),
Ident::new("true", field2.span()).into(),
{
let mut group = Group::new(Delimiter::Brace, TokenStream::new());
group.set_span(field2.span());
group.into()
},
]
.iter()
.cloned(),
),
)
.into(),
]
.iter()
.cloned(),
),
)
.into(),
]
.iter()
.cloned(),
)
}

View file

@ -6,6 +6,8 @@
unused
)]
use std::collections::HashMap;
macro_rules! boxit {
($init:expr, $x:ty) => {
let _: Box<$x> = Box::new($init);
@ -15,6 +17,7 @@ macro_rules! boxit {
fn test_macro() {
boxit!(Vec::new(), Vec<u8>);
}
fn test(foo: Box<Vec<bool>>) {}
fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
@ -22,6 +25,10 @@ fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
foo(vec![1, 2, 3])
}
fn test3(foo: Box<String>) {}
fn test4(foo: Box<HashMap<String, String>>) {}
fn test_local_not_linted() {
let _: Box<Vec<bool>>;
}
@ -29,6 +36,7 @@ fn test_local_not_linted() {
// All of these test should be allowed because they are part of the
// public api and `avoid_breaking_exported_api` is `false` by default.
pub fn pub_test(foo: Box<Vec<bool>>) {}
pub fn pub_test_ret() -> Box<Vec<bool>> {
Box::new(Vec::new())
}

View file

@ -0,0 +1,27 @@
error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
--> $DIR/box_collection.rs:21:14
|
LL | fn test(foo: Box<Vec<bool>>) {}
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::box-collection` implied by `-D warnings`
= help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
error: you seem to be trying to use `Box<String>`. Consider using just `String`
--> $DIR/box_collection.rs:28:15
|
LL | fn test3(foo: Box<String>) {}
| ^^^^^^^^^^^
|
= help: `String` is already on the heap, `Box<String>` makes an extra allocation
error: you seem to be trying to use `Box<HashMap<..>>`. Consider using just `HashMap<..>`
--> $DIR/box_collection.rs:30:15
|
LL | fn test4(foo: Box<HashMap<String, String>>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
error: aborting due to 3 previous errors

View file

@ -1,11 +0,0 @@
error: you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`
--> $DIR/box_vec.rs:18:14
|
LL | fn test(foo: Box<Vec<bool>>) {}
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::box-vec` implied by `-D warnings`
= help: `Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation
error: aborting due to previous error

View file

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports,dead_code)]
#![allow(unused_imports, dead_code)]
#![deny(clippy::default_trait_access)]
use std::default;

View file

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports,dead_code)]
#![allow(unused_imports, dead_code)]
#![deny(clippy::default_trait_access)]
use std::default;

View file

@ -9,7 +9,7 @@ fn get_reference(n: &usize) -> &usize {
n
}
#[allow(clippy::many_single_char_names, clippy::double_parens)]
#[allow(clippy::double_parens)]
#[allow(unused_variables, unused_parens)]
fn main() {
let a = 10;

View file

@ -9,7 +9,7 @@ fn get_reference(n: &usize) -> &usize {
n
}
#[allow(clippy::many_single_char_names, clippy::double_parens)]
#[allow(clippy::double_parens)]
#[allow(unused_variables, unused_parens)]
fn main() {
let a = 10;

View file

@ -167,4 +167,44 @@ impl Default for WithoutSelfParan {
}
}
// https://github.com/rust-lang/rust-clippy/issues/7655
pub struct SpecializedImpl2<T> {
v: Vec<T>,
}
impl Default for SpecializedImpl2<String> {
fn default() -> Self {
Self { v: Vec::new() }
}
}
// https://github.com/rust-lang/rust-clippy/issues/7654
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
}
/// `#000000`
impl Default for Color {
fn default() -> Self {
Color { r: 0, g: 0, b: 0 }
}
}
pub struct Color2 {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Default for Color2 {
/// `#000000`
fn default() -> Self {
Self { r: 0, g: 0, b: 0 }
}
}
fn main() {}

View file

@ -2,7 +2,7 @@
#[rustfmt::skip]
#[warn(clippy::eq_op)]
#[allow(clippy::identity_op, clippy::double_parens, clippy::many_single_char_names)]
#[allow(clippy::identity_op, clippy::double_parens)]
#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
#[allow(clippy::nonminimal_bool)]
#[allow(unused)]

View file

@ -4,7 +4,6 @@
unused,
clippy::no_effect,
clippy::redundant_closure_call,
clippy::many_single_char_names,
clippy::needless_pass_by_value,
clippy::option_map_unit_fn
)]
@ -14,7 +13,7 @@
clippy::needless_borrow
)]
use std::path::PathBuf;
use std::path::{Path, PathBuf};
macro_rules! mac {
() => {
@ -30,19 +29,18 @@ macro_rules! closure_mac {
fn main() {
let a = Some(1u8).map(foo);
meta(foo);
let c = Some(1u8).map(|a| {1+2; foo}(a));
true.then(|| mac!()); // don't lint function in macro expansion
Some(1).map(closure_mac!()); // don't lint closure in macro expansion
let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted
let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
all(&[1, 2, 3], &2, below); //is adjusted
unsafe {
Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
}
// See #815
let e = Some(1u8).map(|a| divergent(a));
let e = Some(1u8).map(divergent);
let e = Some(1u8).map(generic);
let e = Some(1u8).map(generic);
// See #515
@ -90,24 +88,17 @@ impl<'a> std::ops::Deref for TestStruct<'a> {
fn test_redundant_closures_containing_method_calls() {
let i = 10;
let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
unsafe {
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
}
let e = Some("str").map(std::string::ToString::to_string);
let e = Some("str").map(str::to_string);
let e = Some('a').map(char::to_uppercase);
let e = Some('a').map(char::to_uppercase);
let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
let p = Some(PathBuf::new());
let e = p.as_ref().and_then(|s| s.to_str());
let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
let c = Some(TestStruct { some_ref: &i })
.as_ref()
.map(|c| c.to_ascii_uppercase());
@ -119,10 +110,6 @@ fn test_redundant_closures_containing_method_calls() {
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
let mut some = Some(|x| x * x);
let arr = [Ok(1), Err(2)];
let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
}
struct Thunk<T>(Box<dyn FnMut() -> T>);
@ -145,13 +132,6 @@ fn foobar() {
thunk.unwrap()
}
fn meta<F>(f: F)
where
F: Fn(u8),
{
f(1u8)
}
fn foo(_: u8) {}
fn foo2(_: u8) -> u8 {
@ -180,7 +160,7 @@ fn generic<T>(_: T) -> u8 {
}
fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
requires_fn_once(|| x());
requires_fn_once(x);
}
fn requires_fn_once<T: FnOnce()>(_: T) {}
@ -236,3 +216,35 @@ fn mutable_closure_in_loop() {
Some(1).map(&mut closure);
}
}
fn late_bound_lifetimes() {
fn take_asref_path<P: AsRef<Path>>(path: P) {}
fn map_str<F>(thunk: F)
where
F: FnOnce(&str),
{
}
fn map_str_to_path<F>(thunk: F)
where
F: FnOnce(&str) -> &Path,
{
}
map_str(|s| take_asref_path(s));
map_str_to_path(std::convert::AsRef::as_ref);
}
mod type_param_bound {
trait Trait {
fn fun();
}
fn take<T: 'static>(_: T) {}
fn test<X: Trait>() {
// don't lint, but it's questionable that rust requires a cast
take(|| X::fun());
take(X::fun as fn());
}
}

View file

@ -4,7 +4,6 @@
unused,
clippy::no_effect,
clippy::redundant_closure_call,
clippy::many_single_char_names,
clippy::needless_pass_by_value,
clippy::option_map_unit_fn
)]
@ -14,7 +13,7 @@
clippy::needless_borrow
)]
use std::path::PathBuf;
use std::path::{Path, PathBuf};
macro_rules! mac {
() => {
@ -30,7 +29,6 @@ macro_rules! closure_mac {
fn main() {
let a = Some(1u8).map(|a| foo(a));
meta(|a| foo(a));
let c = Some(1u8).map(|a| {1+2; foo}(a));
true.then(|| mac!()); // don't lint function in macro expansion
Some(1).map(closure_mac!()); // don't lint closure in macro expansion
@ -90,24 +88,17 @@ impl<'a> std::ops::Deref for TestStruct<'a> {
fn test_redundant_closures_containing_method_calls() {
let i = 10;
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
unsafe {
let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
}
let e = Some("str").map(|s| s.to_string());
let e = Some("str").map(str::to_string);
let e = Some('a').map(|s| s.to_uppercase());
let e = Some('a').map(char::to_uppercase);
let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
let p = Some(PathBuf::new());
let e = p.as_ref().and_then(|s| s.to_str());
let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
let c = Some(TestStruct { some_ref: &i })
.as_ref()
.map(|c| c.to_ascii_uppercase());
@ -119,10 +110,6 @@ fn test_redundant_closures_containing_method_calls() {
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
let mut some = Some(|x| x * x);
let arr = [Ok(1), Err(2)];
let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect();
}
struct Thunk<T>(Box<dyn FnMut() -> T>);
@ -145,13 +132,6 @@ fn foobar() {
thunk.unwrap()
}
fn meta<F>(f: F)
where
F: Fn(u8),
{
f(1u8)
}
fn foo(_: u8) {}
fn foo2(_: u8) -> u8 {
@ -236,3 +216,35 @@ fn mutable_closure_in_loop() {
Some(1).map(|n| closure(n));
}
}
fn late_bound_lifetimes() {
fn take_asref_path<P: AsRef<Path>>(path: P) {}
fn map_str<F>(thunk: F)
where
F: FnOnce(&str),
{
}
fn map_str_to_path<F>(thunk: F)
where
F: FnOnce(&str) -> &Path,
{
}
map_str(|s| take_asref_path(s));
map_str_to_path(|s| s.as_ref());
}
mod type_param_bound {
trait Trait {
fn fun();
}
fn take<T: 'static>(_: T) {}
fn test<X: Trait>() {
// don't lint, but it's questionable that rust requires a cast
take(|| X::fun());
take(X::fun as fn());
}
}

View file

@ -1,5 +1,5 @@
error: redundant closure
--> $DIR/eta.rs:32:27
--> $DIR/eta.rs:31:27
|
LL | let a = Some(1u8).map(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@ -7,19 +7,19 @@ LL | let a = Some(1u8).map(|a| foo(a));
= note: `-D clippy::redundant-closure` implied by `-D warnings`
error: redundant closure
--> $DIR/eta.rs:33:10
|
LL | meta(|a| foo(a));
| ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
error: redundant closure
--> $DIR/eta.rs:37:40
--> $DIR/eta.rs:35:40
|
LL | let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
error: redundant closure
--> $DIR/eta.rs:36:35
|
LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
| ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler
--> $DIR/eta.rs:39:21
--> $DIR/eta.rs:37:21
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^ help: change this to: `&2`
@ -27,13 +27,25 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
= note: `-D clippy::needless-borrow` implied by `-D warnings`
error: redundant closure
--> $DIR/eta.rs:46:27
--> $DIR/eta.rs:37:26
|
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
error: redundant closure
--> $DIR/eta.rs:43:27
|
LL | let e = Some(1u8).map(|a| divergent(a));
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
error: redundant closure
--> $DIR/eta.rs:44:27
|
LL | let e = Some(1u8).map(|a| generic(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
error: redundant closure
--> $DIR/eta.rs:92:51
--> $DIR/eta.rs:90:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@ -41,70 +53,82 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
= note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
error: redundant closure
--> $DIR/eta.rs:94:51
--> $DIR/eta.rs:91:51
|
LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
error: redundant closure
--> $DIR/eta.rs:97:42
--> $DIR/eta.rs:93:42
|
LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
| ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
error: redundant closure
--> $DIR/eta.rs:102:29
--> $DIR/eta.rs:97:29
|
LL | let e = Some("str").map(|s| s.to_string());
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
error: redundant closure
--> $DIR/eta.rs:104:27
--> $DIR/eta.rs:98:27
|
LL | let e = Some('a').map(|s| s.to_uppercase());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
error: redundant closure
--> $DIR/eta.rs:107:65
--> $DIR/eta.rs:100:65
|
LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure
--> $DIR/eta.rs:190:27
--> $DIR/eta.rs:163:22
|
LL | requires_fn_once(|| x());
| ^^^^^^ help: replace the closure with the function itself: `x`
error: redundant closure
--> $DIR/eta.rs:170:27
|
LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure
--> $DIR/eta.rs:195:27
--> $DIR/eta.rs:175:27
|
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
error: redundant closure
--> $DIR/eta.rs:227:28
--> $DIR/eta.rs:207:28
|
LL | x.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
--> $DIR/eta.rs:228:28
--> $DIR/eta.rs:208:28
|
LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
--> $DIR/eta.rs:229:28
--> $DIR/eta.rs:209:28
|
LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure
--> $DIR/eta.rs:236:21
--> $DIR/eta.rs:216:21
|
LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: aborting due to 17 previous errors
error: redundant closure
--> $DIR/eta.rs:235:21
|
LL | map_str_to_path(|s| s.as_ref());
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
error: aborting due to 21 previous errors

View file

@ -4,7 +4,6 @@
#[allow(
unused_assignments,
unused_variables,
clippy::many_single_char_names,
clippy::no_effect,
dead_code,
clippy::blacklisted_name

View file

@ -1,48 +1,48 @@
error: unsequenced read of `x`
--> $DIR/eval_order_dependence.rs:17:9
--> $DIR/eval_order_dependence.rs:16:9
|
LL | } + x;
| ^
|
= note: `-D clippy::eval-order-dependence` implied by `-D warnings`
note: whether read occurs before this write depends on evaluation order
--> $DIR/eval_order_dependence.rs:15:9
--> $DIR/eval_order_dependence.rs:14:9
|
LL | x = 1;
| ^^^^^
error: unsequenced read of `x`
--> $DIR/eval_order_dependence.rs:20:5
--> $DIR/eval_order_dependence.rs:19:5
|
LL | x += {
| ^
|
note: whether read occurs before this write depends on evaluation order
--> $DIR/eval_order_dependence.rs:21:9
--> $DIR/eval_order_dependence.rs:20:9
|
LL | x = 20;
| ^^^^^^
error: unsequenced read of `x`
--> $DIR/eval_order_dependence.rs:33:12
--> $DIR/eval_order_dependence.rs:32:12
|
LL | a: x,
| ^
|
note: whether read occurs before this write depends on evaluation order
--> $DIR/eval_order_dependence.rs:35:13
--> $DIR/eval_order_dependence.rs:34:13
|
LL | x = 6;
| ^^^^^
error: unsequenced read of `x`
--> $DIR/eval_order_dependence.rs:42:9
--> $DIR/eval_order_dependence.rs:41:9
|
LL | x += {
| ^
|
note: whether read occurs before this write depends on evaluation order
--> $DIR/eval_order_dependence.rs:43:13
--> $DIR/eval_order_dependence.rs:42:13
|
LL | x = 20;
| ^^^^^^

View file

@ -17,8 +17,8 @@ fn main() {
const BAD32_3: f32 = 0.1;
const BAD32_EDGE: f32 = 1.000_001;
const BAD64_1: f64 = 0.123_456_789_012_345_66_f64;
const BAD64_2: f64 = 0.123_456_789_012_345_66;
const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
const BAD64_2: f64 = 0.123_456_789_012_345_67;
const BAD64_3: f64 = 0.1;
// Literal as param
@ -37,9 +37,9 @@ fn main() {
let bad32_suf: f32 = 1.123_456_8_f32;
let bad32_inf = 1.123_456_8_f32;
let bad64: f64 = 0.123_456_789_012_345_66;
let bad64_suf: f64 = 0.123_456_789_012_345_66_f64;
let bad64_inf = 0.123_456_789_012_345_66;
let bad64: f64 = 0.123_456_789_012_345_67;
let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
let bad64_inf = 0.123_456_789_012_345_67;
// Vectors
let good_vec32: Vec<f32> = vec![0.123_456];

View file

@ -24,18 +24,6 @@ error: float has excessive precision
LL | const BAD32_EDGE: f32 = 1.000_000_9;
| ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
error: float has excessive precision
--> $DIR/excessive_precision.rs:20:26
|
LL | const BAD64_1: f64 = 0.123_456_789_012_345_67f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
error: float has excessive precision
--> $DIR/excessive_precision.rs:21:26
|
LL | const BAD64_2: f64 = 0.123_456_789_012_345_67;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
error: float has excessive precision
--> $DIR/excessive_precision.rs:22:26
|
@ -66,24 +54,6 @@ error: float has excessive precision
LL | let bad32_inf = 1.123_456_789_f32;
| ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
error: float has excessive precision
--> $DIR/excessive_precision.rs:40:22
|
LL | let bad64: f64 = 0.123_456_789_012_345_67;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
error: float has excessive precision
--> $DIR/excessive_precision.rs:41:26
|
LL | let bad64_suf: f64 = 0.123_456_789_012_345_67f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66_f64`
error: float has excessive precision
--> $DIR/excessive_precision.rs:42:21
|
LL | let bad64_inf = 0.123_456_789_012_345_67;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_012_345_66`
error: float has excessive precision
--> $DIR/excessive_precision.rs:48:36
|
@ -108,5 +78,5 @@ error: float has excessive precision
LL | let bad_bige32: f32 = 1.123_456_788_888E-10;
| ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
error: aborting due to 18 previous errors
error: aborting due to 13 previous errors

View file

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
#![allow(unused_variables, clippy::clone_double_ref)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};

View file

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
#![allow(unused_variables, clippy::clone_double_ref)]
#![warn(clippy::explicit_deref_methods)]
use std::ops::{Deref, DerefMut};

View file

@ -1,4 +1,5 @@
#![deny(clippy::fallible_impl_from)]
#![allow(clippy::if_then_panic)]
// docs example
struct Foo(i32);

View file

@ -1,5 +1,5 @@
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:5:1
--> $DIR/fallible_impl_from.rs:6:1
|
LL | / impl From<String> for Foo {
LL | | fn from(s: String) -> Self {
@ -15,13 +15,13 @@ LL | #![deny(clippy::fallible_impl_from)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:7:13
--> $DIR/fallible_impl_from.rs:8:13
|
LL | Foo(s.parse().unwrap())
| ^^^^^^^^^^^^^^^^^^
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:26:1
--> $DIR/fallible_impl_from.rs:27:1
|
LL | / impl From<usize> for Invalid {
LL | | fn from(i: usize) -> Invalid {
@ -34,14 +34,14 @@ LL | | }
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:29:13
--> $DIR/fallible_impl_from.rs:30:13
|
LL | panic!();
| ^^^^^^^^^
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:35:1
--> $DIR/fallible_impl_from.rs:36:1
|
LL | / impl From<Option<String>> for Invalid {
LL | | fn from(s: Option<String>) -> Invalid {
@ -54,7 +54,7 @@ LL | | }
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:37:17
--> $DIR/fallible_impl_from.rs:38:17
|
LL | let s = s.unwrap();
| ^^^^^^^^^^
@ -68,7 +68,7 @@ LL | panic!("{:?}", s);
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:53:1
--> $DIR/fallible_impl_from.rs:54:1
|
LL | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
LL | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
@ -81,7 +81,7 @@ LL | | }
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:55:12
--> $DIR/fallible_impl_from.rs:56:12
|
LL | if s.parse::<u32>().ok().unwrap() != 42 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View file

@ -4,8 +4,7 @@
clippy::no_effect,
clippy::op_ref,
clippy::unnecessary_operation,
clippy::cast_lossless,
clippy::many_single_char_names
clippy::cast_lossless
)]
use std::ops::Add;

View file

@ -1,5 +1,5 @@
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:58:5
--> $DIR/float_cmp.rs:57:5
|
LL | ONE as f64 != 2.0;
| ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0;
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:63:5
--> $DIR/float_cmp.rs:62:5
|
LL | x == 1.0;
| ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
@ -16,7 +16,7 @@ LL | x == 1.0;
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:66:5
--> $DIR/float_cmp.rs:65:5
|
LL | twice(x) != twice(ONE as f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64);
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:86:5
--> $DIR/float_cmp.rs:85:5
|
LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64` arrays
--> $DIR/float_cmp.rs:91:5
--> $DIR/float_cmp.rs:90:5
|
LL | a1 == a2;
| ^^^^^^^^
@ -40,7 +40,7 @@ LL | a1 == a2;
= note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
error: strict comparison of `f32` or `f64`
--> $DIR/float_cmp.rs:92:5
--> $DIR/float_cmp.rs:91:5
|
LL | a1[0] == a2[0];
| ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`

View file

@ -29,7 +29,7 @@ impl Unrelated {
clippy::unnecessary_mut_passed,
clippy::similar_names
)]
#[allow(clippy::many_single_char_names, unused_variables)]
#[allow(unused_variables)]
fn main() {
let mut vec = vec![1, 2, 3, 4];

View file

@ -29,7 +29,7 @@ impl Unrelated {
clippy::unnecessary_mut_passed,
clippy::similar_names
)]
#[allow(clippy::many_single_char_names, unused_variables)]
#[allow(unused_variables)]
fn main() {
let mut vec = vec![1, 2, 3, 4];

View file

@ -1,28 +0,0 @@
// run-rustfix
#![warn(clippy::if_let_some_result)]
#![allow(dead_code)]
fn str_to_int(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}
fn str_to_int_ok(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}
#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
if let Ok(y) = x . parse() {
return y;
};
0
}
}
fn negative() {
while let Some(1) = "".parse().ok() {}
}
fn main() {}

View file

@ -1,28 +0,0 @@
// run-rustfix
#![warn(clippy::if_let_some_result)]
#![allow(dead_code)]
fn str_to_int(x: &str) -> i32 {
if let Some(y) = x.parse().ok() { y } else { 0 }
}
fn str_to_int_ok(x: &str) -> i32 {
if let Ok(y) = x.parse() { y } else { 0 }
}
#[rustfmt::skip]
fn strange_some_no_else(x: &str) -> i32 {
{
if let Some(y) = x . parse() . ok () {
return y;
};
0
}
}
fn negative() {
while let Some(1) = "".parse().ok() {}
}
fn main() {}

View file

@ -0,0 +1,34 @@
// run-rustfix
#![warn(clippy::if_then_panic)]
fn main() {
let a = vec![1, 2, 3];
let c = Some(2);
if !a.is_empty()
&& a.len() == 3
&& c != None
&& !a.is_empty()
&& a.len() == 3
&& !a.is_empty()
&& a.len() == 3
&& !a.is_empty()
&& a.len() == 3
{
panic!("qaqaq{:?}", a);
}
assert!(a.is_empty(), "qaqaq{:?}", a);
assert!(a.is_empty(), "qwqwq");
if a.len() == 3 {
println!("qwq");
println!("qwq");
println!("qwq");
}
if let Some(b) = c {
panic!("orz {}", b);
}
if a.len() == 3 {
panic!("qaqaq");
} else {
println!("qwq");
}
}

38
tests/ui/if_then_panic.rs Normal file
View file

@ -0,0 +1,38 @@
// run-rustfix
#![warn(clippy::if_then_panic)]
fn main() {
let a = vec![1, 2, 3];
let c = Some(2);
if !a.is_empty()
&& a.len() == 3
&& c != None
&& !a.is_empty()
&& a.len() == 3
&& !a.is_empty()
&& a.len() == 3
&& !a.is_empty()
&& a.len() == 3
{
panic!("qaqaq{:?}", a);
}
if !a.is_empty() {
panic!("qaqaq{:?}", a);
}
if !a.is_empty() {
panic!("qwqwq");
}
if a.len() == 3 {
println!("qwq");
println!("qwq");
println!("qwq");
}
if let Some(b) = c {
panic!("orz {}", b);
}
if a.len() == 3 {
panic!("qaqaq");
} else {
println!("qwq");
}
}

View file

@ -0,0 +1,20 @@
error: only a `panic!` in `if`-then statement
--> $DIR/if_then_panic.rs:19:5
|
LL | / if !a.is_empty() {
LL | | panic!("qaqaq{:?}", a);
LL | | }
| |_____^ help: try: `assert!(a.is_empty(), "qaqaq{:?}", a);`
|
= note: `-D clippy::if-then-panic` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
--> $DIR/if_then_panic.rs:22:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
LL | | }
| |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
error: aborting due to 2 previous errors

View file

@ -16,7 +16,6 @@ fn foob() -> bool {
unimplemented!()
}
#[allow(clippy::many_single_char_names)]
fn immutable_condition() {
// Should warn when all vars mentioned are immutable
let y = 0;

View file

@ -1,5 +1,5 @@
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:23:11
--> $DIR/infinite_loop.rs:22:11
|
LL | while y < 10 {
| ^^^^^^
@ -8,7 +8,7 @@ LL | while y < 10 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:28:11
--> $DIR/infinite_loop.rs:27:11
|
LL | while y < 10 && x < 3 {
| ^^^^^^^^^^^^^^^
@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:35:11
--> $DIR/infinite_loop.rs:34:11
|
LL | while !cond {
| ^^^^^
@ -24,7 +24,7 @@ LL | while !cond {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:79:11
--> $DIR/infinite_loop.rs:78:11
|
LL | while i < 3 {
| ^^^^^
@ -32,7 +32,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:84:11
--> $DIR/infinite_loop.rs:83:11
|
LL | while i < 3 && j > 0 {
| ^^^^^^^^^^^^^^
@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:88:11
--> $DIR/infinite_loop.rs:87:11
|
LL | while i < 3 {
| ^^^^^
@ -48,7 +48,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:103:11
--> $DIR/infinite_loop.rs:102:11
|
LL | while i < 3 {
| ^^^^^
@ -56,7 +56,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:108:11
--> $DIR/infinite_loop.rs:107:11
|
LL | while i < 3 {
| ^^^^^
@ -64,7 +64,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:174:15
--> $DIR/infinite_loop.rs:173:15
|
LL | while self.count < n {
| ^^^^^^^^^^^^^^
@ -72,7 +72,7 @@ LL | while self.count < n {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:182:11
--> $DIR/infinite_loop.rs:181:11
|
LL | while y < 10 {
| ^^^^^^
@ -82,7 +82,7 @@ LL | while y < 10 {
= help: rewrite it as `if cond { loop { } }`
error: variables in the condition are not mutated in the loop body
--> $DIR/infinite_loop.rs:189:11
--> $DIR/infinite_loop.rs:188:11
|
LL | while y < 10 {
| ^^^^^^

View file

@ -1,6 +1,5 @@
#![warn(clippy::inherent_to_string)]
#![deny(clippy::inherent_to_string_shadow_display)]
#![allow(clippy::many_single_char_names)]
use std::fmt;

View file

@ -1,5 +1,5 @@
error: implementation of inherent method `to_string(&self) -> String` for type `A`
--> $DIR/inherent_to_string.rs:21:5
--> $DIR/inherent_to_string.rs:20:5
|
LL | / fn to_string(&self) -> String {
LL | | "A.to_string()".to_string()
@ -10,7 +10,7 @@ LL | | }
= help: implement trait `Display` for type `A` instead
error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`
--> $DIR/inherent_to_string.rs:45:5
--> $DIR/inherent_to_string.rs:44:5
|
LL | / fn to_string(&self) -> String {
LL | | "C.to_string()".to_string()

View file

@ -0,0 +1,47 @@
#![warn(clippy::iter_not_returning_iterator)]
struct Data {
begin: u32,
}
struct Counter {
count: u32,
}
impl Data {
fn iter(&self) -> Counter {
todo!()
}
fn iter_mut(&self) -> Counter {
todo!()
}
}
struct Data2 {
begin: u32,
}
struct Counter2 {
count: u32,
}
impl Data2 {
fn iter(&self) -> Counter2 {
todo!()
}
fn iter_mut(&self) -> Counter2 {
todo!()
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
todo!()
}
}
fn main() {}

View file

@ -0,0 +1,16 @@
error: this method is named `iter` but its return type does not implement `Iterator`
--> $DIR/iter_not_returning_iterator.rs:30:5
|
LL | fn iter(&self) -> Counter2 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings`
error: this method is named `iter_mut` but its return type does not implement `Iterator`
--> $DIR/iter_not_returning_iterator.rs:34:5
|
LL | fn iter_mut(&self) -> Counter2 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -1,4 +1,4 @@
#![allow(unused, clippy::many_single_char_names, clippy::diverging_sub_expression)]
#![allow(unused, clippy::diverging_sub_expression)]
#![warn(clippy::logic_bug)]
fn main() {

View file

@ -36,6 +36,12 @@ fn main() {
// Don't lint, slices don't have `split_once`
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
let _ = "key=value".rsplit_once('=').unwrap().1;
let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
let _ = "key=value".rsplit_once('=').map(|x| x.1);
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
}
fn _msrv_1_51() {

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