mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-22 12:43:18 +00:00
Merge commit 'c1664c50b27a51f7a78c93ba65558e7c33eabee6' into clippyup
This commit is contained in:
parent
3be53bc45a
commit
8eca423ea1
139 changed files with 4334 additions and 576 deletions
12
.github/workflows/clippy_bors.yml
vendored
12
.github/workflows/clippy_bors.yml
vendored
|
@ -128,14 +128,14 @@ jobs:
|
|||
SYSROOT=$(rustc --print sysroot)
|
||||
echo "$SYSROOT/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings
|
||||
- name: Build with internal lints
|
||||
run: cargo build --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings
|
||||
- name: Test with internal lints
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings
|
||||
- name: Test clippy_lints with internal lints
|
||||
run: cargo test --features deny-warnings,internal-lints
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
|
|
|
@ -1770,6 +1770,7 @@ Released 2018-09-13
|
|||
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
|
||||
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||
|
@ -2056,6 +2057,7 @@ Released 2018-09-13
|
|||
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
|
||||
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
|
||||
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
|
||||
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
|
||||
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
|
||||
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
|
||||
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
|
||||
|
@ -2073,6 +2075,7 @@ Released 2018-09-13
|
|||
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
|
||||
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
|
||||
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
|
||||
|
|
|
@ -14,11 +14,16 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
|||
|
||||
- [Contributing to Clippy](#contributing-to-clippy)
|
||||
- [Getting started](#getting-started)
|
||||
- [High level approach](#high-level-approach)
|
||||
- [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
- [Writing code](#writing-code)
|
||||
- [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Fixing build failures caused by Rust](#fixing-build-failures-caused-by-rust)
|
||||
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
|
||||
- [Performing the sync](#performing-the-sync)
|
||||
- [Syncing back changes in Clippy to [`rust-lang/rust`]](#syncing-back-changes-in-clippy-to-rust-langrust)
|
||||
- [Defining remotes](#defining-remotes)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
- [Contributions](#contributions)
|
||||
|
@ -320,8 +325,8 @@ commands [here][homu_instructions].
|
|||
[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
|
||||
[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
|
||||
[homu]: https://github.com/rust-lang/homu
|
||||
[homu_instructions]: https://buildbot2.rust-lang.org/homu/
|
||||
[homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy
|
||||
[homu_instructions]: https://bors.rust-lang.org/
|
||||
[homu_queue]: https://bors.rust-lang.org/queue/clippy
|
||||
|
||||
## Contributions
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ path = "src/driver.rs"
|
|||
clippy_lints = { version = "0.0.212", path = "clippy_lints" }
|
||||
# end automatic update
|
||||
semver = "0.11"
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
|
||||
tempfile = { version = "3.1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -49,8 +49,9 @@ derive-new = "0.5"
|
|||
rustc-workspace-hack = "1.0.0"
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }
|
||||
|
||||
[features]
|
||||
deny-warnings = []
|
||||
integration = ["tempfile"]
|
||||
internal-lints = ["clippy_lints/internal-lints"]
|
||||
|
|
29
README.md
29
README.md
|
@ -182,7 +182,7 @@ cargo clippy -- -W clippy::lint_name
|
|||
```
|
||||
|
||||
This also works with lint groups. For example you
|
||||
can run Clippy with warnings for all lints enabled:
|
||||
can run Clippy with warnings for all lints enabled:
|
||||
```terminal
|
||||
cargo clippy -- -W clippy::pedantic
|
||||
```
|
||||
|
@ -194,6 +194,33 @@ cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
|
|||
```
|
||||
Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`.
|
||||
|
||||
### Specifying the minimum supported Rust version
|
||||
|
||||
Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
|
||||
specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
|
||||
|
||||
```toml
|
||||
msrv = "1.30.0"
|
||||
```
|
||||
|
||||
The MSRV can also be specified as an inner attribute, like below.
|
||||
|
||||
```rust
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.30.0"]
|
||||
|
||||
fn main() {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
|
||||
is equivalent to `msrv = 1.30.0`.
|
||||
|
||||
Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
|
||||
|
||||
Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
|
||||
|
|
|
@ -146,16 +146,30 @@ pub fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String>
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn gen_register_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
|
||||
let pre = " store.register_lints(&[".to_string();
|
||||
let post = " ]);".to_string();
|
||||
let mut inner = lints
|
||||
pub fn gen_register_lint_list<'a>(
|
||||
internal_lints: impl Iterator<Item = &'a Lint>,
|
||||
usable_lints: impl Iterator<Item = &'a Lint>,
|
||||
) -> Vec<String> {
|
||||
let header = " store.register_lints(&[".to_string();
|
||||
let footer = " ]);".to_string();
|
||||
let internal_lints = internal_lints
|
||||
.sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.map(|l| {
|
||||
format!(
|
||||
" #[cfg(feature = \"internal-lints\")]\n &{}::{},",
|
||||
l.module,
|
||||
l.name.to_uppercase()
|
||||
)
|
||||
});
|
||||
let other_lints = usable_lints
|
||||
.sorted_by_key(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.map(|l| format!(" &{}::{},", l.module, l.name.to_uppercase()))
|
||||
.sorted()
|
||||
.collect::<Vec<String>>();
|
||||
inner.insert(0, pre);
|
||||
inner.push(post);
|
||||
inner
|
||||
.sorted();
|
||||
let mut lint_list = vec![header];
|
||||
lint_list.extend(internal_lints);
|
||||
lint_list.extend(other_lints);
|
||||
lint_list.push(footer);
|
||||
lint_list
|
||||
}
|
||||
|
||||
/// Gathers all files in `src/clippy_lints` and gathers all lints inside
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn run(update_mode: UpdateMode) {
|
|||
"end register lints",
|
||||
false,
|
||||
update_mode == UpdateMode::Change,
|
||||
|| gen_register_lint_list(usable_lints.iter().chain(internal_lints.iter())),
|
||||
|| gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
|
||||
)
|
||||
.changed;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ smallvec = { version = "1", features = ["union"] }
|
|||
toml = "0.5.3"
|
||||
unicode-normalization = "0.1"
|
||||
semver = "0.11"
|
||||
rustc-semver="1.1.0"
|
||||
# NOTE: cargo requires serde feat in its url dep
|
||||
# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
|
||||
url = { version = "2.1.0", features = ["serde"] }
|
||||
|
@ -36,3 +37,5 @@ syn = { version = "1", features = ["full"] }
|
|||
|
||||
[features]
|
||||
deny-warnings = []
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal-lints = []
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::utils::{
|
|||
span_lint_and_sugg, span_lint_and_then, without_block_comments,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
|
@ -15,6 +14,7 @@ use rustc_lint::{CheckLintNameResult, EarlyContext, EarlyLintPass, LateContext,
|
|||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||
|
|
172
clippy_lints/src/collapsible_match.rs
Normal file
172
clippy_lints/src/collapsible_match.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
use crate::utils::visitors::LocalUsedVisitor;
|
||||
use crate::utils::{span_lint_and_then, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{DefIdTree, TyCtxt};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
|
||||
/// without adding any branches.
|
||||
///
|
||||
/// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
|
||||
/// cases where merging would most likely make the code more readable.
|
||||
///
|
||||
/// **Why is this bad?** It is unnecessarily verbose and complex.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(n) => match n {
|
||||
/// Ok(n) => n,
|
||||
/// _ => return,
|
||||
/// }
|
||||
/// None => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn func(opt: Option<Result<u64, String>>) {
|
||||
/// let n = match opt {
|
||||
/// Some(Ok(n)) => n,
|
||||
/// _ => return,
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
pub COLLAPSIBLE_MATCH,
|
||||
style,
|
||||
"Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
|
||||
for arm in arms {
|
||||
check_arm(arm, wild_arm, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
|
||||
if_chain! {
|
||||
let expr = strip_singleton_blocks(arm.body);
|
||||
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
|
||||
// the outer arm pattern and the inner match
|
||||
if expr_in.span.ctxt() == arm.pat.span.ctxt();
|
||||
// there must be no more than two arms in the inner match for this lint
|
||||
if arms_inner.len() == 2;
|
||||
// no if guards on the inner match
|
||||
if arms_inner.iter().all(|arm| arm.guard.is_none());
|
||||
// match expression must be a local binding
|
||||
// match <local> { .. }
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind;
|
||||
if let Res::Local(binding_id) = path.res;
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
|
||||
if !pat_contains_or(non_wild_inner_arm.pat);
|
||||
// the binding must come from the pattern of the containing match arm
|
||||
// ..<local>.. => match <local> { .. }
|
||||
if let Some(binding_span) = find_pat_binding(arm.pat, binding_id);
|
||||
// the "wild-like" branches must be equal
|
||||
if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
|
||||
// the binding must not be used in the if guard
|
||||
if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard));
|
||||
// ...or anywhere in the inner match
|
||||
if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_MATCH,
|
||||
expr.span,
|
||||
"Unnecessary nested match",
|
||||
|diag| {
|
||||
let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]);
|
||||
help_span.push_span_label(binding_span, "Replace this binding".into());
|
||||
help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into());
|
||||
diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern.");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
while let ExprKind::Block(block, _) = expr.kind {
|
||||
match (block.stmts, block.expr) {
|
||||
([stmt], None) => match stmt.kind {
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
|
||||
_ => break,
|
||||
},
|
||||
([], Some(e)) => expr = e,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
/// A "wild-like" pattern is wild ("_") or `None`.
|
||||
/// For this lint to apply, both the outer and inner match expressions
|
||||
/// must have "wild-like" branches that can be combined.
|
||||
fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
|
||||
if arm.guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
match arm.pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
|
||||
let mut span = None;
|
||||
pat.walk_short(|p| match &p.kind {
|
||||
// ignore OR patterns
|
||||
PatKind::Or(_) => false,
|
||||
PatKind::Binding(_bm, _, _ident, _) => {
|
||||
let found = p.hir_id == hir_id;
|
||||
if found {
|
||||
span = Some(p.span);
|
||||
}
|
||||
!found
|
||||
},
|
||||
_ => true,
|
||||
});
|
||||
span
|
||||
}
|
||||
|
||||
fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
||||
let mut result = false;
|
||||
pat.walk(|p| {
|
||||
let is_or = matches!(p.kind, PatKind::Or(_));
|
||||
result |= is_or;
|
||||
!is_or
|
||||
});
|
||||
result
|
||||
}
|
||||
|
||||
fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
|
||||
if let Some(none_id) = tcx.lang_items().option_none_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
|
||||
if let Some(variant_id) = tcx.parent(id) {
|
||||
return variant_id == none_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -12,7 +12,8 @@ declare_clippy_lint! {
|
|||
/// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
|
||||
/// repetitive
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** The match statement may be slower due to the compiler
|
||||
/// not inlining the call to cmp. See issue #5354
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
|
|
|
@ -280,8 +280,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
|
|||
// only take assignments to fields where the left-hand side field is a field of
|
||||
// the same binding as the previous statement
|
||||
if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind;
|
||||
if let ExprKind::Path(ref qpath) = binding.kind;
|
||||
if let QPath::Resolved(_, path) = qpath;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
|
||||
if let Some(second_binding_name) = path.segments.last();
|
||||
if second_binding_name.ident.name == binding_name;
|
||||
then {
|
||||
|
|
|
@ -51,26 +51,6 @@ declare_deprecated_lint! {
|
|||
"`Vec::as_mut_slice` has been stabilized in 1.7"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This used to check for `.to_string()` method calls on values
|
||||
/// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be
|
||||
/// specialized to be as efficient as `to_owned`.
|
||||
pub STR_TO_STRING,
|
||||
"using `str::to_string` is common even today and specialization will likely happen soon"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This used to check for `.to_string()` method calls on values
|
||||
/// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be
|
||||
/// specialized to be as efficient as `clone`.
|
||||
pub STRING_TO_STRING,
|
||||
"using `string::to_string` is common even today and specialization will likely happen soon"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::utils::{
|
||||
eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint,
|
||||
span_lint_and_then,
|
||||
ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of,
|
||||
multispan_sugg, snippet, span_lint, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
|
@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
|
|||
if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
|
||||
return;
|
||||
}
|
||||
if is_valid_operator(op) && eq_expr_value(cx, left, right) {
|
||||
if is_useless_with_eq_exprs(higher::binop(op.node)) && eq_expr_value(cx, left, right) {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
|
@ -245,22 +245,3 @@ impl<'tcx> LateLintPass<'tcx> for EqOp {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_operator(op: BinOp) -> bool {
|
||||
matches!(
|
||||
op.node,
|
||||
BinOpKind::Sub
|
||||
| BinOpKind::Div
|
||||
| BinOpKind::Eq
|
||||
| BinOpKind::Lt
|
||||
| BinOpKind::Le
|
||||
| BinOpKind::Gt
|
||||
| BinOpKind::Ge
|
||||
| BinOpKind::Ne
|
||||
| BinOpKind::And
|
||||
| BinOpKind::Or
|
||||
| BinOpKind::BitXor
|
||||
| BinOpKind::BitAnd
|
||||
| BinOpKind::BitOr
|
||||
)
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
|
|||
impl<'tcx> LateLintPass<'tcx> for OkIfLet {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! { //begin checking variables
|
||||
if let ExprKind::Match(ref op, ref body, source) = expr.kind; //test if expr is a match
|
||||
if let MatchSource::IfLetDesugar { .. } = source; //test if it is an If Let
|
||||
if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let
|
||||
if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation
|
||||
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
|
|
|
@ -68,8 +68,7 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
if_chain! {
|
||||
if let StmtKind::Semi(expr, ..) = &stmt.kind;
|
||||
// make sure it's a break, otherwise we want to skip
|
||||
if let ExprKind::Break(.., break_expr) = &expr.kind;
|
||||
if let Some(break_expr) = break_expr;
|
||||
if let ExprKind::Break(.., Some(break_expr)) = &expr.kind;
|
||||
then {
|
||||
lint(cx, expr.span, break_expr.span, LINT_BREAK);
|
||||
}
|
||||
|
|
|
@ -59,8 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
if let Some(target) = subtracts_one(cx, e);
|
||||
|
||||
// Extracting out the variable name
|
||||
if let ExprKind::Path(ref assign_path) = target.kind;
|
||||
if let QPath::Resolved(_, ref ares_path) = assign_path;
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind;
|
||||
|
||||
then {
|
||||
// Handle symmetric conditions in the if statement
|
||||
|
|
|
@ -58,12 +58,12 @@ impl EarlyLintPass for ItemsAfterStatements {
|
|||
return;
|
||||
}
|
||||
|
||||
// skip initial items
|
||||
// skip initial items and trailing semicolons
|
||||
let stmts = item
|
||||
.stmts
|
||||
.iter()
|
||||
.map(|stmt| &stmt.kind)
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty));
|
||||
|
||||
// lint on all further items
|
||||
for stmt in stmts {
|
||||
|
|
|
@ -52,8 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
|||
if let ItemKind::Const(hir_ty, _) = &item.kind;
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
if let ty::Array(element_type, cst) = ty.kind();
|
||||
if let ConstKind::Value(val) = cst.val;
|
||||
if let ConstValue::Scalar(element_count) = val;
|
||||
if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
|
||||
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < element_count * element_size;
|
||||
|
|
|
@ -43,8 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
|||
if_chain! {
|
||||
if let ExprKind::Repeat(_, _) = expr.kind;
|
||||
if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if let ConstKind::Value(val) = cst.val;
|
||||
if let ConstValue::Scalar(element_count) = val;
|
||||
if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
|
||||
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < element_count * element_size;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use crate::utils::visitors::LocalUsedVisitor;
|
||||
use crate::utils::{higher, qpath_res, snippet, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::BindingAnnotation;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -66,10 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
|
||||
if let hir::StmtKind::Expr(ref if_) = expr.kind;
|
||||
if let Some((ref cond, ref then, ref else_)) = higher::if_block(&if_);
|
||||
if !used_in_expr(cx, canonical_id, cond);
|
||||
if !LocalUsedVisitor::new(canonical_id).check_expr(cond);
|
||||
if let hir::ExprKind::Block(ref then, _) = then.kind;
|
||||
if let Some(value) = check_assign(cx, canonical_id, &*then);
|
||||
if !used_in_expr(cx, canonical_id, value);
|
||||
if !LocalUsedVisitor::new(canonical_id).check_expr(value);
|
||||
then {
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
|
@ -136,32 +135,6 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
}
|
||||
}
|
||||
|
||||
struct UsedVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
id: hir::HirId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = expr.kind;
|
||||
if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id);
|
||||
if self.id == local_id;
|
||||
then {
|
||||
self.used = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_assign<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
decl: hir::HirId,
|
||||
|
@ -176,18 +149,10 @@ fn check_assign<'tcx>(
|
|||
if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id);
|
||||
if decl == local_id;
|
||||
then {
|
||||
let mut v = UsedVisitor {
|
||||
cx,
|
||||
id: decl,
|
||||
used: false,
|
||||
};
|
||||
let mut v = LocalUsedVisitor::new(decl);
|
||||
|
||||
for s in block.stmts.iter().take(block.stmts.len()-1) {
|
||||
intravisit::walk_stmt(&mut v, s);
|
||||
|
||||
if v.used {
|
||||
return None;
|
||||
}
|
||||
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(value);
|
||||
|
@ -196,9 +161,3 @@ fn check_assign<'tcx>(
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
fn used_in_expr<'tcx>(cx: &LateContext<'tcx>, id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> bool {
|
||||
let mut v = UsedVisitor { cx, id, used: false };
|
||||
intravisit::walk_expr(&mut v, expr);
|
||||
v.used
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ extern crate rustc_target;
|
|||
extern crate rustc_trait_selection;
|
||||
extern crate rustc_typeck;
|
||||
|
||||
use crate::utils::parse_msrv;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_lint::LintId;
|
||||
use rustc_session::Session;
|
||||
|
@ -171,6 +172,7 @@ mod cargo_common_metadata;
|
|||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
mod collapsible_if;
|
||||
mod collapsible_match;
|
||||
mod comparison_chain;
|
||||
mod copies;
|
||||
mod copy_iterator;
|
||||
|
@ -304,9 +306,11 @@ mod self_assignment;
|
|||
mod serde_api;
|
||||
mod shadow;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod strings;
|
||||
mod suspicious_operation_groupings;
|
||||
mod suspicious_trait_impl;
|
||||
mod swap;
|
||||
mod tabs_in_doc_comments;
|
||||
|
@ -440,14 +444,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
"clippy::unstable_as_mut_slice",
|
||||
"`Vec::as_mut_slice` has been stabilized in 1.7",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::str_to_string",
|
||||
"using `str::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::string_to_string",
|
||||
"using `string::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::misaligned_transmute",
|
||||
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
|
||||
|
@ -504,6 +500,24 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
store.register_lints(&[
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
&approx_const::APPROX_CONSTANT,
|
||||
&arithmetic::FLOAT_ARITHMETIC,
|
||||
&arithmetic::INTEGER_ARITHMETIC,
|
||||
|
@ -537,6 +551,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&checked_conversions::CHECKED_CONVERSIONS,
|
||||
&cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
&collapsible_if::COLLAPSIBLE_IF,
|
||||
&collapsible_match::COLLAPSIBLE_MATCH,
|
||||
&comparison_chain::COMPARISON_CHAIN,
|
||||
&copies::IFS_SAME_COND,
|
||||
&copies::IF_SAME_THEN_ELSE,
|
||||
|
@ -833,12 +848,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&shadow::SHADOW_SAME,
|
||||
&shadow::SHADOW_UNRELATED,
|
||||
&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
&stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
&strings::STRING_ADD,
|
||||
&strings::STRING_ADD_ASSIGN,
|
||||
&strings::STRING_FROM_UTF8_AS_BYTES,
|
||||
&strings::STRING_LIT_AS_BYTES,
|
||||
&strings::STRING_TO_STRING,
|
||||
&strings::STR_TO_STRING,
|
||||
&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
|
||||
&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
|
||||
&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
|
||||
&swap::ALMOST_SWAPPED,
|
||||
|
@ -907,15 +926,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
&unwrap_in_result::UNWRAP_IN_RESULT,
|
||||
&use_self::USE_SELF,
|
||||
&useless_conversion::USELESS_CONVERSION,
|
||||
&utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
&utils::internal_lints::DEFAULT_LINT,
|
||||
&utils::internal_lints::INVALID_PATHS,
|
||||
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
&utils::internal_lints::PRODUCE_ICE,
|
||||
&vec::USELESS_VEC,
|
||||
&vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
&verbose_file_reads::VERBOSE_FILE_READS,
|
||||
|
@ -934,14 +944,22 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
]);
|
||||
// end register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// all the internal lints
|
||||
#[cfg(feature = "internal-lints")]
|
||||
{
|
||||
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
|
||||
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
|
||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
}
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
|
||||
store.register_late_pass(|| box serde_api::SerdeAPI);
|
||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
|
||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||
store.register_late_pass(|| box utils::author::Author);
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
store.register_late_pass(move || box types::Types::new(vec_box_size_threshold));
|
||||
store.register_late_pass(|| box booleans::NonminimalBool);
|
||||
|
@ -964,12 +982,25 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box len_zero::LenZero);
|
||||
store.register_late_pass(|| box attrs::Attributes);
|
||||
store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions);
|
||||
store.register_late_pass(|| box collapsible_match::CollapsibleMatch);
|
||||
store.register_late_pass(|| box unicode::Unicode);
|
||||
store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd);
|
||||
store.register_late_pass(|| box strings::StringAdd);
|
||||
store.register_late_pass(|| box implicit_return::ImplicitReturn);
|
||||
store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub);
|
||||
store.register_late_pass(|| box methods::Methods);
|
||||
|
||||
let msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
|
||||
None
|
||||
})
|
||||
});
|
||||
|
||||
store.register_late_pass(move || box methods::Methods::new(msrv));
|
||||
store.register_late_pass(move || box matches::Matches::new(msrv));
|
||||
store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv));
|
||||
store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv));
|
||||
store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount);
|
||||
store.register_late_pass(|| box map_clone::MapClone);
|
||||
store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
|
||||
store.register_late_pass(|| box shadow::Shadow);
|
||||
|
@ -983,7 +1014,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box types::Casts);
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold));
|
||||
store.register_late_pass(|| box matches::Matches::default());
|
||||
store.register_late_pass(|| box minmax::MinMaxPass);
|
||||
store.register_late_pass(|| box open_options::OpenOptions);
|
||||
store.register_late_pass(|| box zero_div_zero::ZeroDiv);
|
||||
|
@ -1057,6 +1087,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box types::UnitArg);
|
||||
store.register_late_pass(|| box double_comparison::DoubleComparisons);
|
||||
store.register_late_pass(|| box question_mark::QuestionMark);
|
||||
store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings);
|
||||
store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl);
|
||||
store.register_late_pass(|| box map_unit_fn::MapUnit);
|
||||
store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default());
|
||||
|
@ -1107,10 +1138,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
|
||||
store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
|
||||
store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
|
||||
store.register_early_pass(|| box literal_representation::LiteralDigitGrouping);
|
||||
let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
|
||||
store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability));
|
||||
let literal_representation_threshold = conf.literal_representation_threshold;
|
||||
store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold));
|
||||
store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal);
|
||||
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
|
||||
store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold));
|
||||
store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments);
|
||||
|
@ -1124,7 +1155,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
|
||||
store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic);
|
||||
store.register_early_pass(|| box as_conversions::AsConversions);
|
||||
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
|
||||
store.register_late_pass(|| box let_underscore::LetUnderscore);
|
||||
store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
|
||||
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
|
||||
|
@ -1140,16 +1170,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box dereference::Dereferencing);
|
||||
store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
|
||||
store.register_late_pass(|| box future_not_send::FutureNotSend);
|
||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
|
||||
store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
|
||||
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
|
||||
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
|
||||
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
|
||||
store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
|
||||
store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero);
|
||||
store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn);
|
||||
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
|
||||
single_char_binding_names_threshold,
|
||||
|
@ -1166,14 +1193,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box manual_ok_or::ManualOkOr);
|
||||
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
|
||||
store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
|
||||
store.register_late_pass(|| box manual_strip::ManualStrip);
|
||||
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
|
||||
let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
|
||||
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
|
||||
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
|
||||
store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
|
||||
|
||||
store.register_late_pass(|| box strings::StrToString);
|
||||
store.register_late_pass(|| box strings::StringToString);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
@ -1192,6 +1218,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&integer_division::INTEGER_DIVISION),
|
||||
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
LintId::of(&mem_forget::MEM_FORGET),
|
||||
|
@ -1215,6 +1242,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&shadow::SHADOW_REUSE),
|
||||
LintId::of(&shadow::SHADOW_SAME),
|
||||
LintId::of(&strings::STRING_ADD),
|
||||
LintId::of(&strings::STRING_TO_STRING),
|
||||
LintId::of(&strings::STR_TO_STRING),
|
||||
LintId::of(&types::RC_BUFFER),
|
||||
LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
|
||||
|
@ -1256,7 +1285,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&loops::EXPLICIT_ITER_LOOP),
|
||||
LintId::of(¯o_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(&manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(&matches::MATCH_BOOL),
|
||||
LintId::of(&matches::MATCH_SAME_ARMS),
|
||||
|
@ -1304,6 +1332,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&wildcard_imports::WILDCARD_IMPORTS),
|
||||
]);
|
||||
|
||||
#[cfg(feature = "internal-lints")]
|
||||
store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
|
||||
LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL),
|
||||
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
|
@ -1337,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(&bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&copies::IFS_SAME_COND),
|
||||
LintId::of(&copies::IF_SAME_THEN_ELSE),
|
||||
|
@ -1533,9 +1563,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(&strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(&swap::ALMOST_SWAPPED),
|
||||
|
@ -1602,6 +1634,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(&collapsible_if::COLLAPSIBLE_IF),
|
||||
LintId::of(&collapsible_match::COLLAPSIBLE_MATCH),
|
||||
LintId::of(&comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(&doc::MISSING_SAFETY_DOC),
|
||||
|
@ -1687,6 +1720,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(&returns::LET_AND_RETURN),
|
||||
LintId::of(&returns::NEEDLESS_RETURN),
|
||||
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
|
||||
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
LintId::of(&try_err::TRY_ERR),
|
||||
|
@ -1839,6 +1873,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(®ex::INVALID_REGEX),
|
||||
LintId::of(&self_assignment::SELF_ASSIGNMENT),
|
||||
LintId::of(&serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(&swap::ALMOST_SWAPPED),
|
||||
|
@ -1930,14 +1965,6 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
|
|||
"unstable_as_mut_slice",
|
||||
"`Vec::as_mut_slice` has been stabilized in 1.7",
|
||||
);
|
||||
store.register_removed(
|
||||
"str_to_string",
|
||||
"using `str::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
store.register_removed(
|
||||
"string_to_string",
|
||||
"using `string::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
store.register_removed(
|
||||
"misaligned_transmute",
|
||||
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if a long integral or floating-point constant does
|
||||
|
@ -32,7 +32,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
pub UNREADABLE_LITERAL,
|
||||
pedantic,
|
||||
"long integer literal without underscores"
|
||||
"long literal without underscores"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -208,7 +208,13 @@ impl WarningType {
|
|||
}
|
||||
}
|
||||
|
||||
declare_lint_pass!(LiteralDigitGrouping => [
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LiteralDigitGrouping {
|
||||
lint_fraction_readability: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LiteralDigitGrouping => [
|
||||
UNREADABLE_LITERAL,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
|
@ -223,7 +229,7 @@ impl EarlyLintPass for LiteralDigitGrouping {
|
|||
}
|
||||
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
Self::check_lit(cx, lit)
|
||||
self.check_lit(cx, lit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +238,13 @@ impl EarlyLintPass for LiteralDigitGrouping {
|
|||
const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12];
|
||||
|
||||
impl LiteralDigitGrouping {
|
||||
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
|
||||
pub fn new(lint_fraction_readability: bool) -> Self {
|
||||
Self {
|
||||
lint_fraction_readability,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
|
||||
if_chain! {
|
||||
if let Some(src) = snippet_opt(cx, lit.span);
|
||||
if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit);
|
||||
|
@ -247,9 +259,12 @@ impl LiteralDigitGrouping {
|
|||
|
||||
let result = (|| {
|
||||
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?;
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
|
||||
if let Some(fraction) = num_lit.fraction {
|
||||
let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?;
|
||||
let fractional_group_size = Self::get_group_size(
|
||||
fraction.rsplit('_'),
|
||||
num_lit.radix,
|
||||
self.lint_fraction_readability)?;
|
||||
|
||||
let consistent = Self::parts_consistent(integral_group_size,
|
||||
fractional_group_size,
|
||||
|
@ -363,7 +378,11 @@ impl LiteralDigitGrouping {
|
|||
|
||||
/// Returns the size of the digit groups (or None if ungrouped) if successful,
|
||||
/// otherwise returns a `WarningType` for linting.
|
||||
fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>, radix: Radix) -> Result<Option<usize>, WarningType> {
|
||||
fn get_group_size<'a>(
|
||||
groups: impl Iterator<Item = &'a str>,
|
||||
radix: Radix,
|
||||
lint_unreadable: bool,
|
||||
) -> Result<Option<usize>, WarningType> {
|
||||
let mut groups = groups.map(str::len);
|
||||
|
||||
let first = groups.next().expect("At least one group");
|
||||
|
@ -380,7 +399,7 @@ impl LiteralDigitGrouping {
|
|||
} else {
|
||||
Ok(Some(second))
|
||||
}
|
||||
} else if first > 5 {
|
||||
} else if first > 5 && lint_unreadable {
|
||||
Err(WarningType::UnreadableLiteral)
|
||||
} else {
|
||||
Ok(None)
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::consts::constant;
|
|||
use crate::utils::paths;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::usage::{is_unused, mutated_variables};
|
||||
use crate::utils::visitors::LocalUsedVisitor;
|
||||
use crate::utils::{
|
||||
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
|
||||
indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
|
||||
|
@ -1919,8 +1920,7 @@ fn check_for_single_element_loop<'tcx>(
|
|||
if_chain! {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind;
|
||||
if let PatKind::Binding(.., target, _) = pat.kind;
|
||||
if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind;
|
||||
if let [arg_expression] = arg_expr_list;
|
||||
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
|
||||
if let ExprKind::Path(ref list_item) = arg_expression.kind;
|
||||
if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
|
||||
if let ExprKind::Block(ref block, _) = body.kind;
|
||||
|
@ -2025,8 +2025,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
|
|||
let node_str = cx.tcx.hir().get(hir_id);
|
||||
if_chain! {
|
||||
if let Node::Binding(pat) = node_str;
|
||||
if let PatKind::Binding(bind_ann, ..) = pat.kind;
|
||||
if let BindingAnnotation::Mutable = bind_ann;
|
||||
if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
|
||||
then {
|
||||
return Some(hir_id);
|
||||
}
|
||||
|
@ -2071,28 +2070,6 @@ fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
struct LocalUsedVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
local: HirId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LocalUsedVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if same_var(self.cx, expr, self.local) {
|
||||
self.used = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
struct VarVisitor<'a, 'tcx> {
|
||||
/// context reference
|
||||
cx: &'a LateContext<'tcx>,
|
||||
|
@ -2126,11 +2103,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
|||
then {
|
||||
let index_used_directly = same_var(self.cx, idx, self.var);
|
||||
let indexed_indirectly = {
|
||||
let mut used_visitor = LocalUsedVisitor {
|
||||
cx: self.cx,
|
||||
local: self.var,
|
||||
used: false,
|
||||
};
|
||||
let mut used_visitor = LocalUsedVisitor::new(self.var);
|
||||
walk_expr(&mut used_visitor, idx);
|
||||
used_visitor.used
|
||||
};
|
||||
|
@ -2950,7 +2923,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
|
|||
for ref stmt in block.stmts {
|
||||
if_chain! {
|
||||
if let StmtKind::Local(
|
||||
Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. },
|
||||
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
|
||||
init: Some(ref init_expr), .. }
|
||||
) = stmt.kind;
|
||||
if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
|
||||
|
@ -2964,6 +2937,16 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
|
|||
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
|
||||
if iter_calls.len() == 1;
|
||||
then {
|
||||
let mut used_count_visitor = UsedCountVisitor {
|
||||
cx,
|
||||
id: *pat_id,
|
||||
count: 0,
|
||||
};
|
||||
walk_block(&mut used_count_visitor, block);
|
||||
if used_count_visitor.count > 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Suggest replacing iter_call with iter_replacement, and removing stmt
|
||||
let iter_call = &iter_calls[0];
|
||||
span_lint_and_then(
|
||||
|
@ -3087,6 +3070,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
struct UsedCountVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
id: HirId,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if same_var(self.cx, expr, self.id) {
|
||||
self.count += 1;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect the occurrences of calls to `iter` or `into_iter` for the
|
||||
/// given identifier
|
||||
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind};
|
||||
use rustc_attr as attr;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
|
||||
///
|
||||
|
@ -55,10 +58,26 @@ declare_clippy_lint! {
|
|||
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
#[derive(Clone)]
|
||||
pub struct ManualNonExhaustive {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustive {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
|
||||
impl EarlyLintPass for ManualNonExhaustive {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
check_manual_non_exhaustive_enum(cx, item, &def.variants);
|
||||
|
@ -73,6 +92,8 @@ impl EarlyLintPass for ManualNonExhaustive {
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
|
||||
eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
|
||||
};
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
@ -10,13 +10,16 @@ use rustc_hir::def::Res;
|
|||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::BinOpKind;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::Span;
|
||||
|
||||
const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
|
||||
|
@ -51,7 +54,18 @@ declare_clippy_lint! {
|
|||
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualStrip => [MANUAL_STRIP]);
|
||||
pub struct ManualStrip {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualStrip {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualStrip => [MANUAL_STRIP]);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum StripKind {
|
||||
|
@ -61,6 +75,10 @@ enum StripKind {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some((cond, then, _)) = higher::if_block(&expr);
|
||||
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
|
||||
|
@ -114,6 +132,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
|
||||
|
@ -199,8 +219,7 @@ fn find_stripping<'tcx>(
|
|||
if is_ref_str(self.cx, ex);
|
||||
let unref = peel_ref(ex);
|
||||
if let ExprKind::Index(indexed, index) = &unref.kind;
|
||||
if let Some(range) = higher::range(index);
|
||||
if let higher::Range { start, end, .. } = range;
|
||||
if let Some(higher::Range { start, end, .. }) = higher::range(index);
|
||||
if let ExprKind::Path(path) = &indexed.kind;
|
||||
if qpath_res(self.cx, path, ex.hir_id) == self.target;
|
||||
then {
|
||||
|
|
|
@ -99,7 +99,7 @@ declare_clippy_lint! {
|
|||
/// }
|
||||
/// ```
|
||||
pub MAP_ERR_IGNORE,
|
||||
pedantic,
|
||||
restriction,
|
||||
"`map_err` should not ignore the original error"
|
||||
}
|
||||
|
||||
|
@ -133,9 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
|||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
body_span,
|
||||
"`map_err(|_|...` ignores the original error",
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"Consider wrapping the error in an enum variant",
|
||||
"Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ use crate::utils::sugg::Sugg;
|
|||
use crate::utils::usage::is_unused;
|
||||
use crate::utils::{
|
||||
expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
|
||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
|
||||
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks,
|
||||
snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
|
@ -20,6 +20,7 @@ use rustc_hir::{
|
|||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
@ -411,8 +412,8 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result` or
|
||||
/// `Option`
|
||||
/// **What it does:** Lint for redundant pattern matching over `Result`, `Option`,
|
||||
/// `std::task::Poll` or `std::net::IpAddr`
|
||||
///
|
||||
/// **Why is this bad?** It's more concise and clear to just use the proper
|
||||
/// utility function
|
||||
|
@ -422,10 +423,16 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::task::Poll;
|
||||
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
/// if let Ok(_) = Ok::<i32, i32>(42) {}
|
||||
/// if let Err(_) = Err::<i32, i32>(42) {}
|
||||
/// if let None = None::<()> {}
|
||||
/// if let Some(_) = Some(42) {}
|
||||
/// if let Poll::Pending = Poll::Pending::<()> {}
|
||||
/// if let Poll::Ready(_) = Poll::Ready(42) {}
|
||||
/// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
|
||||
/// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
|
||||
/// match Ok::<i32, i32>(42) {
|
||||
/// Ok(_) => true,
|
||||
/// Err(_) => false,
|
||||
|
@ -435,10 +442,16 @@ declare_clippy_lint! {
|
|||
/// The more idiomatic use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::task::Poll;
|
||||
/// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
/// if Ok::<i32, i32>(42).is_ok() {}
|
||||
/// if Err::<i32, i32>(42).is_err() {}
|
||||
/// if None::<()>.is_none() {}
|
||||
/// if Some(42).is_some() {}
|
||||
/// if Poll::Pending::<()>.is_pending() {}
|
||||
/// if Poll::Ready(42).is_ready() {}
|
||||
/// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
|
||||
/// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
|
||||
/// Ok::<i32, i32>(42).is_ok();
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN_MATCHING,
|
||||
|
@ -452,7 +465,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Why is this bad?** Readability and needless complexity.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
/// **Known problems:** This lint falsely triggers, if there are arms with
|
||||
/// `cfg` attributes that remove an arm evaluating to `false`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
|
@ -521,9 +535,20 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct Matches {
|
||||
msrv: Option<RustcVersion>,
|
||||
infallible_destructuring_match_linted: bool,
|
||||
}
|
||||
|
||||
impl Matches {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Matches::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Matches => [
|
||||
SINGLE_MATCH,
|
||||
MATCH_REF_PATS,
|
||||
|
@ -543,6 +568,8 @@ impl_lint_pass!(Matches => [
|
|||
MATCH_SAME_ARMS,
|
||||
]);
|
||||
|
||||
const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
|
||||
|
@ -550,7 +577,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
}
|
||||
|
||||
redundant_pattern_match::check(cx, expr);
|
||||
if !check_match_like_matches(cx, expr) {
|
||||
|
||||
if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
|
||||
if !check_match_like_matches(cx, expr) {
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
} else {
|
||||
lint_match_arms(cx, expr);
|
||||
}
|
||||
|
||||
|
@ -614,8 +646,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), pat.span);
|
||||
if !in_macro(pat.span);
|
||||
if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
|
||||
if let QPath::Resolved(_, ref path) = qpath;
|
||||
if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
let ty = cx.tcx.type_of(def_id);
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
|
@ -634,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
@ -922,16 +955,14 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
if let QPath::Resolved(_, p) = path {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
} else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind {
|
||||
if let QPath::Resolved(_, p) = path {
|
||||
// Some simple checks for exhaustive patterns.
|
||||
// There is a room for improvements to detect more cases,
|
||||
// but it can be more expensive to do so.
|
||||
let is_pattern_exhaustive =
|
||||
|pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
|
||||
if patterns.iter().all(is_pattern_exhaustive) {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
} else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind {
|
||||
// Some simple checks for exhaustive patterns.
|
||||
// There is a room for improvements to detect more cases,
|
||||
// but it can be more expensive to do so.
|
||||
let is_pattern_exhaustive =
|
||||
|pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
|
||||
if patterns.iter().all(is_pattern_exhaustive) {
|
||||
missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1134,13 +1165,16 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
|
|||
if b0 != b1;
|
||||
let if_guard = &b0_arms[0].guard;
|
||||
if if_guard.is_none() || b0_arms.len() == 1;
|
||||
if b0_arms[0].attrs.is_empty();
|
||||
if b0_arms[1..].iter()
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
|
||||
arm.guard.is_none()
|
||||
arm.guard.is_none() && arm.attrs.is_empty()
|
||||
});
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// The suggestion may be incorrect, because some arms can have `cfg` attributes
|
||||
// evaluated into `false` and so such arms will be stripped before.
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let pat = {
|
||||
use itertools::Itertools as _;
|
||||
b0_arms.iter()
|
||||
|
@ -1403,8 +1437,7 @@ fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
|
|||
if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
|
||||
if let ExprKind::Path(ref some_path) = e.kind;
|
||||
if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
|
||||
if let ExprKind::Path(ref qpath) = args[0].kind;
|
||||
if let &QPath::Resolved(_, ref path2) = qpath;
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind;
|
||||
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
|
||||
then {
|
||||
return Some(rb)
|
||||
|
@ -1538,6 +1571,12 @@ mod redundant_pattern_match {
|
|||
"is_err()"
|
||||
} else if match_qpath(path, &paths::OPTION_SOME) {
|
||||
"is_some()"
|
||||
} else if match_qpath(path, &paths::POLL_READY) {
|
||||
"is_ready()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V4) {
|
||||
"is_ipv4()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V6) {
|
||||
"is_ipv6()"
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -1545,7 +1584,15 @@ mod redundant_pattern_match {
|
|||
return;
|
||||
}
|
||||
},
|
||||
PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()",
|
||||
PatKind::Path(ref path) => {
|
||||
if match_qpath(path, &paths::OPTION_NONE) {
|
||||
"is_none()"
|
||||
} else if match_qpath(path, &paths::POLL_PENDING) {
|
||||
"is_pending()"
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
@ -1610,6 +1657,17 @@ mod redundant_pattern_match {
|
|||
"is_ok()",
|
||||
"is_err()",
|
||||
)
|
||||
.or_else(|| {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::IPADDR_V4,
|
||||
&paths::IPADDR_V6,
|
||||
"is_ipv4()",
|
||||
"is_ipv6()",
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1628,6 +1686,17 @@ mod redundant_pattern_match {
|
|||
"is_some()",
|
||||
"is_none()",
|
||||
)
|
||||
.or_else(|| {
|
||||
find_good_method_for_match(
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
&paths::POLL_READY,
|
||||
&paths::POLL_PENDING,
|
||||
"is_ready()",
|
||||
"is_pending()",
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -90,8 +90,7 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
|
|||
if_chain! {
|
||||
if let hir::ExprKind::Call(func, args) = &expr.kind;
|
||||
if args.is_empty();
|
||||
if let hir::ExprKind::Path(path) = &func.kind;
|
||||
if let hir::QPath::TypeRelative(_, segment) = path;
|
||||
if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
|
||||
then {
|
||||
match &*segment.ident.as_str() {
|
||||
"max_value" => return Some(MinMax::Max),
|
||||
|
|
|
@ -14,13 +14,12 @@ use if_chain::if_chain;
|
|||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
|
||||
|
@ -28,11 +27,12 @@ use crate::consts::{constant, Constant};
|
|||
use crate::utils::eager_or_lazy::is_lazyness_candidate;
|
||||
use crate::utils::usage::mutated_variables;
|
||||
use crate::utils::{
|
||||
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
|
||||
is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath,
|
||||
match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty,
|
||||
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
|
||||
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
|
||||
contains_return, contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher,
|
||||
implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
|
||||
match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
|
||||
method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
|
||||
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
|
||||
walk_ptrs_ty_depth, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -1404,7 +1404,18 @@ declare_clippy_lint! {
|
|||
"use `.collect()` instead of `::from_iter()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Methods => [
|
||||
pub struct Methods {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Methods {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Methods => [
|
||||
UNWRAP_USED,
|
||||
EXPECT_USED,
|
||||
SHOULD_IMPLEMENT_TRAIT,
|
||||
|
@ -1531,8 +1542,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
check_pointer_offset(cx, expr, arg_lists[0])
|
||||
},
|
||||
["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]),
|
||||
["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false),
|
||||
["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true),
|
||||
["map", "as_ref"] => {
|
||||
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref())
|
||||
},
|
||||
["map", "as_mut"] => {
|
||||
lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref())
|
||||
},
|
||||
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
|
||||
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
|
||||
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
|
||||
|
@ -1738,6 +1753,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
/// Checks for the `OR_FUN_CALL` lint.
|
||||
|
@ -3453,6 +3470,8 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
|||
);
|
||||
}
|
||||
|
||||
const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
||||
|
||||
/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
|
||||
fn lint_option_as_ref_deref<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
@ -3460,7 +3479,12 @@ fn lint_option_as_ref_deref<'tcx>(
|
|||
as_ref_args: &[hir::Expr<'_>],
|
||||
map_args: &[hir::Expr<'_>],
|
||||
is_mut: bool,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
|
||||
return;
|
||||
}
|
||||
|
||||
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
|
||||
|
||||
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);
|
||||
|
@ -3846,36 +3870,6 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Returns `true` if `expr` contains a return expression
|
||||
fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||
struct RetCallFinder {
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.found {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
||||
self.found = true;
|
||||
} else {
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = RetCallFinder { found: false };
|
||||
visitor.visit_expr(expr);
|
||||
visitor.found
|
||||
}
|
||||
|
||||
fn check_pointer_offset(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
if_chain! {
|
||||
if args.len() == 2;
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg;
|
|||
use crate::utils::{
|
||||
get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats,
|
||||
last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg,
|
||||
span_lint_and_then, span_lint_hir_and_then, SpanlessEq,
|
||||
span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -139,12 +139,14 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for getting the remainder of a division by one.
|
||||
/// **What it does:** Checks for getting the remainder of a division by one or minus
|
||||
/// one.
|
||||
///
|
||||
/// **Why is this bad?** The result can only ever be zero. No one will write
|
||||
/// such code deliberately, unless trying to win an Underhanded Rust
|
||||
/// Contest. Even for that contest, it's probably a bad idea. Use something more
|
||||
/// underhanded.
|
||||
/// **Why is this bad?** The result for a divisor of one can only ever be zero; for
|
||||
/// minus one it can cause panic/overflow (if the left operand is the minimal value of
|
||||
/// the respective integer type) or results in zero. No one will write such code
|
||||
/// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
|
||||
/// contest, it's probably a bad idea. Use something more underhanded.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
|
@ -152,10 +154,11 @@ declare_clippy_lint! {
|
|||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// let a = x % 1;
|
||||
/// let a = x % -1;
|
||||
/// ```
|
||||
pub MODULO_ONE,
|
||||
correctness,
|
||||
"taking a number modulo 1, which always returns 0"
|
||||
"taking a number modulo +/-1, which can either panic/overflow or always returns 0"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -378,60 +381,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
|||
return;
|
||||
},
|
||||
ExprKind::Binary(ref cmp, ref left, ref right) => {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq"
|
||||
|| name == "ne"
|
||||
|| name == "is_nan"
|
||||
|| name.starts_with("eq_")
|
||||
|| name.ends_with("_eq")
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
|
||||
let (lint, msg) = get_lint_and_message(
|
||||
is_named_constant(cx, left) || is_named_constant(cx, right),
|
||||
is_comparing_arrays,
|
||||
);
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
} else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
check_binary(cx, expr, cmp, left, right);
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
@ -744,3 +695,74 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binary(
|
||||
cx: &LateContext<'a>,
|
||||
expr: &Expr<'_>,
|
||||
cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
|
||||
left: &'a Expr<'_>,
|
||||
right: &'a Expr<'_>,
|
||||
) {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
check_nan(cx, left, expr);
|
||||
check_nan(cx, right, expr);
|
||||
check_to_owned(cx, left, right, true);
|
||||
check_to_owned(cx, right, left, false);
|
||||
}
|
||||
if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
|
||||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
|
||||
let (lint, msg) = get_lint_and_message(
|
||||
is_named_constant(cx, left) || is_named_constant(cx, right),
|
||||
is_comparing_arrays,
|
||||
);
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
|
||||
if !is_comparing_arrays {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"consider comparing them within some margin of error",
|
||||
format!(
|
||||
"({}).abs() {} error_margin",
|
||||
lhs - rhs,
|
||||
if op == BinOpKind::Eq { '<' } else { '>' }
|
||||
),
|
||||
Applicability::HasPlaceholders, // snippet
|
||||
);
|
||||
}
|
||||
diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
|
||||
});
|
||||
} else if op == BinOpKind::Rem {
|
||||
if is_integer_const(cx, right, 1) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
||||
if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
|
||||
if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULO_ONE,
|
||||
expr.span,
|
||||
"any number modulo -1 will panic/overflow or result in 0",
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use crate::utils::sugg::Sugg;
|
|||
use crate::utils::{
|
||||
higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
|
@ -198,13 +197,9 @@ struct ExpressionInfoWithSpan {
|
|||
}
|
||||
|
||||
fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
|
||||
if_chain! {
|
||||
if let ExprKind::Unary(unop, operand) = e.kind;
|
||||
if let UnOp::UnNot = unop;
|
||||
then {
|
||||
return (true, operand.span);
|
||||
}
|
||||
};
|
||||
if let ExprKind::Unary(UnOp::UnNot, operand) = e.kind {
|
||||
return (true, operand.span);
|
||||
}
|
||||
(false, e.span)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
pub UNREACHABLE,
|
||||
restriction,
|
||||
"`unreachable!` should not be present in production code"
|
||||
"usage of the `unreachable!` macro"
|
||||
}
|
||||
|
||||
declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
|
||||
|
@ -85,12 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
} else if is_expn_of(expr.span, "todo").is_some() {
|
||||
span_lint(cx, TODO, span, "`todo` should not be present in production code");
|
||||
} else if is_expn_of(expr.span, "unreachable").is_some() {
|
||||
span_lint(
|
||||
cx,
|
||||
UNREACHABLE,
|
||||
span,
|
||||
"`unreachable` should not be present in production code",
|
||||
);
|
||||
span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro");
|
||||
} else if is_expn_of(expr.span, "panic").is_some() {
|
||||
span_lint(cx, PANIC, span, "`panic` should not be present in production code");
|
||||
}
|
||||
|
|
|
@ -176,8 +176,7 @@ impl QuestionMark {
|
|||
if block.stmts.len() == 1;
|
||||
if let Some(expr) = block.stmts.iter().last();
|
||||
if let StmtKind::Semi(ref expr) = expr.kind;
|
||||
if let ExprKind::Ret(ret_expr) = expr.kind;
|
||||
if let Some(ret_expr) = ret_expr;
|
||||
if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
|
||||
|
||||
then {
|
||||
return Some(ret_expr);
|
||||
|
|
|
@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
|||
cx: &'a LateContext<'tcx>,
|
||||
path: &'tcx hir::Path<'tcx>,
|
||||
count: usize,
|
||||
};
|
||||
}
|
||||
impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
|
@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
|||
fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
|
||||
hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
};
|
||||
}
|
||||
let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
|
||||
closure_usage_count.visit_block(block);
|
||||
closure_usage_count.count
|
||||
|
|
145
clippy_lints/src/size_of_in_element_count.rs
Normal file
145
clippy_lints/src/size_of_in_element_count.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
//! Lint on use of `size_of` or `size_of_val` of T in an expression
|
||||
//! expecting a count of T
|
||||
|
||||
use crate::utils::{match_def_path, paths, span_lint_and_help};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::BinOpKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty, TyS, TypeAndMut};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects expressions where
|
||||
/// `size_of::<T>` or `size_of_val::<T>` is used as a
|
||||
/// count of elements of type `T`
|
||||
///
|
||||
/// **Why is this bad?** These functions expect a count
|
||||
/// of `T` and not a number of bytes
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,no_run
|
||||
/// # use std::ptr::copy_nonoverlapping;
|
||||
/// # use std::mem::size_of;
|
||||
/// const SIZE: usize = 128;
|
||||
/// let x = [2u8; SIZE];
|
||||
/// let mut y = [2u8; SIZE];
|
||||
/// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
|
||||
/// ```
|
||||
pub SIZE_OF_IN_ELEMENT_COUNT,
|
||||
correctness,
|
||||
"using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
|
||||
|
||||
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Ty<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(count_func, _func_args) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
|
||||
if match_def_path(cx, def_id, &paths::MEM_SIZE_OF)
|
||||
|| match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL);
|
||||
then {
|
||||
cx.typeck_results().node_substs(count_func.hir_id).types().next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => {
|
||||
get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right))
|
||||
},
|
||||
ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
const FUNCTIONS: [&[&str]; 8] = [
|
||||
&paths::COPY_NONOVERLAPPING,
|
||||
&paths::COPY,
|
||||
&paths::WRITE_BYTES,
|
||||
&paths::PTR_SWAP_NONOVERLAPPING,
|
||||
&paths::PTR_SLICE_FROM_RAW_PARTS,
|
||||
&paths::PTR_SLICE_FROM_RAW_PARTS_MUT,
|
||||
&paths::SLICE_FROM_RAW_PARTS,
|
||||
&paths::SLICE_FROM_RAW_PARTS_MUT,
|
||||
];
|
||||
const METHODS: [&str; 11] = [
|
||||
"write_bytes",
|
||||
"copy_to",
|
||||
"copy_from",
|
||||
"copy_to_nonoverlapping",
|
||||
"copy_from_nonoverlapping",
|
||||
"add",
|
||||
"wrapping_add",
|
||||
"sub",
|
||||
"wrapping_sub",
|
||||
"offset",
|
||||
"wrapping_offset",
|
||||
];
|
||||
|
||||
if_chain! {
|
||||
// Find calls to ptr::{copy, copy_nonoverlapping}
|
||||
// and ptr::{swap_nonoverlapping, write_bytes},
|
||||
if let ExprKind::Call(func, [.., count]) = expr.kind;
|
||||
if let ExprKind::Path(ref func_qpath) = func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
|
||||
if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path));
|
||||
|
||||
// Get the pointee type
|
||||
if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next();
|
||||
then {
|
||||
return Some((pointee_ty, count));
|
||||
}
|
||||
};
|
||||
if_chain! {
|
||||
// Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods
|
||||
if let ExprKind::MethodCall(method_path, _, [ptr_self, .., count], _) = expr.kind;
|
||||
let method_ident = method_path.ident.as_str();
|
||||
if METHODS.iter().any(|m| *m == &*method_ident);
|
||||
|
||||
// Get the pointee type
|
||||
if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) =
|
||||
cx.typeck_results().expr_ty(ptr_self).kind();
|
||||
then {
|
||||
return Some((pointee_ty, count));
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
const HELP_MSG: &str = "use a count of elements instead of a count of bytes\
|
||||
, it already gets multiplied by the size of the type";
|
||||
|
||||
const LINT_MSG: &str = "found a count of bytes \
|
||||
instead of a count of elements of `T`";
|
||||
|
||||
if_chain! {
|
||||
// Find calls to functions with an element count parameter and get
|
||||
// the pointee type and count parameter expression
|
||||
if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr);
|
||||
|
||||
// Find a size_of call in the count parameter expression and
|
||||
// check that it's the same type
|
||||
if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr);
|
||||
if TyS::same_type(pointee_ty, ty_used_for_size_of);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
SIZE_OF_IN_ELEMENT_COUNT,
|
||||
count_expr.span,
|
||||
LINT_MSG,
|
||||
None,
|
||||
HELP_MSG
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
|
@ -11,7 +12,7 @@ use if_chain::if_chain;
|
|||
use crate::utils::SpanlessEq;
|
||||
use crate::utils::{
|
||||
get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint,
|
||||
span_lint_and_sugg,
|
||||
span_lint_and_help, span_lint_and_sugg,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -221,8 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
if method_names[0] == sym!(as_bytes);
|
||||
|
||||
// Check for slicer
|
||||
if let ExprKind::Struct(ref path, _, _) = right.kind;
|
||||
if let QPath::LangItem(LangItem::Range, _) = path;
|
||||
if let ExprKind::Struct(QPath::LangItem(LangItem::Range, _), _, _) = right.kind;
|
||||
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
@ -289,3 +289,100 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.
|
||||
///
|
||||
/// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.
|
||||
/// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
|
||||
/// expressed with `.to_owned()`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // example code where clippy issues a warning
|
||||
/// let _ = "str".to_string();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // example code which does not raise clippy warning
|
||||
/// let _ = "str".to_owned();
|
||||
/// ```
|
||||
pub STR_TO_STRING,
|
||||
restriction,
|
||||
"using `to_string()` on a `&str`, which should be `to_owned()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StrToString => [STR_TO_STRING]);
|
||||
|
||||
impl LateLintPass<'_> for StrToString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
|
||||
if path.ident.name == sym!(to_string);
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if let ty::Ref(_, ty, ..) = ty.kind();
|
||||
if *ty.kind() == ty::Str;
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
STR_TO_STRING,
|
||||
expr.span,
|
||||
"`to_string()` called on a `&str`",
|
||||
None,
|
||||
"consider using `.to_owned()`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.
|
||||
///
|
||||
/// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.
|
||||
/// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // example code where clippy issues a warning
|
||||
/// let msg = String::from("Hello World");
|
||||
/// let _ = msg.to_string();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// // example code which does not raise clippy warning
|
||||
/// let msg = String::from("Hello World");
|
||||
/// let _ = msg.clone();
|
||||
/// ```
|
||||
pub STRING_TO_STRING,
|
||||
restriction,
|
||||
"using `to_string()` on a `String`, which should be `clone()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(StringToString => [STRING_TO_STRING]);
|
||||
|
||||
impl LateLintPass<'_> for StringToString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, args, _) = &expr.kind;
|
||||
if path.ident.name == sym!(to_string);
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if is_type_diagnostic_item(cx, ty, sym!(string_type));
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
STRING_TO_STRING,
|
||||
expr.span,
|
||||
"`to_string()` called on a `String`",
|
||||
None,
|
||||
"consider using `.clone()`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
693
clippy_lints/src/suspicious_operation_groupings.rs
Normal file
693
clippy_lints/src/suspicious_operation_groupings.rs
Normal file
|
@ -0,0 +1,693 @@
|
|||
use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
|
||||
use core::ops::{Add, AddAssign};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for unlikely usages of binary operators that are almost
|
||||
/// certainly typos and/or copy/paste errors, given the other usages
|
||||
/// of binary operators nearby.
|
||||
/// **Why is this bad?**
|
||||
/// They are probably bugs and if they aren't then they look like bugs
|
||||
/// and you should add a comment explaining why you are doing such an
|
||||
/// odd set of operations.
|
||||
/// **Known problems:**
|
||||
/// There may be some false positives if you are trying to do something
|
||||
/// unusual that happens to look like a typo.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Vec3 {
|
||||
/// x: f64,
|
||||
/// y: f64,
|
||||
/// z: f64,
|
||||
/// }
|
||||
///
|
||||
/// impl Eq for Vec3 {}
|
||||
///
|
||||
/// impl PartialEq for Vec3 {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// // This should trigger the lint because `self.x` is compared to `other.y`
|
||||
/// self.x == other.y && self.y == other.y && self.z == other.z
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # struct Vec3 {
|
||||
/// # x: f64,
|
||||
/// # y: f64,
|
||||
/// # z: f64,
|
||||
/// # }
|
||||
/// // same as above except:
|
||||
/// impl PartialEq for Vec3 {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// // Note we now compare other.x to self.x
|
||||
/// self.x == other.x && self.y == other.y && self.z == other.z
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_OPERATION_GROUPINGS,
|
||||
style,
|
||||
"groupings of binary operations that look suspiciously like typos"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]);
|
||||
|
||||
impl EarlyLintPass for SuspiciousOperationGroupings {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(binops) = extract_related_binops(&expr.kind) {
|
||||
check_binops(cx, &binops.iter().collect::<Vec<_>>());
|
||||
|
||||
let mut op_types = Vec::with_capacity(binops.len());
|
||||
// We could use a hashmap, etc. to avoid being O(n*m) here, but
|
||||
// we want the lints to be emitted in a consistent order. Besides,
|
||||
// m, (the number of distinct `BinOpKind`s in `binops`)
|
||||
// will often be small, and does have an upper limit.
|
||||
binops.iter().map(|b| b.op).for_each(|op| {
|
||||
if !op_types.contains(&op) {
|
||||
op_types.push(op);
|
||||
}
|
||||
});
|
||||
|
||||
for op_type in op_types {
|
||||
let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect();
|
||||
|
||||
check_binops(cx, &ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) {
|
||||
let binop_count = binops.len();
|
||||
if binop_count < 2 {
|
||||
// Single binary operation expressions would likely be false
|
||||
// positives.
|
||||
return;
|
||||
}
|
||||
|
||||
let mut one_ident_difference_count = 0;
|
||||
let mut no_difference_info = None;
|
||||
let mut double_difference_info = None;
|
||||
let mut expected_ident_loc = None;
|
||||
|
||||
let mut paired_identifiers = FxHashSet::default();
|
||||
|
||||
for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() {
|
||||
match ident_difference_expr(left, right) {
|
||||
IdentDifference::NoDifference => {
|
||||
if is_useless_with_eq_exprs(*op) {
|
||||
// The `eq_op` lint should catch this in this case.
|
||||
return;
|
||||
}
|
||||
|
||||
no_difference_info = Some(i);
|
||||
},
|
||||
IdentDifference::Single(ident_loc) => {
|
||||
one_ident_difference_count += 1;
|
||||
if let Some(previous_expected) = expected_ident_loc {
|
||||
if previous_expected != ident_loc {
|
||||
// This expression doesn't match the form we're
|
||||
// looking for.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
expected_ident_loc = Some(ident_loc);
|
||||
}
|
||||
|
||||
// If there was only a single difference, all other idents
|
||||
// must have been the same, and thus were paired.
|
||||
for id in skip_index(IdentIter::from(*left), ident_loc.index) {
|
||||
paired_identifiers.insert(id);
|
||||
}
|
||||
},
|
||||
IdentDifference::Double(ident_loc1, ident_loc2) => {
|
||||
double_difference_info = Some((i, ident_loc1, ident_loc2));
|
||||
},
|
||||
IdentDifference::Multiple | IdentDifference::NonIdent => {
|
||||
// It's too hard to know whether this is a bug or not.
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
if let Some(expected_loc) = expected_ident_loc {
|
||||
match (no_difference_info, double_difference_info) {
|
||||
(Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc),
|
||||
(None, Some((double_difference_index, ident_loc1, ident_loc2))) => {
|
||||
if_chain! {
|
||||
if one_ident_difference_count == binop_count - 1;
|
||||
if let Some(binop) = binops.get(double_difference_index);
|
||||
then {
|
||||
let changed_loc = if ident_loc1 == expected_loc {
|
||||
ident_loc2
|
||||
} else if ident_loc2 == expected_loc {
|
||||
ident_loc1
|
||||
} else {
|
||||
// This expression doesn't match the form we're
|
||||
// looking for.
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(sugg) = ident_swap_sugg(
|
||||
cx,
|
||||
&paired_identifiers,
|
||||
binop,
|
||||
changed_loc,
|
||||
&mut applicability,
|
||||
) {
|
||||
emit_suggestion(
|
||||
cx,
|
||||
binop.span,
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn attempt_to_emit_no_difference_lint(
|
||||
cx: &EarlyContext<'_>,
|
||||
binops: &[&BinaryOp<'_>],
|
||||
i: usize,
|
||||
expected_loc: IdentLocation,
|
||||
) {
|
||||
if let Some(binop) = binops.get(i).cloned() {
|
||||
// We need to try and figure out which identifier we should
|
||||
// suggest using instead. Since there could be multiple
|
||||
// replacement candidates in a given expression, and we're
|
||||
// just taking the first one, we may get some bad lint
|
||||
// messages.
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
|
||||
// We assume that the correct ident is one used elsewhere in
|
||||
// the other binops, in a place that there was a single
|
||||
// difference between idents before.
|
||||
let old_left_ident = get_ident(binop.left, expected_loc);
|
||||
let old_right_ident = get_ident(binop.right, expected_loc);
|
||||
|
||||
for b in skip_index(binops.iter(), i) {
|
||||
if_chain! {
|
||||
if let (Some(old_ident), Some(new_ident)) =
|
||||
(old_left_ident, get_ident(b.left, expected_loc));
|
||||
if old_ident != new_ident;
|
||||
if let Some(sugg) = suggestion_with_swapped_ident(
|
||||
cx,
|
||||
binop.left,
|
||||
expected_loc,
|
||||
new_ident,
|
||||
&mut applicability,
|
||||
);
|
||||
then {
|
||||
emit_suggestion(
|
||||
cx,
|
||||
binop.span,
|
||||
replace_left_sugg(cx, &binop, &sugg, &mut applicability),
|
||||
applicability,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let (Some(old_ident), Some(new_ident)) =
|
||||
(old_right_ident, get_ident(b.right, expected_loc));
|
||||
if old_ident != new_ident;
|
||||
if let Some(sugg) = suggestion_with_swapped_ident(
|
||||
cx,
|
||||
binop.right,
|
||||
expected_loc,
|
||||
new_ident,
|
||||
&mut applicability,
|
||||
);
|
||||
then {
|
||||
emit_suggestion(
|
||||
cx,
|
||||
binop.span,
|
||||
replace_right_sugg(cx, &binop, &sugg, &mut applicability),
|
||||
applicability,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUSPICIOUS_OPERATION_GROUPINGS,
|
||||
span,
|
||||
"This sequence of operators looks suspiciously like a bug.",
|
||||
"I think you meant",
|
||||
sugg,
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
|
||||
fn ident_swap_sugg(
|
||||
cx: &EarlyContext<'_>,
|
||||
paired_identifiers: &FxHashSet<Ident>,
|
||||
binop: &BinaryOp<'_>,
|
||||
location: IdentLocation,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<String> {
|
||||
let left_ident = get_ident(&binop.left, location)?;
|
||||
let right_ident = get_ident(&binop.right, location)?;
|
||||
|
||||
let sugg = match (
|
||||
paired_identifiers.contains(&left_ident),
|
||||
paired_identifiers.contains(&right_ident),
|
||||
) {
|
||||
(true, true) | (false, false) => {
|
||||
// We don't have a good guess of what ident should be
|
||||
// used instead, in these cases.
|
||||
*applicability = Applicability::MaybeIncorrect;
|
||||
|
||||
// We arbitraily choose one side to suggest changing,
|
||||
// since we don't have a better guess. If the user
|
||||
// ends up duplicating a clause, the `logic_bug` lint
|
||||
// should catch it.
|
||||
|
||||
let right_suggestion =
|
||||
suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?;
|
||||
|
||||
replace_right_sugg(cx, binop, &right_suggestion, applicability)
|
||||
},
|
||||
(false, true) => {
|
||||
// We haven't seen a pair involving the left one, so
|
||||
// it's probably what is wanted.
|
||||
|
||||
let right_suggestion =
|
||||
suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?;
|
||||
|
||||
replace_right_sugg(cx, binop, &right_suggestion, applicability)
|
||||
},
|
||||
(true, false) => {
|
||||
// We haven't seen a pair involving the right one, so
|
||||
// it's probably what is wanted.
|
||||
let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?;
|
||||
|
||||
replace_left_sugg(cx, binop, &left_suggestion, applicability)
|
||||
},
|
||||
};
|
||||
|
||||
Some(sugg)
|
||||
}
|
||||
|
||||
fn replace_left_sugg(
|
||||
cx: &EarlyContext<'_>,
|
||||
binop: &BinaryOp<'_>,
|
||||
left_suggestion: &str,
|
||||
applicability: &mut Applicability,
|
||||
) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
left_suggestion,
|
||||
binop.op.to_string(),
|
||||
snippet_with_applicability(cx, binop.right.span, "..", applicability),
|
||||
)
|
||||
}
|
||||
|
||||
fn replace_right_sugg(
|
||||
cx: &EarlyContext<'_>,
|
||||
binop: &BinaryOp<'_>,
|
||||
right_suggestion: &str,
|
||||
applicability: &mut Applicability,
|
||||
) -> String {
|
||||
format!(
|
||||
"{} {} {}",
|
||||
snippet_with_applicability(cx, binop.left.span, "..", applicability),
|
||||
binop.op.to_string(),
|
||||
right_suggestion,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct BinaryOp<'exprs> {
|
||||
op: BinOpKind,
|
||||
span: Span,
|
||||
left: &'exprs Expr,
|
||||
right: &'exprs Expr,
|
||||
}
|
||||
|
||||
impl BinaryOp<'exprs> {
|
||||
fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
|
||||
Self { op, span, left, right }
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_non_ident_wrappers(expr: &Expr) -> &Expr {
|
||||
let mut output = expr;
|
||||
loop {
|
||||
output = match &output.kind {
|
||||
ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner,
|
||||
_ => {
|
||||
return output;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_related_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
|
||||
append_opt_vecs(chained_binops(kind), if_statment_binops(kind))
|
||||
}
|
||||
|
||||
fn if_statment_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
|
||||
match kind {
|
||||
ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind),
|
||||
ExprKind::Paren(ref e) => if_statment_binops(&e.kind),
|
||||
ExprKind::Block(ref block, _) => {
|
||||
let mut output = None;
|
||||
for stmt in &block.stmts {
|
||||
match stmt.kind {
|
||||
StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => {
|
||||
output = append_opt_vecs(output, if_statment_binops(&e.kind));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
output
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> Option<Vec<A>> {
|
||||
match (target_opt, source_opt) {
|
||||
(Some(mut target), Some(mut source)) => {
|
||||
target.reserve(source.len());
|
||||
for op in source.drain(..) {
|
||||
target.push(op);
|
||||
}
|
||||
Some(target)
|
||||
},
|
||||
(Some(v), None) | (None, Some(v)) => Some(v),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
|
||||
match kind {
|
||||
ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer),
|
||||
ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
|
||||
match (&left_outer.kind, &right_outer.kind) {
|
||||
(
|
||||
ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
|
||||
ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e),
|
||||
) => chained_binops_helper(left_e, right_e),
|
||||
(ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer),
|
||||
(_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => {
|
||||
chained_binops_helper(left_outer, right_e)
|
||||
},
|
||||
(
|
||||
ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right),
|
||||
ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right),
|
||||
) => match (
|
||||
chained_binops_helper(left_left, left_right),
|
||||
chained_binops_helper(right_left, right_right),
|
||||
) {
|
||||
(Some(mut left_ops), Some(mut right_ops)) => {
|
||||
left_ops.reserve(right_ops.len());
|
||||
for op in right_ops.drain(..) {
|
||||
left_ops.push(op);
|
||||
}
|
||||
Some(left_ops)
|
||||
},
|
||||
(Some(mut left_ops), _) => {
|
||||
left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)));
|
||||
Some(left_ops)
|
||||
},
|
||||
(_, Some(mut right_ops)) => {
|
||||
right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)));
|
||||
Some(right_ops)
|
||||
},
|
||||
(None, None) => Some(vec![
|
||||
BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)),
|
||||
BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)),
|
||||
]),
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||
struct IdentLocation {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl Add for IdentLocation {
|
||||
type Output = IdentLocation;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
Self {
|
||||
index: self.index + other.index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for IdentLocation {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum IdentDifference {
|
||||
NoDifference,
|
||||
Single(IdentLocation),
|
||||
Double(IdentLocation, IdentLocation),
|
||||
Multiple,
|
||||
NonIdent,
|
||||
}
|
||||
|
||||
impl Add for IdentDifference {
|
||||
type Output = IdentDifference;
|
||||
|
||||
fn add(self, other: Self) -> Self::Output {
|
||||
match (self, other) {
|
||||
(Self::NoDifference, output) | (output, Self::NoDifference) => output,
|
||||
(Self::Multiple, _)
|
||||
| (_, Self::Multiple)
|
||||
| (Self::Double(_, _), Self::Single(_))
|
||||
| (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple,
|
||||
(Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent,
|
||||
(Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for IdentDifference {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentDifference {
|
||||
/// Returns true if learning about more differences will not change the value
|
||||
/// of this `IdentDifference`, and false otherwise.
|
||||
fn is_complete(&self) -> bool {
|
||||
match self {
|
||||
Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false,
|
||||
Self::Multiple | Self::NonIdent => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference {
|
||||
ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0
|
||||
}
|
||||
|
||||
fn ident_difference_expr_with_base_location(
|
||||
left: &Expr,
|
||||
right: &Expr,
|
||||
mut base: IdentLocation,
|
||||
) -> (IdentDifference, IdentLocation) {
|
||||
// Ideally, this function should not use IdentIter because it should return
|
||||
// early if the expressions have any non-ident differences. We want that early
|
||||
// return because if without that restriction the lint would lead to false
|
||||
// positives.
|
||||
//
|
||||
// But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need
|
||||
// the two expressions to be walked in lockstep. And without a `Visitor`, we'd
|
||||
// have to do all the AST traversal ourselves, which is a lot of work, since to
|
||||
// do it properly we'd need to be able to handle more or less every possible
|
||||
// AST node since `Item`s can be written inside `Expr`s.
|
||||
//
|
||||
// In practice, it seems likely that expressions, above a certain size, that
|
||||
// happen to use the exact same idents in the exact same order, and which are
|
||||
// not structured the same, would be rare. Therefore it seems likely that if
|
||||
// we do only the first layer of matching ourselves and eventually fallback on
|
||||
// IdentIter, then the output of this function will be almost always be correct
|
||||
// in practice.
|
||||
//
|
||||
// If it turns out that problematic cases are more prelavent than we assume,
|
||||
// then we should be able to change this function to do the correct traversal,
|
||||
// without needing to change the rest of the code.
|
||||
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
use ExprKind::*;
|
||||
|
||||
match (
|
||||
&strip_non_ident_wrappers(left).kind,
|
||||
&strip_non_ident_wrappers(right).kind,
|
||||
) {
|
||||
(Yield(_), Yield(_))
|
||||
| (Try(_), Try(_))
|
||||
| (Paren(_), Paren(_))
|
||||
| (Repeat(_, _), Repeat(_, _))
|
||||
| (Struct(_, _, _), Struct(_, _, _))
|
||||
| (MacCall(_), MacCall(_))
|
||||
| (LlvmInlineAsm(_), LlvmInlineAsm(_))
|
||||
| (InlineAsm(_), InlineAsm(_))
|
||||
| (Ret(_), Ret(_))
|
||||
| (Continue(_), Continue(_))
|
||||
| (Break(_, _), Break(_, _))
|
||||
| (AddrOf(_, _, _), AddrOf(_, _, _))
|
||||
| (Path(_, _), Path(_, _))
|
||||
| (Range(_, _, _), Range(_, _, _))
|
||||
| (Index(_, _), Index(_, _))
|
||||
| (Field(_, _), Field(_, _))
|
||||
| (AssignOp(_, _, _), AssignOp(_, _, _))
|
||||
| (Assign(_, _, _), Assign(_, _, _))
|
||||
| (TryBlock(_), TryBlock(_))
|
||||
| (Await(_), Await(_))
|
||||
| (Async(_, _, _), Async(_, _, _))
|
||||
| (Block(_, _), Block(_, _))
|
||||
| (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _))
|
||||
| (Match(_, _), Match(_, _))
|
||||
| (Loop(_, _), Loop(_, _))
|
||||
| (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
|
||||
| (While(_, _, _), While(_, _, _))
|
||||
| (If(_, _, _), If(_, _, _))
|
||||
| (Let(_, _), Let(_, _))
|
||||
| (Type(_, _), Type(_, _))
|
||||
| (Cast(_, _), Cast(_, _))
|
||||
| (Lit(_), Lit(_))
|
||||
| (Unary(_, _), Unary(_, _))
|
||||
| (Binary(_, _, _), Binary(_, _, _))
|
||||
| (Tup(_), Tup(_))
|
||||
| (MethodCall(_, _, _), MethodCall(_, _, _))
|
||||
| (Call(_, _), Call(_, _))
|
||||
| (ConstBlock(_), ConstBlock(_))
|
||||
| (Array(_), Array(_))
|
||||
| (Box(_), Box(_)) => {
|
||||
// keep going
|
||||
},
|
||||
_ => {
|
||||
return (IdentDifference::NonIdent, base);
|
||||
},
|
||||
}
|
||||
|
||||
let mut difference = IdentDifference::NoDifference;
|
||||
|
||||
for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) {
|
||||
let (new_difference, new_base) =
|
||||
ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base);
|
||||
base = new_base;
|
||||
difference += new_difference;
|
||||
if difference.is_complete() {
|
||||
return (difference, base);
|
||||
}
|
||||
}
|
||||
|
||||
let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base);
|
||||
base = new_base;
|
||||
difference += new_difference;
|
||||
|
||||
(difference, base)
|
||||
}
|
||||
|
||||
fn ident_difference_via_ident_iter_with_base_location<Iterable: Into<IdentIter>>(
|
||||
left: Iterable,
|
||||
right: Iterable,
|
||||
mut base: IdentLocation,
|
||||
) -> (IdentDifference, IdentLocation) {
|
||||
// See the note in `ident_difference_expr_with_base_location` about `IdentIter`
|
||||
let mut difference = IdentDifference::NoDifference;
|
||||
|
||||
let mut left_iterator = left.into();
|
||||
let mut right_iterator = right.into();
|
||||
|
||||
loop {
|
||||
match (left_iterator.next(), right_iterator.next()) {
|
||||
(Some(left_ident), Some(right_ident)) => {
|
||||
if !eq_id(left_ident, right_ident) {
|
||||
difference += IdentDifference::Single(base);
|
||||
if difference.is_complete() {
|
||||
return (difference, base);
|
||||
}
|
||||
}
|
||||
},
|
||||
(Some(_), None) | (None, Some(_)) => {
|
||||
return (IdentDifference::NonIdent, base);
|
||||
},
|
||||
(None, None) => {
|
||||
return (difference, base);
|
||||
},
|
||||
}
|
||||
base += IdentLocation { index: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ident(expr: &Expr, location: IdentLocation) -> Option<Ident> {
|
||||
IdentIter::from(expr).nth(location.index)
|
||||
}
|
||||
|
||||
fn suggestion_with_swapped_ident(
|
||||
cx: &EarlyContext<'_>,
|
||||
expr: &Expr,
|
||||
location: IdentLocation,
|
||||
new_ident: Ident,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<String> {
|
||||
get_ident(expr, location).and_then(|current_ident| {
|
||||
if eq_id(current_ident, new_ident) {
|
||||
// We never want to suggest a non-change
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(format!(
|
||||
"{}{}{}",
|
||||
snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability),
|
||||
new_ident.to_string(),
|
||||
snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
|
||||
where
|
||||
Iter: Iterator<Item = A>,
|
||||
{
|
||||
iter.enumerate()
|
||||
.filter_map(move |(i, a)| if i == index { None } else { Some(a) })
|
||||
}
|
|
@ -168,8 +168,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
|||
if_chain! {
|
||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||
if !in_macro(bound_predicate.span);
|
||||
if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind;
|
||||
if let QPath::Resolved(_, Path { ref segments, .. }) = path;
|
||||
if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind;
|
||||
if let Some(segment) = segments.first();
|
||||
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
|
||||
then {
|
||||
|
|
|
@ -48,8 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
|||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = args[0].kind;
|
||||
let x = const_eval_context.expr(&args[0]);
|
||||
if let Some(constant) = x;
|
||||
if let Constant::RawPtr(0) = constant;
|
||||
if let Some(Constant::RawPtr(0)) = x;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use if_chain::if_chain;
|
|||
use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{
|
||||
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
|
||||
|
@ -737,8 +738,7 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
|
|||
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
|
||||
if let Some(node) = cx.tcx.hir().get_if_local(did);
|
||||
if let Node::GenericParam(generic_param) = node;
|
||||
if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
|
||||
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
|
||||
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
|
||||
then {
|
||||
|
@ -1469,8 +1469,7 @@ fn check_loss_of_sign(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast
|
|||
// don't lint for positive constants
|
||||
let const_val = constant(cx, &cx.typeck_results(), op);
|
||||
if_chain! {
|
||||
if let Some((const_val, _)) = const_val;
|
||||
if let Constant::Int(n) = const_val;
|
||||
if let Some((Constant::Int(n), _)) = const_val;
|
||||
if let ty::Int(ity) = *cast_from.kind();
|
||||
if sext(cx.tcx, n, ity) >= 0;
|
||||
then {
|
||||
|
@ -1632,7 +1631,14 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Cast(ref ex, _) = expr.kind {
|
||||
if let ExprKind::Cast(ref ex, cast_to) = expr.kind {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind {
|
||||
if let Res::Def(_, def_id) = path.res {
|
||||
if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr));
|
||||
lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
|
||||
if let Some(lit) = get_numeric_literal(ex) {
|
||||
|
@ -1711,7 +1717,7 @@ fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &st
|
|||
expr.span,
|
||||
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
|
||||
"try",
|
||||
format!("{}_{}", literal_str, cast_to),
|
||||
format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::{
|
||||
in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
|
||||
contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then,
|
||||
visitors::find_all_ret_expressions,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
|
@ -95,6 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
if let ExprKind::Path(ref qpath) = func.kind;
|
||||
if match_qpath(qpath, path);
|
||||
if args.len() == 1;
|
||||
if !contains_return(&args[0]);
|
||||
then {
|
||||
suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string()));
|
||||
true
|
||||
|
@ -134,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
diag.multipart_suggestion(
|
||||
"...and change the returning expressions",
|
||||
suggs,
|
||||
Applicability::MachineApplicable,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -10,6 +10,17 @@ use rustc_ast::{self as ast, *};
|
|||
use rustc_span::symbol::Ident;
|
||||
use std::mem;
|
||||
|
||||
pub mod ident_iter;
|
||||
pub use ident_iter::IdentIter;
|
||||
|
||||
pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
|
||||
use BinOpKind::*;
|
||||
matches!(
|
||||
kind,
|
||||
Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
|
||||
pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
|
||||
|
|
45
clippy_lints/src/utils/ast_utils/ident_iter.rs
Normal file
45
clippy_lints/src/utils/ast_utils/ident_iter.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use core::iter::FusedIterator;
|
||||
use rustc_ast::visit::{walk_attribute, walk_expr, Visitor};
|
||||
use rustc_ast::{Attribute, Expr};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
pub struct IdentIter(std::vec::IntoIter<Ident>);
|
||||
|
||||
impl Iterator for IdentIter {
|
||||
type Item = Ident;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for IdentIter {}
|
||||
|
||||
impl From<&Expr> for IdentIter {
|
||||
fn from(expr: &Expr) -> Self {
|
||||
let mut visitor = IdentCollector::default();
|
||||
|
||||
walk_expr(&mut visitor, expr);
|
||||
|
||||
IdentIter(visitor.0.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Attribute> for IdentIter {
|
||||
fn from(attr: &Attribute) -> Self {
|
||||
let mut visitor = IdentCollector::default();
|
||||
|
||||
walk_attribute(&mut visitor, attr);
|
||||
|
||||
IdentIter(visitor.0.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct IdentCollector(Vec<Ident>);
|
||||
|
||||
impl Visitor<'_> for IdentCollector {
|
||||
fn visit_ident(&mut self, ident: Ident) {
|
||||
self.0.push(ident);
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
|
|||
DeprecationStatus::Replaced("cognitive_complexity"),
|
||||
),
|
||||
("dump", DeprecationStatus::None),
|
||||
("msrv", DeprecationStatus::None),
|
||||
];
|
||||
|
||||
pub struct LimitStack {
|
||||
|
@ -123,6 +124,24 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
|
||||
let mut unique_attr = None;
|
||||
for attr in get_attr(sess, attrs, name) {
|
||||
match attr.style {
|
||||
ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
|
||||
ast::AttrStyle::Inner => {
|
||||
sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name))
|
||||
.span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
|
||||
.emit();
|
||||
},
|
||||
ast::AttrStyle::Outer => {
|
||||
sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
|
||||
},
|
||||
}
|
||||
}
|
||||
unique_attr
|
||||
}
|
||||
|
||||
/// Return true if the attributes contain any of `proc_macro`,
|
||||
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
|
||||
pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
||||
|
|
|
@ -106,6 +106,8 @@ macro_rules! define_Conf {
|
|||
|
||||
pub use self::helpers::Conf;
|
||||
define_Conf! {
|
||||
/// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
|
||||
(msrv, "msrv": Option<String>, None),
|
||||
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
|
||||
/// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
|
||||
|
@ -168,6 +170,8 @@ define_Conf! {
|
|||
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
|
||||
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
|
||||
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
|
||||
(unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
|
||||
}
|
||||
|
||||
impl Default for Conf {
|
||||
|
|
|
@ -186,7 +186,9 @@ pub fn span_lint_hir_and_then(
|
|||
/// |
|
||||
/// = note: `-D fold-any` implied by `-D warnings`
|
||||
/// ```
|
||||
#[allow(clippy::collapsible_span_lint_calls)]
|
||||
|
||||
#[allow(clippy::unknown_clippy_lints)]
|
||||
#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
|
||||
pub fn span_lint_and_sugg<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
|
|
|
@ -162,8 +162,7 @@ pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<
|
|||
if let hir::Block { expr: Some(expr), .. } = &**block;
|
||||
if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
|
||||
if let hir::ExprKind::DropTemps(cond) = &cond.kind;
|
||||
if let [arm, ..] = &arms[..];
|
||||
if let hir::Arm { body, .. } = arm;
|
||||
if let [hir::Arm { body, .. }, ..] = &arms[..];
|
||||
then {
|
||||
return Some((cond, body));
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
match (&left.kind, &right.kind) {
|
||||
match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
|
||||
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
|
||||
},
|
||||
|
@ -306,6 +306,32 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Some simple reductions like `{ return }` => `return`
|
||||
fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> {
|
||||
if let ExprKind::Block(block, _) = kind {
|
||||
match (block.stmts, block.expr) {
|
||||
// `{}` => `()`
|
||||
([], None) => &ExprKind::Tup(&[]),
|
||||
([], Some(expr)) => match expr.kind {
|
||||
// `{ return .. }` => `return ..`
|
||||
ExprKind::Ret(..) => &expr.kind,
|
||||
_ => kind,
|
||||
},
|
||||
([stmt], None) => match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
|
||||
// `{ return ..; }` => `return ..`
|
||||
ExprKind::Ret(..) => &expr.kind,
|
||||
_ => kind,
|
||||
},
|
||||
_ => kind,
|
||||
},
|
||||
_ => kind,
|
||||
}
|
||||
} else {
|
||||
kind
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_binop<'a>(
|
||||
binop: BinOpKind,
|
||||
lhs: &'a Expr<'a>,
|
||||
|
|
|
@ -14,6 +14,7 @@ pub mod eager_or_lazy;
|
|||
pub mod higher;
|
||||
mod hir_utils;
|
||||
pub mod inspector;
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub mod internal_lints;
|
||||
pub mod numeric_literal;
|
||||
pub mod paths;
|
||||
|
@ -51,6 +52,8 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::sym as rustc_sym;
|
||||
|
@ -62,6 +65,49 @@ use smallvec::SmallVec;
|
|||
|
||||
use crate::consts::{constant, Constant};
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||
return Some(version);
|
||||
} else if let Some(sess) = sess {
|
||||
if let Some(span) = span {
|
||||
sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
|
||||
msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
|
||||
}
|
||||
|
||||
macro_rules! extract_msrv_attr {
|
||||
(LateContext) => {
|
||||
extract_msrv_attr!(@LateContext, ());
|
||||
};
|
||||
(EarlyContext) => {
|
||||
extract_msrv_attr!(@EarlyContext);
|
||||
};
|
||||
(@$context:ident$(, $call:tt)?) => {
|
||||
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
|
||||
use $crate::utils::get_unique_inner_attr;
|
||||
match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
|
||||
Some(msrv_attr) => {
|
||||
if let Some(msrv) = msrv_attr.value_str() {
|
||||
self.msrv = $crate::utils::parse_msrv(
|
||||
&msrv.to_string(),
|
||||
Some(cx.sess$($call)?),
|
||||
Some(msrv_attr.span),
|
||||
);
|
||||
} else {
|
||||
cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns `true` if the two spans come from differing expansions (i.e., one is
|
||||
/// from a macro and one isn't).
|
||||
#[must_use]
|
||||
|
@ -527,6 +573,36 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
|
|||
cn.result
|
||||
}
|
||||
|
||||
/// Returns `true` if `expr` contains a return expression
|
||||
pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||
struct RetCallFinder {
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.found {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
||||
self.found = true;
|
||||
} else {
|
||||
hir::intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
|
||||
hir::intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = RetCallFinder { found: false };
|
||||
visitor.visit_expr(expr);
|
||||
visitor.found
|
||||
}
|
||||
|
||||
/// Converts a span to a code snippet if available, otherwise use default.
|
||||
///
|
||||
/// This is useful if you want to provide suggestions for your lint or more generally, if you want
|
||||
|
|
|
@ -20,6 +20,8 @@ pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
|
|||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
|
||||
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
|
||||
pub const COPY: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||
pub const COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
|
||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
|
||||
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
|
||||
|
@ -31,6 +33,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
|
|||
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
|
||||
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
|
||||
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
|
@ -58,9 +61,13 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"];
|
|||
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
|
||||
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
|
||||
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
|
||||
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
|
||||
|
@ -68,6 +75,8 @@ pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "Manua
|
|||
pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
|
||||
pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
|
||||
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
|
||||
pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
|
||||
pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
|
||||
pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
|
||||
|
@ -90,9 +99,14 @@ pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
|
|||
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
|
||||
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
|
||||
pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
|
||||
pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
|
||||
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
|
||||
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
|
||||
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
|
||||
|
@ -114,6 +128,8 @@ pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGu
|
|||
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||
pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
|
||||
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
|
||||
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
|
||||
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
|
||||
|
@ -129,6 +145,7 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
|
|||
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
|
||||
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
|
||||
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
|
||||
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
|
||||
|
@ -146,3 +163,4 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
|
|||
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
|
||||
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
|
||||
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
|
||||
pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
|
||||
|
@ -123,3 +125,54 @@ where
|
|||
!ret_finder.failed
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalUsedVisitor {
|
||||
pub local_hir_id: HirId,
|
||||
pub used: bool,
|
||||
}
|
||||
|
||||
impl LocalUsedVisitor {
|
||||
pub fn new(local_hir_id: HirId) -> Self {
|
||||
Self {
|
||||
local_hir_id,
|
||||
used: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
|
||||
visit(self, t);
|
||||
std::mem::replace(&mut self.used, false)
|
||||
}
|
||||
|
||||
pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool {
|
||||
self.check(arm, Self::visit_arm)
|
||||
}
|
||||
|
||||
pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool {
|
||||
self.check(expr, Self::visit_expr)
|
||||
}
|
||||
|
||||
pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool {
|
||||
self.check(stmt, Self::visit_stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for LocalUsedVisitor {
|
||||
type Map = Map<'v>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'v Expr<'v>) {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
|
||||
if let Res::Local(id) = path.res {
|
||||
if id == self.local_hir_id {
|
||||
self.used = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
mod cargo;
|
||||
|
||||
// whether to run internal tests or not
|
||||
const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
|
||||
|
||||
fn host_lib() -> PathBuf {
|
||||
option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from)
|
||||
}
|
||||
|
@ -96,6 +99,16 @@ fn run_mode(cfg: &mut compiletest::Config) {
|
|||
compiletest::run_tests(&cfg);
|
||||
}
|
||||
|
||||
fn run_internal_tests(cfg: &mut compiletest::Config) {
|
||||
// only run internal tests with the internal-tests feature
|
||||
if !RUN_INTERNAL_TESTS {
|
||||
return;
|
||||
}
|
||||
cfg.mode = TestMode::Ui;
|
||||
cfg.src_base = Path::new("tests").join("ui-internal");
|
||||
compiletest::run_tests(&cfg);
|
||||
}
|
||||
|
||||
fn run_ui_toml(config: &mut compiletest::Config) {
|
||||
fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
|
||||
let mut result = true;
|
||||
|
@ -199,7 +212,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) {
|
|||
Some("main.rs") => {},
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
let paths = compiletest::common::TestPaths {
|
||||
file: file_path,
|
||||
base: config.src_base.clone(),
|
||||
|
@ -253,4 +265,5 @@ fn compile_test() {
|
|||
run_mode(&mut config);
|
||||
run_ui_toml(&mut config);
|
||||
run_ui_cargo(&mut config);
|
||||
run_internal_tests(&mut config);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ fn dogfood_clippy() {
|
|||
}
|
||||
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
let output = Command::new(&*CLIPPY_PATH)
|
||||
let mut command = Command::new(&*CLIPPY_PATH);
|
||||
command
|
||||
.current_dir(root_dir)
|
||||
.env("CLIPPY_DOGFOOD", "1")
|
||||
.env("CARGO_INCREMENTAL", "0")
|
||||
|
@ -27,11 +28,16 @@ fn dogfood_clippy() {
|
|||
.arg("--all-features")
|
||||
.arg("--")
|
||||
.args(&["-D", "clippy::all"])
|
||||
.args(&["-D", "clippy::internal"])
|
||||
.args(&["-D", "clippy::pedantic"])
|
||||
.arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
|
||||
.output()
|
||||
.unwrap();
|
||||
.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
|
||||
|
||||
// internal lints only exist if we build with the internal-lints feature
|
||||
if cfg!(feature = "internal-lints") {
|
||||
command.args(&["-D", "clippy::internal"]);
|
||||
}
|
||||
|
||||
let output = command.output().unwrap();
|
||||
|
||||
println!("status: {}", output.status);
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
|
|
1
tests/ui-toml/invalid_min_rust_version/clippy.toml
Normal file
1
tests/ui-toml/invalid_min_rust_version/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
msrv = "invalid.version"
|
|
@ -0,0 +1,3 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,4 @@
|
|||
error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version
|
||||
|
||||
error: aborting due to previous error
|
||||
|
1
tests/ui-toml/lint_decimal_readability/clippy.toml
Normal file
1
tests/ui-toml/lint_decimal_readability/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
unreadable-literal-lint-fractions = false
|
22
tests/ui-toml/lint_decimal_readability/test.rs
Normal file
22
tests/ui-toml/lint_decimal_readability/test.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
#[deny(clippy::unreadable_literal)]
|
||||
|
||||
fn allow_inconsistent_digit_grouping() {
|
||||
#![allow(clippy::inconsistent_digit_grouping)]
|
||||
let _pass1 = 100_200_300.123456789;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
allow_inconsistent_digit_grouping();
|
||||
|
||||
let _pass1 = 100_200_300.100_200_300;
|
||||
let _pass2 = 1.123456789;
|
||||
let _pass3 = 1.0;
|
||||
let _pass4 = 10000.00001;
|
||||
let _pass5 = 1.123456789e1;
|
||||
|
||||
// due to clippy::inconsistent-digit-grouping
|
||||
let _fail1 = 100_200_300.123456789;
|
||||
|
||||
// fail due to the integer part
|
||||
let _fail2 = 100200300.300200100;
|
||||
}
|
10
tests/ui-toml/lint_decimal_readability/test.stderr
Normal file
10
tests/ui-toml/lint_decimal_readability/test.stderr
Normal file
|
@ -0,0 +1,10 @@
|
|||
error: digits grouped inconsistently by underscores
|
||||
--> $DIR/test.rs:18:18
|
||||
|
|
||||
LL | let _fail1 = 100_200_300.123456789;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789`
|
||||
|
|
||||
= note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
1
tests/ui-toml/min_rust_version/clippy.toml
Normal file
1
tests/ui-toml/min_rust_version/clippy.toml
Normal file
|
@ -0,0 +1 @@
|
|||
msrv = "1.0.0"
|
68
tests/ui-toml/min_rust_version/min_rust_version.rs
Normal file
68
tests/ui-toml/min_rust_version/min_rust_version.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
#![warn(clippy::manual_non_exhaustive)]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
mod enums {
|
||||
enum E {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
|
||||
// user forgot to remove the marker
|
||||
#[non_exhaustive]
|
||||
enum Ep {
|
||||
A,
|
||||
B,
|
||||
#[doc(hidden)]
|
||||
_C,
|
||||
}
|
||||
}
|
||||
|
||||
fn option_as_ref_deref() {
|
||||
let mut opt = Some(String::from("123"));
|
||||
|
||||
let _ = opt.as_ref().map(String::as_str);
|
||||
let _ = opt.as_ref().map(|x| x.as_str());
|
||||
let _ = opt.as_mut().map(String::as_mut_str);
|
||||
let _ = opt.as_mut().map(|x| x.as_mut_str());
|
||||
}
|
||||
|
||||
fn match_like_matches() {
|
||||
let _y = match Some(5) {
|
||||
Some(0) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms() {
|
||||
match (1, 2, 3) {
|
||||
(1, .., 3) => 42,
|
||||
(.., 3) => 42, //~ ERROR match arms have same body
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms2() {
|
||||
let _ = match Some(42) {
|
||||
Some(_) => 24,
|
||||
None => 24, //~ ERROR match arms have same body
|
||||
};
|
||||
}
|
||||
|
||||
fn manual_strip_msrv() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
option_as_ref_deref();
|
||||
match_like_matches();
|
||||
match_same_arms();
|
||||
match_same_arms2();
|
||||
manual_strip_msrv();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `third-party` at line 5 column 1
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
#[warn(clippy::as_conversions)]
|
||||
// aux-build:macro_rules.rs
|
||||
|
||||
#![warn(clippy::as_conversions)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate macro_rules;
|
||||
|
||||
fn with_external_macro() {
|
||||
as_conv_with_arg!(0u32 as u64);
|
||||
as_conv!();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let i = 0u32 as u64;
|
||||
|
||||
let j = &i as *const u64 as *mut u64;
|
||||
|
||||
with_external_macro();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:4:13
|
||||
--> $DIR/as_conversions.rs:14:13
|
||||
|
|
||||
LL | let i = 0u32 as u64;
|
||||
| ^^^^^^^^^^^
|
||||
|
@ -8,7 +8,7 @@ LL | let i = 0u32 as u64;
|
|||
= help: consider using a safe wrapper for this conversion
|
||||
|
||||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:6:13
|
||||
--> $DIR/as_conversions.rs:16:13
|
||||
|
|
||||
LL | let j = &i as *const u64 as *mut u64;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64;
|
|||
= help: consider using a safe wrapper for this conversion
|
||||
|
||||
error: using a potentially dangerous silent `as` conversion
|
||||
--> $DIR/as_conversions.rs:6:13
|
||||
--> $DIR/as_conversions.rs:16:13
|
||||
|
|
||||
LL | let j = &i as *const u64 as *mut u64;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -70,3 +70,17 @@ macro_rules! ref_arg_function {
|
|||
fn fun_example(ref _x: usize) {}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! as_conv_with_arg {
|
||||
(0u32 as u64) => {
|
||||
()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! as_conv {
|
||||
() => {
|
||||
0u32 as u64
|
||||
};
|
||||
}
|
||||
|
|
239
tests/ui/collapsible_match.rs
Normal file
239
tests/ui/collapsible_match.rs
Normal file
|
@ -0,0 +1,239 @@
|
|||
#![warn(clippy::collapsible_match)]
|
||||
#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)]
|
||||
|
||||
fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
|
||||
// match without block
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// match with block
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// if let, if let
|
||||
if let Ok(val) = res_opt {
|
||||
if let Some(n) = val {
|
||||
take(n);
|
||||
}
|
||||
}
|
||||
|
||||
// if let else, if let else
|
||||
if let Ok(val) = res_opt {
|
||||
if let Some(n) = val {
|
||||
take(n);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// if let, match
|
||||
if let Ok(val) = res_opt {
|
||||
match val {
|
||||
Some(n) => foo(n),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// match, if let
|
||||
match res_opt {
|
||||
Ok(val) => {
|
||||
if let Some(n) = val {
|
||||
take(n);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
// if let else, match
|
||||
if let Ok(val) = res_opt {
|
||||
match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// match, if let else
|
||||
match res_opt {
|
||||
Ok(val) => {
|
||||
if let Some(n) = val {
|
||||
take(n);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// None in inner match same as outer wild branch
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
None => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// None in outer match same as inner wild branch
|
||||
match opt_opt {
|
||||
Some(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
None => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn negative_cases(res_opt: Result<Option<u32>, String>, res_res: Result<Result<u32, String>, String>) {
|
||||
// no wild pattern in outer match
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
Err(_) => return,
|
||||
}
|
||||
|
||||
// inner branch is not wild or None
|
||||
match res_res {
|
||||
Ok(val) => match val {
|
||||
Ok(n) => foo(n),
|
||||
Err(_) => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// statement before inner match
|
||||
match res_opt {
|
||||
Ok(val) => {
|
||||
"hi buddy";
|
||||
match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// statement after inner match
|
||||
match res_opt {
|
||||
Ok(val) => {
|
||||
match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
}
|
||||
"hi buddy";
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// wild branches do not match
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => {
|
||||
"sup";
|
||||
return;
|
||||
},
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// binding used in if guard
|
||||
match res_opt {
|
||||
Ok(val) if val.is_some() => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// binding used in inner match body
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(_) => take(val),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// if guard on inner match
|
||||
{
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) if make() => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
_ => make(),
|
||||
_ if make() => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
// differing macro contexts
|
||||
{
|
||||
macro_rules! mac {
|
||||
($val:ident) => {
|
||||
match $val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
match res_opt {
|
||||
Ok(val) => mac!(val),
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
// OR pattern
|
||||
enum E<T> {
|
||||
A(T),
|
||||
B(T),
|
||||
C(T),
|
||||
};
|
||||
match make::<E<Option<u32>>>() {
|
||||
E::A(val) | E::B(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
match make::<Option<E<u32>>>() {
|
||||
Some(val) => match val {
|
||||
E::A(val) | E::B(val) => foo(val),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn make<T>() -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn foo<T, U>(t: T) -> U {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn take<T>(t: T) {}
|
||||
|
||||
fn main() {}
|
179
tests/ui/collapsible_match.stderr
Normal file
179
tests/ui/collapsible_match.stderr
Normal file
|
@ -0,0 +1,179 @@
|
|||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:7:20
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ____________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
= note: `-D clippy::collapsible-match` implied by `-D warnings`
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:7:12
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:16:20
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ____________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:16:12
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:25:9
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
LL | | take(n);
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:24:15
|
||||
|
|
||||
LL | if let Ok(val) = res_opt {
|
||||
| ^^^ Replace this binding
|
||||
LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:32:9
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
LL | | take(n);
|
||||
LL | | } else {
|
||||
LL | | return;
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:31:15
|
||||
|
|
||||
LL | if let Ok(val) = res_opt {
|
||||
| ^^^ Replace this binding
|
||||
LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:43:9
|
||||
|
|
||||
LL | / match val {
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => (),
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:42:15
|
||||
|
|
||||
LL | if let Ok(val) = res_opt {
|
||||
| ^^^ Replace this binding
|
||||
LL | match val {
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:52:13
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
LL | | take(n);
|
||||
LL | | }
|
||||
| |_____________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:51:12
|
||||
|
|
||||
LL | Ok(val) => {
|
||||
| ^^^ Replace this binding
|
||||
LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:61:9
|
||||
|
|
||||
LL | / match val {
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | }
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:60:15
|
||||
|
|
||||
LL | if let Ok(val) = res_opt {
|
||||
| ^^^ Replace this binding
|
||||
LL | match val {
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:72:13
|
||||
|
|
||||
LL | / if let Some(n) = val {
|
||||
LL | | take(n);
|
||||
LL | | } else {
|
||||
LL | | return;
|
||||
LL | | }
|
||||
| |_____________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:71:12
|
||||
|
|
||||
LL | Ok(val) => {
|
||||
| ^^^ Replace this binding
|
||||
LL | if let Some(n) = val {
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:83:20
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ____________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | None => return,
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:83:12
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match.rs:92:22
|
||||
|
|
||||
LL | Some(val) => match val {
|
||||
| ______________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match.rs:92:14
|
||||
|
|
||||
LL | Some(val) => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
53
tests/ui/collapsible_match2.rs
Normal file
53
tests/ui/collapsible_match2.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
#![warn(clippy::collapsible_match)]
|
||||
#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)]
|
||||
|
||||
fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
|
||||
// if guards on outer match
|
||||
{
|
||||
match res_opt {
|
||||
Ok(val) if make() => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
match res_opt {
|
||||
Ok(val) => match val {
|
||||
Some(n) => foo(n),
|
||||
_ => return,
|
||||
},
|
||||
_ if make() => return,
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
|
||||
// macro
|
||||
{
|
||||
macro_rules! mac {
|
||||
($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => {
|
||||
match $outer {
|
||||
$pat => match $e {
|
||||
$inner_pat => $then,
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
}
|
||||
// Lint this since the patterns are not defined by the macro.
|
||||
// Allows the lint to work on if_chain! for example.
|
||||
// Fixing the lint requires knowledge of the specific macro, but we optimistically assume that
|
||||
// there is still a better way to write this.
|
||||
mac!(res_opt => Ok(val), val => Some(n), foo(n));
|
||||
}
|
||||
}
|
||||
|
||||
fn make<T>() -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn foo<T, U>(t: T) -> U {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn main() {}
|
61
tests/ui/collapsible_match2.stderr
Normal file
61
tests/ui/collapsible_match2.stderr
Normal file
|
@ -0,0 +1,61 @@
|
|||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match2.rs:8:34
|
||||
|
|
||||
LL | Ok(val) if make() => match val {
|
||||
| __________________________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_____________^
|
||||
|
|
||||
= note: `-D clippy::collapsible-match` implied by `-D warnings`
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match2.rs:8:16
|
||||
|
|
||||
LL | Ok(val) if make() => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match2.rs:15:24
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ________________________^
|
||||
LL | | Some(n) => foo(n),
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_____________^
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match2.rs:15:16
|
||||
|
|
||||
LL | Ok(val) => match val {
|
||||
| ^^^ Replace this binding
|
||||
LL | Some(n) => foo(n),
|
||||
| ^^^^^^^ with this pattern
|
||||
|
||||
error: Unnecessary nested match
|
||||
--> $DIR/collapsible_match2.rs:29:29
|
||||
|
|
||||
LL | $pat => match $e {
|
||||
| _____________________________^
|
||||
LL | | $inner_pat => $then,
|
||||
LL | | _ => return,
|
||||
LL | | },
|
||||
| |_____________________^
|
||||
...
|
||||
LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
|
||||
| ------------------------------------------------- in this macro invocation
|
||||
|
|
||||
help: The outer pattern can be modified to include the inner pattern.
|
||||
--> $DIR/collapsible_match2.rs:41:28
|
||||
|
|
||||
LL | mac!(res_opt => Ok(val), val => Some(n), foo(n));
|
||||
| ^^^ ^^^^^^^ with this pattern
|
||||
| |
|
||||
| Replace this binding
|
||||
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
#[warn(clippy::str_to_string)]
|
||||
#[warn(clippy::string_to_string)]
|
||||
#[warn(clippy::unstable_as_slice)]
|
||||
#[warn(clippy::unstable_as_mut_slice)]
|
||||
#[warn(clippy::misaligned_transmute)]
|
||||
|
|
|
@ -1,88 +1,76 @@
|
|||
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
|
||||
--> $DIR/deprecated.rs:1:8
|
||||
|
|
||||
LL | #[warn(clippy::str_to_string)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
|
||||
|
||||
error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
|
||||
--> $DIR/deprecated.rs:2:8
|
||||
|
|
||||
LL | #[warn(clippy::string_to_string)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated.rs:3:8
|
||||
--> $DIR/deprecated.rs:1:8
|
||||
|
|
||||
LL | #[warn(clippy::unstable_as_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
|
||||
|
||||
error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated.rs:4:8
|
||||
--> $DIR/deprecated.rs:2:8
|
||||
|
|
||||
LL | #[warn(clippy::unstable_as_mut_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr`
|
||||
--> $DIR/deprecated.rs:5:8
|
||||
--> $DIR/deprecated.rs:3:8
|
||||
|
|
||||
LL | #[warn(clippy::misaligned_transmute)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint`
|
||||
--> $DIR/deprecated.rs:6:8
|
||||
--> $DIR/deprecated.rs:4:8
|
||||
|
|
||||
LL | #[warn(clippy::unused_collect)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value``
|
||||
--> $DIR/deprecated.rs:7:8
|
||||
--> $DIR/deprecated.rs:5:8
|
||||
|
|
||||
LL | #[warn(clippy::invalid_ref)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter``
|
||||
--> $DIR/deprecated.rs:8:8
|
||||
--> $DIR/deprecated.rs:6:8
|
||||
|
|
||||
LL | #[warn(clippy::into_iter_on_array)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels``
|
||||
--> $DIR/deprecated.rs:9:8
|
||||
--> $DIR/deprecated.rs:7:8
|
||||
|
|
||||
LL | #[warn(clippy::unused_label)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018`
|
||||
--> $DIR/deprecated.rs:10:8
|
||||
--> $DIR/deprecated.rs:8:8
|
||||
|
|
||||
LL | #[warn(clippy::regex_macro)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted to rustc and is now called `drop_bounds``
|
||||
--> $DIR/deprecated.rs:11:8
|
||||
--> $DIR/deprecated.rs:9:8
|
||||
|
|
||||
LL | #[warn(clippy::drop_bounds)]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr``
|
||||
--> $DIR/deprecated.rs:12:8
|
||||
--> $DIR/deprecated.rs:10:8
|
||||
|
|
||||
LL | #[warn(clippy::temporary_cstring_as_ptr)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt``
|
||||
--> $DIR/deprecated.rs:13:8
|
||||
--> $DIR/deprecated.rs:11:8
|
||||
|
|
||||
LL | #[warn(clippy::panic_params)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
|
||||
error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated.rs:1:8
|
||||
|
|
||||
LL | #[warn(clippy::str_to_string)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | #[warn(clippy::unstable_as_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#[warn(str_to_string)]
|
||||
#[warn(string_to_string)]
|
||||
#[warn(unstable_as_slice)]
|
||||
#[warn(unstable_as_mut_slice)]
|
||||
#[warn(misaligned_transmute)]
|
||||
|
|
|
@ -1,40 +1,28 @@
|
|||
error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
|
||||
--> $DIR/deprecated_old.rs:1:8
|
||||
|
|
||||
LL | #[warn(str_to_string)]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
|
||||
|
||||
error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon`
|
||||
--> $DIR/deprecated_old.rs:2:8
|
||||
|
|
||||
LL | #[warn(string_to_string)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated_old.rs:3:8
|
||||
--> $DIR/deprecated_old.rs:1:8
|
||||
|
|
||||
LL | #[warn(unstable_as_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
|
||||
|
||||
error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated_old.rs:4:8
|
||||
--> $DIR/deprecated_old.rs:2:8
|
||||
|
|
||||
LL | #[warn(unstable_as_mut_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr`
|
||||
--> $DIR/deprecated_old.rs:5:8
|
||||
--> $DIR/deprecated_old.rs:3:8
|
||||
|
|
||||
LL | #[warn(misaligned_transmute)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
|
||||
error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7`
|
||||
--> $DIR/deprecated_old.rs:1:8
|
||||
|
|
||||
LL | #[warn(str_to_string)]
|
||||
| ^^^^^^^^^^^^^
|
||||
LL | #[warn(unstable_as_slice)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -86,3 +86,12 @@ fn check_ignore_macro() {
|
|||
// checks if the lint ignores macros with `!` operator
|
||||
!bool_macro!(1) && !bool_macro!("");
|
||||
}
|
||||
|
||||
struct Nested {
|
||||
inner: ((i32,), (i32,), (i32,)),
|
||||
}
|
||||
|
||||
fn check_nested(n1: &Nested, n2: &Nested) -> bool {
|
||||
// `n2.inner.0.0` mistyped as `n1.inner.0.0`
|
||||
(n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
|
||||
}
|
||||
|
|
|
@ -162,5 +162,13 @@ error: equal expressions as operands to `/`
|
|||
LL | const D: u32 = A / A;
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 27 previous errors
|
||||
error: equal expressions as operands to `==`
|
||||
--> $DIR/eq_op.rs:96:5
|
||||
|
|
||||
LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[deny(clippy::eq_op)]` on by default
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
|
|
|
@ -37,3 +37,16 @@ fn mac() {
|
|||
b!();
|
||||
println!("{}", a);
|
||||
}
|
||||
|
||||
fn semicolon() {
|
||||
struct S {
|
||||
a: u32,
|
||||
};
|
||||
impl S {
|
||||
fn new(a: u32) -> Self {
|
||||
Self { a }
|
||||
}
|
||||
}
|
||||
|
||||
let _ = S::new(3);
|
||||
}
|
||||
|
|
|
@ -22,5 +22,9 @@ fn main() -> Result<(), Errors> {
|
|||
|
||||
println!("{:?}", x.map_err(|_| Errors::Ignored));
|
||||
|
||||
// Should not warn you because you explicitly ignore the parameter
|
||||
// using a named wildcard value
|
||||
println!("{:?}", x.map_err(|_foo| Errors::Ignored));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
error: `map_err(|_|...` ignores the original error
|
||||
error: `map_err(|_|...` wildcard pattern discards the original error
|
||||
--> $DIR/map_err.rs:23:32
|
||||
|
|
||||
LL | println!("{:?}", x.map_err(|_| Errors::Ignored));
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::map-err-ignore` implied by `-D warnings`
|
||||
= help: Consider wrapping the error in an enum variant
|
||||
= help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
87
tests/ui/min_rust_version_attr.rs
Normal file
87
tests/ui/min_rust_version_attr.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
#![allow(clippy::redundant_clone)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.0.0"]
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
fn option_as_ref_deref() {
|
||||
let mut opt = Some(String::from("123"));
|
||||
|
||||
let _ = opt.as_ref().map(String::as_str);
|
||||
let _ = opt.as_ref().map(|x| x.as_str());
|
||||
let _ = opt.as_mut().map(String::as_mut_str);
|
||||
let _ = opt.as_mut().map(|x| x.as_mut_str());
|
||||
}
|
||||
|
||||
fn match_like_matches() {
|
||||
let _y = match Some(5) {
|
||||
Some(0) => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms() {
|
||||
match (1, 2, 3) {
|
||||
(1, .., 3) => 42,
|
||||
(.., 3) => 42, //~ ERROR match arms have same body
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_same_arms2() {
|
||||
let _ = match Some(42) {
|
||||
Some(_) => 24,
|
||||
None => 24, //~ ERROR match arms have same body
|
||||
};
|
||||
}
|
||||
|
||||
pub fn manual_strip_msrv() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
option_as_ref_deref();
|
||||
match_like_matches();
|
||||
match_same_arms();
|
||||
match_same_arms2();
|
||||
manual_strip_msrv();
|
||||
}
|
||||
|
||||
mod meets_msrv {
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.45.0"]
|
||||
|
||||
fn main() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod just_under_msrv {
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.46.0"]
|
||||
|
||||
fn main() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod just_above_msrv {
|
||||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.44.0"]
|
||||
|
||||
fn main() {
|
||||
let s = "hello, world!";
|
||||
if s.starts_with("hello, ") {
|
||||
assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
}
|
||||
}
|
||||
}
|
37
tests/ui/min_rust_version_attr.stderr
Normal file
37
tests/ui/min_rust_version_attr.stderr
Normal file
|
@ -0,0 +1,37 @@
|
|||
error: stripping a prefix manually
|
||||
--> $DIR/min_rust_version_attr.rs:60:24
|
||||
|
|
||||
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::manual-strip` implied by `-D warnings`
|
||||
note: the prefix was tested here
|
||||
--> $DIR/min_rust_version_attr.rs:59:9
|
||||
|
|
||||
LL | if s.starts_with("hello, ") {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: try using the `strip_prefix` method
|
||||
|
|
||||
LL | if let Some(<stripped>) = s.strip_prefix("hello, ") {
|
||||
LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!");
|
||||
|
|
||||
|
||||
error: stripping a prefix manually
|
||||
--> $DIR/min_rust_version_attr.rs:72:24
|
||||
|
|
||||
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the prefix was tested here
|
||||
--> $DIR/min_rust_version_attr.rs:71:9
|
||||
|
|
||||
LL | if s.starts_with("hello, ") {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: try using the `strip_prefix` method
|
||||
|
|
||||
LL | if let Some(<stripped>) = s.strip_prefix("hello, ") {
|
||||
LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!");
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
4
tests/ui/min_rust_version_invalid_attr.rs
Normal file
4
tests/ui/min_rust_version_invalid_attr.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "invalid.version"]
|
||||
|
||||
fn main() {}
|
8
tests/ui/min_rust_version_invalid_attr.stderr
Normal file
8
tests/ui/min_rust_version_invalid_attr.stderr
Normal file
|
@ -0,0 +1,8 @@
|
|||
error: `invalid.version` is not a valid Rust version
|
||||
--> $DIR/min_rust_version_invalid_attr.rs:2:1
|
||||
|
|
||||
LL | #![clippy::msrv = "invalid.version"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
11
tests/ui/min_rust_version_multiple_inner_attr.rs
Normal file
11
tests/ui/min_rust_version_multiple_inner_attr.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![feature(custom_inner_attributes)]
|
||||
#![clippy::msrv = "1.40"]
|
||||
#![clippy::msrv = "=1.35.0"]
|
||||
#![clippy::msrv = "1.10.1"]
|
||||
|
||||
mod foo {
|
||||
#![clippy::msrv = "1"]
|
||||
#![clippy::msrv = "1.0.0"]
|
||||
}
|
||||
|
||||
fn main() {}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue