Merge commit '3d0b0e66afdfaa519d8855b338b35b4605775945' into clippyup

This commit is contained in:
flip1995 2020-08-28 16:10:16 +02:00
parent 48b4aeabf8
commit 282c59820b
154 changed files with 4170 additions and 1102 deletions

View File

@ -1,6 +1,6 @@
[alias]
uitest = "test --test compile-test"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
[build]
rustflags = ["-Zunstable-options"]

View File

@ -92,6 +92,13 @@ jobs:
env:
OS: ${{ runner.os }}
- name: Test cargo dev new lint
run: |
cargo dev new_lint --name new_early_pass --pass early
cargo dev new_lint --name new_late_pass --pass late
cargo check
git reset --hard HEAD
# Cleanup
- name: Run cargo-cache --autoclean
run: |

View File

@ -1498,6 +1498,7 @@ Released 2018-09-13
[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
@ -1690,6 +1691,7 @@ Released 2018-09-13
[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
@ -1699,6 +1701,7 @@ Released 2018-09-13
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
[`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
@ -1723,6 +1726,7 @@ Released 2018-09-13
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
@ -1752,6 +1756,7 @@ Released 2018-09-13
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
@ -1773,6 +1778,7 @@ Released 2018-09-13
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self

View File

@ -189,6 +189,35 @@ Clippy in the `rust-lang/rust` repository.
For general information about `subtree`s in the Rust repository see [Rust's
`CONTRIBUTING.md`][subtree].
### Patching git-subtree to work with big repos
Currently there's a bug in `git-subtree` that prevents it from working properly
with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
Before continuing with the following steps, we need to manually apply that fix to
our local copy of `git-subtree`.
You can get the patched version of `git-subtree` from [here][gitgitgadget-pr].
Put this file under `/usr/lib/git-core` (taking a backup of the previous file)
and make sure it has the proper permissions:
```bash
sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree
sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree
```
_Note:_ The first time running `git subtree push` a cache has to be built. This
involves going through the complete Clippy history once. For this you have to
increase the stack limit though, which you can do with `ulimit -s 60000`.
Make sure to run the `ulimit` command from the same session you call git subtree.
_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`.
This shell has a hardcoded recursion limit set to 1000. In order to make this process work,
you need to force the script to run `bash` instead. You can do this by editing the first
line of the `git-subtree` script and changing `sh` to `bash`.
### Performing the sync
Here is a TL;DR version of the sync process (all of the following commands have
to be run inside the `rust` directory):
@ -198,6 +227,7 @@ to be run inside the `rust` directory):
# Make sure to change `your-github-name` to your github name in the following command
git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
```
_Note:_ This will directly push to the remote repository. You can also push
to your local copy by replacing the remote address with `/path/to/rust-clippy`
directory.
@ -213,14 +243,30 @@ to be run inside the `rust` directory):
3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
~~annoy~~ ask them in the [Discord] channel.)
4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
### Syncing back changes in Clippy to [`rust-lang/rust`]
To avoid flooding the [`rust-lang/rust`] PR queue, changes in Clippy's repo are synced back
in a bi-weekly basis if there's no urgent changes. This is done starting on the day of
the Rust stable release and then every other week. That way we guarantee that
every feature in Clippy is available for 2 weeks in nightly, before it can get to beta.
For reference, the first sync following this cadence was performed the 2020-08-27.
All of the following commands have to be run inside the `rust` directory.
1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous
section if necessary.
2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
```bash
git checkout -b sync-from-clippy
git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
```
5. Open a PR to [`rust-lang/rust`]
3. Open a PR to [`rust-lang/rust`]
Also, you may want to define remotes, so you don't have to type out the remote
### Defining remotes
You may want to define remotes, so you don't have to type out the remote
addresses on every sync. You can do this with the following commands (these
commands still have to be run inside the `rust` directory):
@ -241,12 +287,6 @@ You can then sync with the remote names from above, e.g.:
$ git subtree push -P src/tools/clippy clippy-local sync-from-rust
```
_Note:_ The first time running `git subtree push` a cache has to be built. This
involves going through the complete Clippy history once. For this you have to
increase the stack limit though, which you can do with `ulimit -s 60000`. For
this to work, you will need the fix of `git subtree` available
[here][gitgitgadget-pr].
[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree
[`rust-lang/rust`]: https://github.com/rust-lang/rust

View File

@ -31,13 +31,13 @@ path = "src/driver.rs"
# begin automatic update
clippy_lints = { version = "0.0.212", path = "clippy_lints" }
# end automatic update
semver = "0.9"
semver = "0.10"
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
tempfile = { version = "3.1.0", optional = true }
lazy_static = "1.0"
[dev-dependencies]
cargo_metadata = "0.9.1"
cargo_metadata = "0.11.1"
compiletest_rs = { version = "0.5.0", features = ["tmp"] }
tester = "0.7"
lazy_static = "1.0"

View File

@ -47,7 +47,7 @@ pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str
fn create_lint(lint: &LintData) -> io::Result<()> {
let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
"early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
"late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
"late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
_ => {
unreachable!("`pass_type` should only ever be `early` or `late`!");
},

View File

@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"]
edition = "2018"
[dependencies]
cargo_metadata = "0.9.1"
cargo_metadata = "0.11.1"
if_chain = "1.0.0"
itertools = "0.9"
lazy_static = "1.0.2"
@ -28,7 +28,7 @@ serde = { version = "1.0", features = ["derive"] }
smallvec = { version = "1", features = ["union"] }
toml = "0.5.3"
unicode-normalization = "0.1"
semver = "0.9.0"
semver = "0.10.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"] }

View File

@ -1,5 +1,5 @@
use crate::utils::{
get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method,
};
use crate::utils::{higher, sugg};
use if_chain::if_chain;
@ -70,11 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps {
return;
}
// lhs op= l op r
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
if eq_expr_value(cx, lhs, l) {
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
}
// lhs op= l commutative_op r
if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
}
}
@ -161,14 +161,12 @@ impl<'tcx> LateLintPass<'tcx> for AssignOps {
if visitor.counter == 1 {
// a = a op b
if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, l) {
if eq_expr_value(cx, assignee, l) {
lint(assignee, r);
}
// a = b commutative_op a
// Limited to primitive type as these ops are know to be commutative
if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r)
&& cx.typeck_results().expr_ty(assignee).is_primitive_ty()
{
if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
match op.node {
hir::BinOpKind::Add
| hir::BinOpKind::Mul
@ -253,7 +251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(self.assignee, expr) {
if eq_expr_value(self.cx, self.assignee, expr) {
self.counter += 1;
}

View File

@ -5,8 +5,8 @@ use crate::utils::{
span_lint_and_sugg, span_lint_and_then, without_block_comments,
};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_ast::util::lev_distance::find_best_match_for_name;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,

View File

@ -1,6 +1,6 @@
use crate::utils::{
get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt, span_lint_and_sugg,
span_lint_and_then, SpanlessEq,
eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt,
span_lint_and_sugg, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
@ -128,7 +128,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
}
}
for (n, expr) in self.terminals.iter().enumerate() {
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
if eq_expr_value(self.cx, e, expr) {
#[allow(clippy::cast_possible_truncation)]
return Ok(Bool::Term(n as u8));
}
@ -138,8 +138,8 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
if implements_ord(self.cx, e_lhs);
if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
if negate(e_binop.node) == Some(expr_binop.node);
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_lhs, expr_lhs);
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e_rhs, expr_rhs);
if eq_expr_value(self.cx, e_lhs, expr_lhs);
if eq_expr_value(self.cx, e_rhs, expr_rhs);
then {
#[allow(clippy::cast_possible_truncation)]
return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));

View File

@ -1,5 +1,5 @@
use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash};
use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
use crate::utils::{SpanlessEq, SpanlessHash};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
@ -197,8 +197,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
h.finish()
};
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool =
&|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
for (i, j) in search_same(conds, hash, eq) {
span_lint_and_note(
@ -222,7 +221,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
// Do not spawn warning if `IFS_SAME_COND` already produced it.
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) {
if eq_expr_value(cx, lhs, rhs) {
return false;
}
SpanlessEq::new(cx).eq_expr(lhs, rhs)

View File

@ -1,16 +1,22 @@
use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{AttrKind, Attribute};
use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind};
use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter;
use rustc_errors::Handler;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{BytePos, MultiSpan, Span};
use rustc_span::Pos;
use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
use rustc_span::{FileName, Pos};
use std::io;
use std::ops::Range;
use url::Url;
@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
headers
}
static LEAVE_MAIN_PATTERNS: &[&str] = &["static", "fn main() {}", "extern crate", "async fn main() {"];
fn check_code(cx: &LateContext<'_>, text: &str, span: Span) {
if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) {
fn has_needless_main(code: &str) -> bool {
let filename = FileName::anon_source_code(code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false);
let handler = Handler::with_emitter(false, None, box emitter);
let sess = ParseSess::with_span_handler(handler, sm);
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) {
Ok(p) => p,
Err(errs) => {
for mut err in errs {
err.cancel();
}
return false;
},
};
let mut relevant_main_found = false;
loop {
match parser.parse_item() {
Ok(Some(item)) => match &item.kind {
// Tests with one of these items are ignored
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..) => return false,
// We found a main function ...
ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => {
let is_async = matches!(sig.header.asyncness, Async::Yes{..});
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
_ => false,
};
if returns_nothing && !is_async && !block.stmts.is_empty() {
// This main function should be linted, but only if there are no other functions
relevant_main_found = true;
} else {
// This main function should not be linted, we're done
return false;
}
},
// Another function was found; this case is ignored too
ItemKind::Fn(..) => return false,
_ => {},
},
Ok(None) => break,
Err(mut e) => {
e.cancel();
return false;
},
}
}
relevant_main_found
}
if has_needless_main(text) {
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
}
}

View File

@ -6,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg};
declare_clippy_lint! {
/// **What it does:** Checks for double comparisons that could be simplified to a single expression.
@ -46,8 +46,7 @@ impl<'tcx> DoubleComparisons {
},
_ => return,
};
let mut spanless_eq = SpanlessEq::new(cx).ignore_fn();
if !(spanless_eq.eq_expr(&llhs, &rlhs) && spanless_eq.eq_expr(&lrhs, &rrhs)) {
if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) {
return;
}
macro_rules! lint_double_comparison {

View File

@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
cx,
DURATION_SUBSEC,
expr.span,
&format!("Calling `{}()` is more concise than this calculation", suggested_fn),
&format!("calling `{}()` is more concise than this calculation", suggested_fn),
"try",
format!(
"{}.{}()",

View File

@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
cx,
ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span,
"Clike enum variant discriminant is not portable to 32-bit targets",
"C-like enum variant discriminant is not portable to 32-bit targets",
);
};
}

View File

@ -183,10 +183,10 @@ fn check_variant(
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
{
span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
span_lint(cx, lint, var.span, "variant name starts with the enum's name");
}
if partial_rmatch(item_name, &name) == item_name_chars {
span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
span_lint(cx, lint, var.span, "variant name ends with the enum's name");
}
}
let first = &def.variants[0].ident.name.as_str();
@ -227,7 +227,7 @@ fn check_variant(
cx,
lint,
span,
&format!("All variants have the same {}fix: `{}`", what, value),
&format!("all variants have the same {}fix: `{}`", what, value),
None,
&format!(
"remove the {}fixes and use full paths to \

View File

@ -1,5 +1,5 @@
use crate::utils::{
implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq,
eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then,
};
use rustc_errors::Applicability;
use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind};
@ -69,7 +69,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) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
if is_valid_operator(op) && eq_expr_value(cx, left, right) {
span_lint(
cx,
EQ_OP,

View File

@ -0,0 +1,110 @@
use crate::utils::{match_qpath, paths, span_lint_and_then, sugg};
use if_chain::if_chain;
use rustc_ast::util::parser::AssocOp;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
declare_clippy_lint! {
/// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or
/// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
///
/// **Why is this bad?** The code without `.abs()` is more likely to have a bug.
///
/// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is
/// technically unneccessary. However, it will make the code more robust and doesn't have any
/// large performance implications. If the abs call was deliberately left out for performance
/// reasons, it is probably better to state this explicitly in the code, which then can be done
/// with an allow.
///
/// **Example:**
///
/// ```rust
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
/// (a - b) < f32::EPSILON
/// }
/// ```
/// Use instead:
/// ```rust
/// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
/// (a - b).abs() < f32::EPSILON
/// }
/// ```
pub FLOAT_EQUALITY_WITHOUT_ABS,
correctness,
"float equality check without `.abs()`"
}
declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]);
impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let lhs;
let rhs;
// check if expr is a binary expression with a lt or gt operator
if let ExprKind::Binary(op, ref left, ref right) = expr.kind {
match op.node {
BinOpKind::Lt => {
lhs = left;
rhs = right;
},
BinOpKind::Gt => {
lhs = right;
rhs = left;
},
_ => return,
};
} else {
return;
}
if_chain! {
// left hand side is a substraction
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Sub,
..
},
val_l,
val_r,
) = lhs.kind;
// right hand side matches either f32::EPSILON or f64::EPSILON
if let ExprKind::Path(ref epsilon_path) = rhs.kind;
if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON);
// values of the substractions on the left hand side are of the type float
let t_val_l = cx.typeck_results().expr_ty(val_l);
let t_val_r = cx.typeck_results().expr_ty(val_r);
if let ty::Float(_) = t_val_l.kind;
if let ty::Float(_) = t_val_r.kind;
then {
let sug_l = sugg::Sugg::hir(cx, &val_l, "..");
let sug_r = sugg::Sugg::hir(cx, &val_r, "..");
// format the suggestion
let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
// spans the lint
span_lint_and_then(
cx,
FLOAT_EQUALITY_WITHOUT_ABS,
expr.span,
"float equality check without `.abs()`",
| diag | {
diag.span_suggestion(
lhs.span,
"add `.abs()`",
suggestion,
Applicability::MaybeIncorrect,
);
}
);
}
}
}
}

View File

@ -2,7 +2,7 @@ use crate::consts::{
constant, constant_simple, Constant,
Constant::{Int, F32, F64},
};
use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq};
use crate::utils::{eq_expr_value, get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@ -363,8 +363,8 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
if_chain! {
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind;
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind;
if are_exprs_equal(cx, lmul_lhs, lmul_rhs);
if are_exprs_equal(cx, rmul_lhs, rmul_rhs);
if eq_expr_value(cx, lmul_lhs, lmul_rhs);
if eq_expr_value(cx, rmul_lhs, rmul_rhs);
then {
return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, "..")));
}
@ -502,8 +502,8 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
match op {
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && are_exprs_equal(cx, left, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && are_exprs_equal(cx, right, test),
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
_ => false,
}
} else {
@ -515,8 +515,8 @@ fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
match op {
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && are_exprs_equal(cx, right, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && are_exprs_equal(cx, left, test),
BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
_ => false,
}
} else {
@ -524,10 +524,6 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
}
}
fn are_exprs_equal(cx: &LateContext<'_>, expr1: &Expr<'_>, expr2: &Expr<'_>) -> bool {
SpanlessEq::new(cx).ignore_fn().eq_expr(expr1, expr2)
}
/// Returns true iff expr is some zero literal
fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match constant_simple(cx, cx.typeck_results(), expr) {
@ -546,12 +542,12 @@ fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// returns None.
fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
if let ExprKind::Unary(UnOp::UnNeg, expr1_negated) = &expr1.kind {
if are_exprs_equal(cx, expr1_negated, expr2) {
if eq_expr_value(cx, expr1_negated, expr2) {
return Some((false, expr2));
}
}
if let ExprKind::Unary(UnOp::UnNeg, expr2_negated) = &expr2.kind {
if are_exprs_equal(cx, expr1, expr2_negated) {
if eq_expr_value(cx, expr1, expr2_negated) {
return Some((true, expr1));
}
}
@ -614,7 +610,7 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>
args_a.len() == args_b.len() &&
(
["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) ||
method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1])
method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
);
}
}

View File

@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
cx,
IF_LET_SOME_RESULT,
expr.span.with_hi(op.span.hi()),
"Matching on `Some` with `ok()` is redundant",
&format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
"matching on `Some` with `ok()` is redundant",
&format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
sugg,
applicability,
);

View File

@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse {
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary boolean `not` operation",
"unnecessary boolean `not` operation",
None,
"remove the `!` and swap the blocks of the `if`/`else`",
);
@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse {
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary `!=` operation",
"unnecessary `!=` operation",
None,
"change to `==` and swap the blocks of the `if`/`else`",
);

View File

@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
cx,
IMPLICIT_SATURATING_SUB,
expr.span,
"Implicitly performing saturating subtraction",
"implicitly performing saturating subtraction",
"try",
format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()),
format!("{} = {}.saturating_sub({});", var_name, var_name, '1'),
Applicability::MachineApplicable,
);
}

View File

@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
cx,
MULTIPLE_INHERENT_IMPL,
*additional_span,
"Multiple implementations of this structure",
"multiple implementations of this structure",
|diag| {
diag.span_note(*initial_span, "First implementation here");
diag.span_note(*initial_span, "first implementation here");
},
)
})

View File

@ -152,7 +152,7 @@ impl IntPlusOne {
cx,
INT_PLUS_ONE,
block.span,
"Unnecessary `>= y + 1` or `x - 1 >=`",
"unnecessary `>= y + 1` or `x - 1 >=`",
"change it to",
recommendation,
Applicability::MachineApplicable, // snippet
@ -163,8 +163,8 @@ impl IntPlusOne {
impl EarlyLintPass for IntPlusOne {
fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
Self::emit_warning(cx, item, rec.clone());
if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
Self::emit_warning(cx, item, rec);
}
}
}

View File

@ -1,4 +1,4 @@
use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
@ -260,17 +260,6 @@ fn check_len(
/// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Special case ranges until `range_is_empty` is stabilized. See issue 3807.
fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
higher::range(expr).map_or(false, |_| {
!cx.tcx
.features()
.declared_lib_features
.iter()
.any(|(name, _)| name.as_str() == "range_is_empty")
})
}
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
if let ty::AssocKind::Fn = item.kind {
@ -296,10 +285,6 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
})
}
if should_skip_range(cx, expr) {
return false;
}
let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr));
match ty.kind {
ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| {

View File

@ -1,124 +0,0 @@
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then};
declare_clippy_lint! {
/// **What it does:** Checks for `let`-bindings, which are subsequently
/// returned.
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo() -> String {
/// let x = String::new();
/// x
/// }
/// ```
/// instead, use
/// ```
/// fn foo() -> String {
/// String::new()
/// }
/// ```
pub LET_AND_RETURN,
style,
"creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
}
declare_lint_pass!(LetReturn => [LET_AND_RETURN]);
impl<'tcx> LateLintPass<'tcx> for LetReturn {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
// we need both a let-binding stmt and an expr
if_chain! {
if let Some(retexpr) = block.expr;
if let Some(stmt) = block.stmts.iter().last();
if let StmtKind::Local(local) = &stmt.kind;
if local.ty.is_none();
if local.attrs.is_empty();
if let Some(initexpr) = &local.init;
if let PatKind::Binding(.., ident, _) = local.pat.kind;
if let ExprKind::Path(qpath) = &retexpr.kind;
if match_qpath(qpath, &[&*ident.name.as_str()]);
if !last_statement_borrows(cx, initexpr);
if !in_external_macro(cx.sess(), initexpr.span);
if !in_external_macro(cx.sess(), retexpr.span);
if !in_external_macro(cx.sess(), local.span);
if !in_macro(local.span);
then {
span_lint_and_then(
cx,
LET_AND_RETURN,
retexpr.span,
"returning the result of a `let` binding from a block",
|err| {
err.span_label(local.span, "unnecessary `let` binding");
if let Some(snippet) = snippet_opt(cx, initexpr.span) {
err.multipart_suggestion(
"return the expression directly",
vec![
(local.span, String::new()),
(retexpr.span, snippet),
],
Applicability::MachineApplicable,
);
} else {
err.span_help(initexpr.span, "this expression can be directly returned");
}
},
);
}
}
}
}
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
let mut visitor = BorrowVisitor { cx, borrows: false };
walk_expr(&mut visitor, expr);
visitor.borrows
}
struct BorrowVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
borrows: bool,
}
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if self.borrows {
return;
}
if let Some(def_id) = fn_def_id(self.cx, expr) {
self.borrows = self
.cx
.tcx
.fn_sig(def_id)
.output()
.skip_binder()
.walk()
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}
walk_expr(self, expr);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}

View File

@ -193,6 +193,7 @@ mod excessive_bools;
mod exit;
mod explicit_write;
mod fallible_impl_from;
mod float_equality_without_abs;
mod float_literal;
mod floating_point_arithmetic;
mod format;
@ -218,7 +219,6 @@ mod large_const_arrays;
mod large_enum_variant;
mod large_stack_arrays;
mod len_zero;
mod let_and_return;
mod let_if_seq;
mod let_underscore;
mod lifetimes;
@ -285,6 +285,7 @@ mod reference;
mod regex;
mod repeat_once;
mod returns;
mod self_assignment;
mod serde_api;
mod shadow;
mod single_component_path_imports;
@ -296,6 +297,7 @@ mod swap;
mod tabs_in_doc_comments;
mod temporary_assignment;
mod to_digit_is_some;
mod to_string_in_display;
mod trait_bounds;
mod transmute;
mod transmuting_null;
@ -310,7 +312,9 @@ mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_io_amount;
mod unused_self;
mod unused_unit;
mod unwrap;
mod unwrap_in_result;
mod use_self;
mod useless_conversion;
mod vec;
@ -547,6 +551,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&exit::EXIT,
&explicit_write::EXPLICIT_WRITE,
&fallible_impl_from::FALLIBLE_IMPL_FROM,
&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
&float_literal::EXCESSIVE_PRECISION,
&float_literal::LOSSY_FLOAT_LITERAL,
&floating_point_arithmetic::IMPRECISE_FLOPS,
@ -586,7 +591,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&large_stack_arrays::LARGE_STACK_ARRAYS,
&len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO,
&let_and_return::LET_AND_RETURN,
&let_if_seq::USELESS_LET_IF_SEQ,
&let_underscore::LET_UNDERSCORE_LOCK,
&let_underscore::LET_UNDERSCORE_MUST_USE,
@ -677,6 +681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::SEARCH_IS_SOME,
&methods::SHOULD_IMPLEMENT_TRAIT,
&methods::SINGLE_CHAR_PATTERN,
&methods::SINGLE_CHAR_PUSH_STR,
&methods::SKIP_WHILE_NEXT,
&methods::STRING_EXTEND_CHARS,
&methods::SUSPICIOUS_MAP,
@ -684,6 +689,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::UNINIT_ASSUMED_INIT,
&methods::UNNECESSARY_FILTER_MAP,
&methods::UNNECESSARY_FOLD,
&methods::UNNECESSARY_LAZY_EVALUATIONS,
&methods::UNWRAP_USED,
&methods::USELESS_ASREF,
&methods::WRONG_PUB_SELF_CONVENTION,
@ -769,8 +775,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&regex::INVALID_REGEX,
&regex::TRIVIAL_REGEX,
&repeat_once::REPEAT_ONCE,
&returns::LET_AND_RETURN,
&returns::NEEDLESS_RETURN,
&returns::UNUSED_UNIT,
&self_assignment::SELF_ASSIGNMENT,
&serde_api::SERDE_API_MISUSE,
&shadow::SHADOW_REUSE,
&shadow::SHADOW_SAME,
@ -788,6 +795,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
&temporary_assignment::TEMPORARY_ASSIGNMENT,
&to_digit_is_some::TO_DIGIT_IS_SOME,
&to_string_in_display::TO_STRING_IN_DISPLAY,
&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
&trait_bounds::TYPE_REPETITION_IN_BOUNDS,
&transmute::CROSSPOINTER_TRANSMUTE,
@ -840,8 +848,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
&unused_io_amount::UNUSED_IO_AMOUNT,
&unused_self::UNUSED_SELF,
&unused_unit::UNUSED_UNIT,
&unwrap::PANICKING_UNWRAP,
&unwrap::UNNECESSARY_UNWRAP,
&unwrap_in_result::UNWRAP_IN_RESULT,
&use_self::USE_SELF,
&useless_conversion::USELESS_CONVERSION,
&utils::internal_lints::CLIPPY_LINTS_INTERNAL,
@ -930,11 +940,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold));
let too_large_for_stack = conf.too_large_for_stack;
store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack});
store.register_late_pass(move || box vec::UselessVec{too_large_for_stack});
store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented);
store.register_late_pass(|| box strings::StringLitAsBytes);
store.register_late_pass(|| box derive::Derive);
store.register_late_pass(|| box types::CharLitAsU8);
store.register_late_pass(|| box vec::UselessVec);
store.register_late_pass(|| box drop_bounds::DropBounds);
store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
@ -1017,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| box reference::DerefAddrOf);
store.register_early_pass(|| box reference::RefInDeref);
store.register_early_pass(|| box double_parens::DoubleParens);
store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new());
store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval);
store.register_early_pass(|| box if_not_else::IfNotElse);
store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
@ -1025,8 +1036,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| box misc_early::MiscEarlyLints);
store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall);
store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall);
store.register_early_pass(|| box returns::Return);
store.register_late_pass(|| box let_and_return::LetReturn);
store.register_early_pass(|| box unused_unit::UnusedUnit);
store.register_late_pass(|| box returns::Return);
store.register_early_pass(|| box collapsible_if::CollapsibleIf);
store.register_early_pass(|| box items_after_statements::ItemsAfterStatements);
store.register_early_pass(|| box precedence::Precedence);
@ -1085,6 +1096,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
store.register_late_pass(|| box repeat_once::RepeatOnce);
store.register_late_pass(|| box unwrap_in_result::UnwrapInResult);
store.register_late_pass(|| box self_assignment::SelfAssignment);
store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@ -1122,6 +1136,7 @@ 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(&unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(&verbose_file_reads::VERBOSE_FILE_READS),
LintId::of(&write::PRINT_STDOUT),
LintId::of(&write::USE_DEBUG),
@ -1260,6 +1275,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&eval_order_dependence::DIVERGING_SUB_EXPRESSION),
LintId::of(&eval_order_dependence::EVAL_ORDER_DEPENDENCE),
LintId::of(&explicit_write::EXPLICIT_WRITE),
LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(&float_literal::EXCESSIVE_PRECISION),
LintId::of(&format::USELESS_FORMAT),
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
@ -1284,7 +1300,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&let_and_return::LET_AND_RETURN),
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
@ -1349,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::SEARCH_IS_SOME),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(&methods::SINGLE_CHAR_PATTERN),
LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
LintId::of(&methods::SKIP_WHILE_NEXT),
LintId::of(&methods::STRING_EXTEND_CHARS),
LintId::of(&methods::SUSPICIOUS_MAP),
@ -1356,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::UNINIT_ASSUMED_INIT),
LintId::of(&methods::UNNECESSARY_FILTER_MAP),
LintId::of(&methods::UNNECESSARY_FOLD),
LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(&methods::USELESS_ASREF),
LintId::of(&methods::WRONG_SELF_CONVENTION),
LintId::of(&methods::ZST_OFFSET),
@ -1413,8 +1430,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&regex::INVALID_REGEX),
LintId::of(&regex::TRIVIAL_REGEX),
LintId::of(&repeat_once::REPEAT_ONCE),
LintId::of(&returns::LET_AND_RETURN),
LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT),
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(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
@ -1427,6 +1445,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME),
LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY),
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),
LintId::of(&transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
LintId::of(&transmute::TRANSMUTE_BYTES_TO_STR),
@ -1460,6 +1479,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&unwrap::PANICKING_UNWRAP),
LintId::of(&unwrap::UNNECESSARY_UNWRAP),
LintId::of(&useless_conversion::USELESS_CONVERSION),
@ -1500,7 +1520,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&let_and_return::LET_AND_RETURN),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_KV_MAP),
@ -1532,8 +1551,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::OPTION_MAP_OR_NONE),
LintId::of(&methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT),
LintId::of(&methods::SINGLE_CHAR_PUSH_STR),
LintId::of(&methods::STRING_EXTEND_CHARS),
LintId::of(&methods::UNNECESSARY_FOLD),
LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(&methods::WRONG_SELF_CONVENTION),
LintId::of(&misc::TOPLEVEL_REF_ARG),
LintId::of(&misc::ZERO_PTR),
@ -1554,8 +1575,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(&regex::TRIVIAL_REGEX),
LintId::of(&returns::LET_AND_RETURN),
LintId::of(&returns::NEEDLESS_RETURN),
LintId::of(&returns::UNUSED_UNIT),
LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(&strings::STRING_LIT_AS_BYTES),
LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
@ -1564,6 +1585,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&types::FN_TO_NUMERIC_CAST),
LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(&unused_unit::UNUSED_UNIT),
LintId::of(&write::PRINTLN_EMPTY_STRING),
LintId::of(&write::PRINT_LITERAL),
LintId::of(&write::PRINT_WITH_NEWLINE),
@ -1672,6 +1694,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
LintId::of(&eq_op::EQ_OP),
LintId::of(&erasing_op::ERASING_OP),
LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
LintId::of(&formatting::POSSIBLE_MISSING_COMMA),
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(&if_let_mutex::IF_LET_MUTEX),
@ -1704,10 +1727,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&ptr::MUT_FROM_REF),
LintId::of(&ranges::REVERSED_EMPTY_RANGES),
LintId::of(&regex::INVALID_REGEX),
LintId::of(&self_assignment::SELF_ASSIGNMENT),
LintId::of(&serde_api::SERDE_API_MISUSE),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(&swap::ALMOST_SWAPPED),
LintId::of(&to_string_in_display::TO_STRING_IN_DISPLAY),
LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(&transmute::WRONG_TRANSMUTE),
LintId::of(&transmuting_null::TRANSMUTING_NULL),

View File

@ -1141,11 +1141,31 @@ fn detect_same_item_push<'tcx>(
if same_item_push_visitor.should_lint {
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
// Make sure that the push does not involve possibly mutating values
if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
if let PatKind::Wild = pat.kind {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
if let PatKind::Wild = pat.kind {
let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
if let ExprKind::Path(ref qpath) = pushed_item.kind {
if_chain! {
if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id);
let node = cx.tcx.hir().get(hir_id);
if let Node::Binding(pat) = node;
if let PatKind::Binding(bind_ann, ..) = pat.kind;
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
then {
span_lint_and_help(
cx,
SAME_ITEM_PUSH,
vec.span,
"it looks like the same item is being pushed into this Vec",
None,
&format!(
"try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
item_str, vec_str, item_str
),
)
}
}
} else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
span_lint_and_help(
cx,
SAME_ITEM_PUSH,

View File

@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
cx,
MAP_CLONE,
root.trim_start(receiver).unwrap(),
"You are needlessly cloning iterator elements",
"Remove the `map` call",
"you are needlessly cloning iterator elements",
"remove the `map` call",
String::new(),
Applicability::MachineApplicable,
)
@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
cx,
MAP_CLONE,
replace,
"You are using an explicit closure for copying elements",
"Consider calling the dedicated `copied` method",
"you are using an explicit closure for copying elements",
"consider calling the dedicated `copied` method",
format!(
"{}.copied()",
snippet_with_applicability(cx, root, "..", &mut applicability)
@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) {
cx,
MAP_CLONE,
replace,
"You are using an explicit closure for cloning elements",
"Consider calling the dedicated `cloned` method",
"you are using an explicit closure for cloning elements",
"consider calling the dedicated `cloned` method",
format!(
"{}.cloned()",
snippet_with_applicability(cx, root, "..", &mut applicability)

View File

@ -1,5 +1,5 @@
use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
use crate::utils::walk_ptrs_ty;
use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};

View File

@ -3,6 +3,7 @@ mod inefficient_to_string;
mod manual_saturating_arithmetic;
mod option_map_unwrap_or;
mod unnecessary_filter_map;
mod unnecessary_lazy_eval;
use std::borrow::Cow;
use std::fmt;
@ -14,11 +15,11 @@ 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::subst::GenericArgKind;
use rustc_middle::ty::{self, Ty, TyS};
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::{sym, SymbolStr};
@ -26,12 +27,12 @@ use rustc_span::symbol::{sym, SymbolStr};
use crate::consts::{constant, Constant};
use crate::utils::usage::mutated_variables;
use crate::utils::{
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy,
is_ctor_or_promotable_const_function, 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_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty,
walk_ptrs_ty_depth, SpanlessEq,
contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro,
is_copy, is_ctor_or_promotable_const_function, 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_note, span_lint_and_sugg,
span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
};
declare_clippy_lint! {
@ -723,6 +724,7 @@ declare_clippy_lint! {
/// **Known problems:** None.
///
/// **Example:**
/// In an impl block:
/// ```rust
/// # struct Foo;
/// # struct NotAFoo;
@ -735,25 +737,40 @@ declare_clippy_lint! {
///
/// ```rust
/// # struct Foo;
/// # struct FooError;
/// struct Bar(Foo);
/// impl Foo {
/// // Good. Return type contains `Self`
/// fn new() -> Result<Foo, FooError> {
/// # Ok(Foo)
/// // Bad. The type name must contain `Self`
/// fn new() -> Bar {
/// # Bar(Foo)
/// }
/// }
/// ```
///
/// ```rust
/// # struct Foo;
/// struct Bar(Foo);
/// # struct FooError;
/// impl Foo {
/// // Bad. The type name must contain `Self`.
/// fn new() -> Bar {
/// # Bar(Foo)
/// // Good. Return type contains `Self`
/// fn new() -> Result<Foo, FooError> {
/// # Ok(Foo)
/// }
/// }
/// ```
///
/// Or in a trait definition:
/// ```rust
/// pub trait Trait {
/// // Bad. The type name must contain `Self`
/// fn new();
/// }
/// ```
///
/// ```rust
/// pub trait Trait {
/// // Good. Return type contains `Self`
/// fn new() -> Self;
/// }
/// ```
pub NEW_RET_NO_SELF,
style,
"not returning type containing `Self` in a `new` method"
@ -799,7 +816,7 @@ declare_clippy_lint! {
/// call_some_ffi_func(c_str);
/// }
/// ```
/// Here `c_str` point to a freed address. The correct use would be:
/// Here `c_str` points to a freed address. The correct use would be:
/// ```rust
/// # use std::ffi::CString;
/// # fn call_some_ffi_func(_: *const i8) {}
@ -1306,6 +1323,65 @@ declare_clippy_lint! {
"using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
}
declare_clippy_lint! {
/// **What it does:** Warns when using push_str with a single-character string literal,
/// and push with a char would work fine.
///
/// **Why is this bad?** It's less clear that we are pushing a single character
///
/// **Known problems:** None
///
/// **Example:**
/// ```
/// let mut string = String::new();
/// string.push_str("R");
/// ```
/// Could be written as
/// ```
/// let mut string = String::new();
/// string.push('R');
/// ```
pub SINGLE_CHAR_PUSH_STR,
style,
"`push_str()` used with a single-character string literal as parameter"
}
declare_clippy_lint! {
/// **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary
/// lazily evaluated closures on `Option` and `Result`.
///
/// This lint suggests changing the following functions, when eager evaluation results in
/// simpler code:
/// - `unwrap_or_else` to `unwrap_or`
/// - `and_then` to `and`
/// - `or_else` to `or`
/// - `get_or_insert_with` to `get_or_insert`
/// - `ok_or_else` to `ok_or`
///
/// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.
///
/// **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have
/// side effects. Eagerly evaluating them can change the semantics of the program.
///
/// **Example:**
///
/// ```rust
/// // example code where clippy issues a warning
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or_else(|| 42);
/// ```
/// Use instead:
/// ```rust
/// let opt: Option<u32> = None;
///
/// opt.unwrap_or(42);
/// ```
pub UNNECESSARY_LAZY_EVALUATIONS,
style,
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
}
declare_lint_pass!(Methods => [
UNWRAP_USED,
EXPECT_USED,
@ -1327,6 +1403,7 @@ declare_lint_pass!(Methods => [
INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SINGLE_CHAR_PUSH_STR,
SEARCH_IS_SOME,
TEMPORARY_CSTRING_AS_PTR,
FILTER_NEXT,
@ -1354,6 +1431,7 @@ declare_lint_pass!(Methods => [
ZST_OFFSET,
FILETYPE_IS_FILE,
OPTION_AS_REF_DEREF,
UNNECESSARY_LAZY_EVALUATIONS,
]);
impl<'tcx> LateLintPass<'tcx> for Methods {
@ -1374,13 +1452,19 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
["unwrap_or_else", "map"] => {
if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or");
}
},
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
["and_then", ..] => {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and");
bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]);
bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]);
},
["or_else", ..] => {
unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or");
bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]);
},
["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]),
@ -1424,6 +1508,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["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),
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"),
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"),
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"),
_ => {},
}
@ -1441,6 +1528,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
inefficient_to_string::lint(cx, expr, &args[0], self_ty);
}
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
lint_single_char_push_string(cx, expr, args);
}
}
match self_ty.kind {
ty::Ref(_, ty, _) if ty.kind == ty::Str => {
for &(method, pos) in &PATTERN_METHODS {
@ -1470,6 +1563,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
}
}
#[allow(clippy::too_many_lines)]
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
if in_external_macro(cx.sess(), impl_item.span) {
return;
@ -1495,16 +1589,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
then {
if cx.access_levels.is_exported(impl_item.hir_id) {
// check missing trait implementations
for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS {
if name == method_name &&
sig.decl.inputs.len() == n_args &&
out_type.matches(cx, &sig.decl.output) &&
self_kind.matches(cx, self_ty, first_arg_ty) &&
fn_header_equals(*fn_header, sig.header) {
span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!(
"defining a method called `{}` on this type; consider implementing \
the `{}` trait or choosing a less ambiguous name", name, trait_name));
// check missing trait implementations
for method_config in &TRAIT_METHODS {
if name == method_config.method_name &&
sig.decl.inputs.len() == method_config.param_count &&
method_config.output_type.matches(cx, &sig.decl.output) &&
method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
fn_header_equals(method_config.fn_header, sig.header) &&
method_config.lifetime_param_cond(&impl_item)
{
span_lint_and_help(
cx,
SHOULD_IMPLEMENT_TRAIT,
impl_item.span,
&format!(
"method `{}` can be confused for the standard trait method `{}::{}`",
method_config.method_name,
method_config.trait_name,
method_config.method_name
),
None,
&format!(
"consider implementing the trait `{}` or choosing a less ambiguous method name",
method_config.trait_name
)
);
}
}
}
@ -1538,19 +1647,16 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
}
}
// if this impl block implements a trait, lint in trait definition instead
if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind {
return;
}
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id);
let contains_self_ty = |ty: Ty<'tcx>| {
ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => TyS::same_type(self_ty, inner_ty),
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
})
};
// walk the return type and check for Self (this does not check associated types)
if contains_self_ty(ret_ty) {
if contains_ty(ret_ty, self_ty) {
return;
}
@ -1560,7 +1666,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
for &(predicate, _span) in cx.tcx.predicates_of(def_id).predicates {
if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
// walk the associated type and check for Self
if contains_self_ty(projection_predicate.ty) {
if contains_ty(projection_predicate.ty, self_ty) {
return;
}
}
@ -1577,6 +1683,26 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
}
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if_chain! {
if !in_external_macro(cx.tcx.sess, item.span);
if item.ident.name == sym!(new);
if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.hir_id);
let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty();
if !contains_ty(ret_ty, self_ty);
then {
span_lint(
cx,
NEW_RET_NO_SELF,
item.span,
"methods called `new` usually return `Self`",
);
}
}
}
}
/// Checks for the `OR_FUN_CALL` lint.
@ -2057,18 +2183,15 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::
return;
};
let snippet = snippet_with_macro_callsite(cx, arg.span, "_");
span_lint_and_sugg(
cx,
CLONE_ON_REF_PTR,
expr.span,
"using `.clone()` on a ref-counted pointer",
"try this",
format!(
"{}::<{}>::clone(&{})",
caller_type,
subst.type_at(0),
snippet(cx, arg.span, "_")
),
format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet),
Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak
);
}
@ -2280,7 +2403,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
cx,
ITER_NEXT_SLICE,
expr.span,
"Using `.iter().next()` on a Slice without end index.",
"using `.iter().next()` on a Slice without end index",
"try calling",
format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
applicability,
@ -2299,7 +2422,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_
cx,
ITER_NEXT_SLICE,
expr.span,
"Using `.iter().next()` on an array",
"using `.iter().next()` on an array",
"try calling",
format!(
"{}.get(0)",
@ -2618,12 +2741,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
}
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
/// Return true if lint triggered
fn lint_map_unwrap_or_else<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
map_args: &'tcx [hir::Expr<'_>],
unwrap_args: &'tcx [hir::Expr<'_>],
) {
) -> bool {
// lint if the caller of `map()` is an `Option`
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type));
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(result_type));
@ -2635,10 +2759,10 @@ fn lint_map_unwrap_or_else<'tcx>(
let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
return;
return false;
}
} else {
return;
return false;
}
// lint message
@ -2668,10 +2792,14 @@ fn lint_map_unwrap_or_else<'tcx>(
map_snippet, unwrap_snippet,
),
);
return true;
} else if same_span && multiline {
span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
};
return true;
}
}
false
}
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
@ -3124,15 +3252,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx
}
}
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
fn get_hint_if_single_char_arg(
cx: &LateContext<'_>,
arg: &hir::Expr<'_>,
applicability: &mut Applicability,
) -> Option<String> {
if_chain! {
if let hir::ExprKind::Lit(lit) = &arg.kind;
if let ast::LitKind::Str(r, style) = lit.node;
if r.as_str().len() == 1;
let string = r.as_str();
if string.len() == 1;
then {
let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style {
let nhash = nhash as usize;
// for raw string: r##"a"##
@ -3142,19 +3273,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr
&snip[1..(snip.len() - 1)]
};
let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
span_lint_and_sugg(
cx,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
"try using a `char` instead",
hint,
applicability,
);
Some(hint)
} else {
None
}
}
}
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
let mut applicability = Applicability::MachineApplicable;
if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) {
span_lint_and_sugg(
cx,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
"try using a `char` instead",
hint,
applicability,
);
}
}
/// lint for length-1 `str`s as argument for `push_str`
fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
let sugg = format!("{}.push({})", base_string_snippet, extension_string);
span_lint_and_sugg(
cx,
SINGLE_CHAR_PUSH_STR,
expr.span,
"calling `push_str()` using a single-character string literal",
"consider using `push` with a character literal",
sugg,
applicability,
);
}
}
/// Checks for the `USELESS_ASREF` lint.
fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
@ -3292,7 +3451,12 @@ fn lint_option_as_ref_deref<'tcx>(
];
let is_deref = match map_args[1].kind {
hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)),
hir::ExprKind::Path(ref expr_qpath) => cx
.qpath_res(expr_qpath, map_args[1].hir_id)
.opt_def_id()
.map_or(false, |fun_def_id| {
deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
}),
hir::ExprKind::Closure(_, _, body_id, _, _) => {
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
@ -3403,38 +3567,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader {
abi: rustc_target::spec::abi::Abi::Rust,
};
struct ShouldImplTraitCase {
trait_name: &'static str,
method_name: &'static str,
param_count: usize,
fn_header: hir::FnHeader,
// implicit self kind expected (none, self, &self, ...)
self_kind: SelfKind,
// checks against the output type
output_type: OutType,
// certain methods with explicit lifetimes can't implement the equivalent trait method
lint_explicit_lifetime: bool,
}
impl ShouldImplTraitCase {
const fn new(
trait_name: &'static str,
method_name: &'static str,
param_count: usize,
fn_header: hir::FnHeader,
self_kind: SelfKind,
output_type: OutType,
lint_explicit_lifetime: bool,
) -> ShouldImplTraitCase {
ShouldImplTraitCase {
trait_name,
method_name,
param_count,
fn_header,
self_kind,
output_type,
lint_explicit_lifetime,
}
}
fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
self.lint_explicit_lifetime
|| !impl_item.generics.params.iter().any(|p| {
matches!(
p.kind,
hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
}
)
})
}
}
#[rustfmt::skip]
const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [
("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"),
("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"),
("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"),
("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"),
("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"),
("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"),
("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"),
("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"),
("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"),
("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"),
("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"),
("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"),
("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"),
("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"),
("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"),
("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"),
("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"),
("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"),
("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"),
("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"),
("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"),
("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"),
("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"),
("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"),
("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"),
("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"),
("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"),
("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"),
("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"),
("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"),
const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
ShouldImplTraitCase::new("std::ops::Add", "add", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, FN_HEADER, SelfKind::Ref, OutType::Any, true),
ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, FN_HEADER, SelfKind::Ref, OutType::Any, true),
// FIXME: default doesn't work
ShouldImplTraitCase::new("std::default::Default", "default", 0, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::Div", "div", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, FN_HEADER, SelfKind::RefMut, OutType::Unit, true),
ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, FN_HEADER, SelfKind::Ref, OutType::Bool, true),
ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, FN_HEADER, SelfKind::No, OutType::Any, true),
ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, FN_HEADER, SelfKind::Ref, OutType::Unit, true),
ShouldImplTraitCase::new("std::ops::Index", "index", 2, FN_HEADER, SelfKind::Ref, OutType::Ref, true),
ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, FN_HEADER, SelfKind::RefMut, OutType::Ref, true),
ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, FN_HEADER, SelfKind::RefMut, OutType::Any, false),
ShouldImplTraitCase::new("std::ops::Not", "not", 1, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true),
];
#[rustfmt::skip]

View File

@ -0,0 +1,111 @@
use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::UNNECESSARY_LAZY_EVALUATIONS;
// Return true if the expression is an accessor of any of the arguments
fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
params.iter().any(|arg| {
if_chain! {
if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
if let [p, ..] = path.segments;
then {
ident.name == p.ident.name
} else {
false
}
}
})
}
fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
paths.iter().any(|candidate| match_qpath(path, candidate))
}
fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
match expr.kind {
// Closures returning literals can be unconditionally simplified
hir::ExprKind::Lit(_) => true,
hir::ExprKind::Index(ref object, ref index) => {
// arguments are not being indexed into
if expr_uses_argument(object, params) {
false
} else {
// arguments are not used as index
!expr_uses_argument(index, params)
}
},
// Reading fields can be simplified if the object is not an argument of the closure
hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
// Paths can be simplified if the root is not the argument, this also covers None
hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
// Calls to Some, Ok, Err can be considered literals if they don't derive an argument
hir::ExprKind::Call(ref func, ref args) => if_chain! {
if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
if let hir::ExprKind::Path(ref path) = func.kind;
if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
then {
// Recursively check all arguments
args.iter().all(|arg| can_simplify(arg, params, variant_calls))
} else {
false
}
},
// For anything more complex than the above, a closure is probably the right solution,
// or the case is handled by an other lint
_ => false,
}
}
/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
/// replaced with `<fn>(return value of simple closure)`
pub(super) fn lint<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
args: &'tcx [hir::Expr<'_>],
allow_variant_calls: bool,
simplify_using: &str,
) {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
if is_option || is_result {
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
let body = cx.tcx.hir().body(eid);
let ex = &body.value;
let params = &body.params;
if can_simplify(ex, params, allow_variant_calls) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
} else {
"unnecessary closure used to substitute value for `Result::Err`"
};
span_lint_and_sugg(
cx,
UNNECESSARY_LAZY_EVALUATIONS,
expr.span,
msg,
&format!("Use `{}` instead", simplify_using),
format!(
"{0}.{1}({2})",
snippet(cx, args[0].span, ".."),
simplify_using,
snippet(cx, ex.span, ".."),
),
Applicability::MachineApplicable,
);
}
}
}
}

View File

@ -433,8 +433,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
return;
}
let binding = match expr.kind {
ExprKind::Path(hir::QPath::LangItem(..)) => None,
ExprKind::Path(ref qpath) => {
ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
let binding = last_path_segment(qpath).ident.as_str();
if binding.starts_with('_') &&
!binding.starts_with("__") &&

View File

@ -9,8 +9,8 @@ declare_clippy_lint! {
/// **What it does:** Detects passing a mutable reference to a function that only
/// requires an immutable reference.
///
/// **Why is this bad?** The immutable reference rules out all other references
/// to the value. Also the code misleads about the intent of the call site.
/// **Why is this bad?** The mutable reference rules out all other references to
/// the value. Also the code misleads about the intent of the call site.
///
/// **Known problems:** None.
///
@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
arguments,
cx.typeck_results().expr_ty(fn_expr),
&rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
"function",
);
}
},
@ -46,14 +47,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(e.hir_id);
let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
check_arguments(cx, arguments, method_type, &path.ident.as_str())
check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method")
},
_ => (),
}
}
}
fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) {
fn check_arguments<'tcx>(
cx: &LateContext<'tcx>,
arguments: &[Expr<'_>],
type_definition: Ty<'tcx>,
name: &str,
fn_kind: &str,
) {
match type_definition.kind {
ty::FnDef(..) | ty::FnPtr(_) => {
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de
cx,
UNNECESSARY_MUT_PASSED,
argument.span,
&format!("The function/method `{}` doesn't need a mutable reference", name),
&format!("the {} `{}` doesn't need a mutable reference", fn_kind, name),
);
}
},

View File

@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex {
let mutex_param = subst.type_at(0);
if let Some(atomic_name) = get_atomic_name(mutex_param) {
let msg = format!(
"Consider using an `{}` instead of a `Mutex` here. If you just want the locking \
behavior and not the internal type, consider using `Mutex<()>`.",
"consider using an `{}` instead of a `Mutex` here; if you just want the locking \
behavior and not the internal type, consider using `Mutex<()>`",
atomic_name
);
match mutex_param.kind {

View File

@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
needs_check_adjustment = false;
},
ExprKind::Field(..) => {
dereferenced_expr = parent_expr;
needs_check_adjustment = true;
// Check whether implicit dereferences happened;
// if so, no need to go further up
// because of the same reason as the `ExprKind::Unary` case.
if cx
.typeck_results()
.expr_adjustments(dereferenced_expr)
.iter()
.any(|adj| matches!(adj.kind, Adjust::Deref(_)))
{
break;
}
dereferenced_expr = parent_expr;
},
ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => {
// `e[i]` => desugared to `*Index::index(&e, i)`,

View File

@ -1,4 +1,5 @@
use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence {
}
}
if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind {
if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind {
if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind {
let mut arg = operand;
let mut all_odd = true;
while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind {
let path_segment_str = path_segment.ident.name.as_str();
if let Some(slf) = args.first() {
if let ExprKind::Lit(ref lit) = slf.kind {
match lit.kind {
LitKind::Int(..) | LitKind::Float(..) => {
if ALLOWED_ODD_FUNCTIONS
.iter()
.any(|odd_function| **odd_function == *path_segment_str)
{
return;
}
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
PRECEDENCE,
expr.span,
"unary minus has lower precedence than method call",
"consider adding parentheses to clarify your intent",
format!(
"-({})",
snippet_with_applicability(cx, rhs.span, "..", &mut applicability)
),
applicability,
);
},
_ => (),
}
}
all_odd &= ALLOWED_ODD_FUNCTIONS
.iter()
.any(|odd_function| **odd_function == *path_segment_str);
arg = args.first().expect("A method always has a receiver.");
}
if_chain! {
if !all_odd;
if let ExprKind::Lit(lit) = &arg.kind;
if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind;
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
PRECEDENCE,
expr.span,
"unary minus has lower precedence than method call",
"consider adding parentheses to clarify your intent",
format!(
"-({})",
snippet_with_applicability(cx, operand.span, "..", &mut applicability)
),
applicability,
);
}
}
}

View File

@ -36,14 +36,27 @@ declare_clippy_lint! {
/// argument may also fail to compile if you change the argument. Applying
/// this lint on them will fix the problem, but they may be in other crates.
///
/// One notable example of a function that may cause issues, and which cannot
/// easily be changed due to being in the standard library is `Vec::contains`.
/// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
/// it will compile, but if a `&[T]` is passed then it will not compile.
///
/// ```ignore
/// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
/// let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
///
/// vec_of_vecs.contains(v)
/// }
/// ```
///
/// Also there may be `fn(&Vec)`-typed references pointing to your function.
/// If you have them, you will get a compiler error after applying this lint's
/// suggestions. You then have the choice to undo your changes or change the
/// type of the reference.
///
/// Note that if the function is part of your public interface, there may be
/// other crates referencing it you may not be aware. Carefully deprecate the
/// function before applying the lint suggestions in this case.
/// other crates referencing it, of which you may not be aware. Carefully
/// deprecate the function before applying the lint suggestions in this case.
///
/// **Example:**
/// ```ignore

View File

@ -7,8 +7,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use crate::utils::sugg::Sugg;
use crate::utils::{
higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
span_lint_and_sugg, SpanlessEq,
eq_expr_value, higher, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
span_lint_and_sugg,
};
declare_clippy_lint! {
@ -65,7 +65,7 @@ impl QuestionMark {
if let ExprKind::Block(block, None) = &else_.kind;
if block.stmts.is_empty();
if let Some(block_expr) = &block.expr;
if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr);
if eq_expr_value(cx, subject, block_expr);
then {
replacement = Some(format!("Some({}?)", receiver_str));
}

View File

@ -77,7 +77,7 @@ impl EarlyLintPass for RedundantClosureCall {
cx,
REDUNDANT_CLOSURE_CALL,
expr.span,
"try not to call a closure in the expression where it is declared.",
"try not to call a closure in the expression where it is declared",
|diag| {
if decl.inputs.is_empty() {
let mut app = Applicability::MachineApplicable;
@ -95,12 +95,17 @@ impl EarlyLintPass for RedundantClosureCall {
impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize {
struct ClosureUsageCount<'tcx> {
fn count_closure_usage<'a, 'tcx>(
cx: &'a LateContext<'tcx>,
block: &'tcx hir::Block<'_>,
path: &'tcx hir::Path<'tcx>,
) -> usize {
struct ClosureUsageCount<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
path: &'tcx hir::Path<'tcx>,
count: usize,
};
impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> {
impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
@ -117,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
}
fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
hir_visit::NestedVisitorMap::None
hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
}
};
let mut closure_usage_count = ClosureUsageCount { path, count: 0 };
let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
closure_usage_count.visit_block(block);
closure_usage_count.count
}
@ -136,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
if let hir::ExprKind::Call(ref closure, _) = call.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind;
if ident == path.segments[0].ident;
if count_closure_usage(block, path) == 1;
if count_closure_usage(cx, block, path) == 1;
then {
span_lint(
cx,

View File

@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
if path.ident.name == sym!(repeat);
if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&args[1]);
if !in_macro(args[0].span);
if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count);
if !in_macro(receiver.span);
then {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0]));
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver));
if ty.is_str() {
span_lint_and_sugg(
cx,
@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
expr.span,
"calling `repeat(1)` on str",
"consider using `.to_string()` instead",
format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)),
Applicability::MachineApplicable,
);
} else if ty.builtin_index().is_some() {
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
expr.span,
"calling `repeat(1)` on slice",
"consider using `.to_vec()` instead",
format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)),
Applicability::MachineApplicable,
);
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
expr.span,
"calling `repeat(1)` on a string literal",
"consider using `.clone()` instead",
format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)),
Applicability::MachineApplicable,
);
}

View File

@ -1,14 +1,43 @@
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_ast::visit::FnKind;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::BytePos;
use crate::utils::{snippet_opt, span_lint_and_sugg, span_lint_and_then};
use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
declare_clippy_lint! {
/// **What it does:** Checks for `let`-bindings, which are subsequently
/// returned.
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
/// more rusty.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo() -> String {
/// let x = String::new();
/// x
/// }
/// ```
/// instead, use
/// ```
/// fn foo() -> String {
/// String::new()
/// }
/// ```
pub LET_AND_RETURN,
style,
"creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
}
declare_clippy_lint! {
/// **What it does:** Checks for return statements at the end of a block.
@ -16,8 +45,7 @@ declare_clippy_lint! {
/// **Why is this bad?** Removing the `return` and semicolon will make the code
/// more rusty.
///
/// **Known problems:** If the computation returning the value borrows a local
/// variable, removing the `return` may run afoul of the borrow checker.
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
@ -36,248 +64,226 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}
declare_clippy_lint! {
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
///
/// **Why is this bad?** Such expressions add no value, but can make the code
/// less readable. Depending on formatting they can make a `break` or `return`
/// statement look like a function call.
///
/// **Known problems:** The lint currently misses unit return types in types,
/// e.g., the `F` in `fn generic_unit<F: Fn() -> ()>(f: F) { .. }`.
///
/// **Example:**
/// ```rust
/// fn return_unit() -> () {
/// ()
/// }
/// ```
pub UNUSED_UNIT,
style,
"needless unit expression"
}
#[derive(PartialEq, Eq, Copy, Clone)]
enum RetReplacement {
Empty,
Block,
}
declare_lint_pass!(Return => [NEEDLESS_RETURN, UNUSED_UNIT]);
declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
impl Return {
// Check the final stmt or expr in a block for unnecessary return.
fn check_block_return(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
if let Some(stmt) = block.stmts.last() {
match stmt.kind {
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
self.check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
},
_ => (),
impl<'tcx> LateLintPass<'tcx> for Return {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
// we need both a let-binding stmt and an expr
if_chain! {
if let Some(retexpr) = block.expr;
if let Some(stmt) = block.stmts.iter().last();
if let StmtKind::Local(local) = &stmt.kind;
if local.ty.is_none();
if local.attrs.is_empty();
if let Some(initexpr) = &local.init;
if let PatKind::Binding(.., ident, _) = local.pat.kind;
if let ExprKind::Path(qpath) = &retexpr.kind;
if match_qpath(qpath, &[&*ident.name.as_str()]);
if !last_statement_borrows(cx, initexpr);
if !in_external_macro(cx.sess(), initexpr.span);
if !in_external_macro(cx.sess(), retexpr.span);
if !in_external_macro(cx.sess(), local.span);
if !in_macro(local.span);
then {
span_lint_and_then(
cx,
LET_AND_RETURN,
retexpr.span,
"returning the result of a `let` binding from a block",
|err| {
err.span_label(local.span, "unnecessary `let` binding");
if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() {
snippet.push_str(" as _");
}
err.multipart_suggestion(
"return the expression directly",
vec![
(local.span, String::new()),
(retexpr.span, snippet),
],
Applicability::MachineApplicable,
);
} else {
err.span_help(initexpr.span, "this expression can be directly returned");
}
},
);
}
}
}
// Check the final expression in a block if it's a return.
fn check_final_expr(
fn check_fn(
&mut self,
cx: &EarlyContext<'_>,
expr: &ast::Expr,
span: Option<Span>,
replacement: RetReplacement,
cx: &LateContext<'tcx>,
kind: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
_: Span,
_: HirId,
) {
match expr.kind {
// simple return is always "bad"
ast::ExprKind::Ret(ref inner) => {
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
if !expr.attrs.iter().any(attr_is_cfg) {
Self::emit_return_lint(
match kind {
FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty),
FnKind::ItemFn(..) | FnKind::Method(..) => {
if let ExprKind::Block(ref block, _) = body.value.kind {
check_block_return(cx, block);
}
},
}
}
}
fn attr_is_cfg(attr: &Attribute) -> bool {
attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
}
fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
if let Some(expr) = block.expr {
check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
} else if let Some(stmt) = block.stmts.iter().last() {
match stmt.kind {
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
},
_ => (),
}
}
}
fn check_final_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
span: Option<Span>,
replacement: RetReplacement,
) {
match expr.kind {
// simple return is always "bad"
ExprKind::Ret(ref inner) => {
// allow `#[cfg(a)] return a; #[cfg(b)] return b;`
if !expr.attrs.iter().any(attr_is_cfg) {
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if !borrows {
emit_return_lint(
cx,
span.expect("`else return` is not possible"),
inner.as_ref().map(|i| i.span),
replacement,
);
}
},
// a whole block? check it!
ast::ExprKind::Block(ref block, _) => {
self.check_block_return(cx, block);
},
// an if/if let expr, check both exprs
// note, if without else is going to be a type checking error anyways
// (except for unit type functions) so we don't match it
ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
self.check_block_return(cx, ifblock);
self.check_final_expr(cx, elsexpr, None, RetReplacement::Empty);
},
// a match expr, check all arms
ast::ExprKind::Match(_, ref arms) => {
for arm in arms {
self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
}
},
// a whole block? check it!
ExprKind::Block(ref block, _) => {
check_block_return(cx, block);
},
// a match expr, check all arms
// an if/if let expr, check both exprs
// note, if without else is going to be a type checking error anyways
// (except for unit type functions) so we don't match it
ExprKind::Match(_, ref arms, source) => match source {
MatchSource::Normal => {
for arm in arms.iter() {
check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
}
},
MatchSource::IfDesugar {
contains_else_clause: true,
}
| MatchSource::IfLetDesugar {
contains_else_clause: true,
} => {
if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind {
check_block_return(cx, ifblock);
}
check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
},
_ => (),
}
}
fn emit_return_lint(cx: &EarlyContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
match inner_span {
Some(inner_span) => {
if in_external_macro(cx.sess(), inner_span) || inner_span.from_expansion() {
return;
}
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
}
})
},
None => match replacement {
RetReplacement::Empty => {
span_lint_and_sugg(
cx,
NEEDLESS_RETURN,
ret_span,
"unneeded `return` statement",
"remove `return`",
String::new(),
Applicability::MachineApplicable,
);
},
RetReplacement::Block => {
span_lint_and_sugg(
cx,
NEEDLESS_RETURN,
ret_span,
"unneeded `return` statement",
"replace `return` with an empty block",
"{}".to_string(),
Applicability::MachineApplicable,
);
},
},
}
},
_ => (),
}
}
impl EarlyLintPass for Return {
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
match kind {
FnKind::Fn(.., Some(block)) => self.check_block_return(cx, block),
FnKind::Closure(_, body) => self.check_final_expr(cx, body, Some(body.span), RetReplacement::Empty),
FnKind::Fn(.., None) => {},
}
if_chain! {
if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
if let ast::TyKind::Tup(ref vals) = ty.kind;
if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
then {
lint_unneeded_unit_return(cx, ty, span);
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
match inner_span {
Some(inner_span) => {
if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
return;
}
}
}
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
if_chain! {
if let Some(ref stmt) = block.stmts.last();
if let ast::StmtKind::Expr(ref expr) = stmt.kind;
if is_unit_expr(expr) && !stmt.span.from_expansion();
then {
let sp = expr.span;
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
}
})
},
None => match replacement {
RetReplacement::Empty => {
span_lint_and_sugg(
cx,
UNUSED_UNIT,
sp,
"unneeded unit expression",
"remove the final `()`",
NEEDLESS_RETURN,
ret_span,
"unneeded `return` statement",
"remove `return`",
String::new(),
Applicability::MachineApplicable,
);
}
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
match e.kind {
ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
if is_unit_expr(expr) && !expr.span.from_expansion() {
span_lint_and_sugg(
cx,
UNUSED_UNIT,
expr.span,
"unneeded `()`",
"remove the `()`",
String::new(),
Applicability::MachineApplicable,
);
}
},
_ => (),
}
}
fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
let segments = &poly.trait_ref.path.segments;
if_chain! {
if segments.len() == 1;
if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
if let Some(args) = &segments[0].args;
if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
if let ast::FnRetTy::Ty(ty) = &generic_args.output;
if ty.kind.is_unit();
then {
lint_unneeded_unit_return(cx, ty, generic_args.span);
}
}
}
}
fn attr_is_cfg(attr: &ast::Attribute) -> bool {
attr.meta_item_list().is_some() && attr.has_name(sym!(cfg))
}
// get the def site
#[must_use]
fn get_def(span: Span) -> Option<Span> {
if span.from_expansion() {
Some(span.ctxt().outer_expn_data().def_site)
} else {
None
}
}
// is this expr a `()` unit?
fn is_unit_expr(expr: &ast::Expr) -> bool {
if let ast::ExprKind::Tup(ref vals) = expr.kind {
vals.is_empty()
} else {
false
}
}
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
fn_source
.rfind("->")
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
(
#[allow(clippy::cast_possible_truncation)]
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
RetReplacement::Block => {
span_lint_and_sugg(
cx,
NEEDLESS_RETURN,
ret_span,
"unneeded `return` statement",
"replace `return` with an empty block",
"{}".to_string(),
Applicability::MachineApplicable,
)
})
} else {
(ty.span, Applicability::MaybeIncorrect)
};
span_lint_and_sugg(
cx,
UNUSED_UNIT,
ret_span,
"unneeded unit return type",
"remove the `-> ()`",
String::new(),
appl,
);
);
},
},
}
}
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
let mut visitor = BorrowVisitor { cx, borrows: false };
walk_expr(&mut visitor, expr);
visitor.borrows
}
struct BorrowVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
borrows: bool,
}
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if self.borrows {
return;
}
if let Some(def_id) = fn_def_id(self.cx, expr) {
self.borrows = self
.cx
.tcx
.fn_sig(def_id)
.output()
.skip_binder()
.walk()
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
}
walk_expr(self, expr);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}

View File

@ -0,0 +1,51 @@
use crate::utils::{eq_expr_value, snippet, span_lint};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for explicit self-assignments.
///
/// **Why is this bad?** Self-assignments are redundant and unlikely to be
/// intentional.
///
/// **Known problems:** If expression contains any deref coercions or
/// indexing operations they are assumed not to have any side effects.
///
/// **Example:**
///
/// ```rust
/// struct Event {
/// id: usize,
/// x: i32,
/// y: i32,
/// }
///
/// fn copy_position(a: &mut Event, b: &Event) {
/// a.x = b.x;
/// a.y = a.y;
/// }
/// ```
pub SELF_ASSIGNMENT,
correctness,
"explicit self-assignment"
}
declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
if eq_expr_value(cx, lhs, rhs) {
let lhs = snippet(cx, lhs.span, "<lhs>");
let rhs = snippet(cx, rhs.span, "<rhs>");
span_lint(
cx,
SELF_ASSIGNMENT,
expr.span,
&format!("self-assignment of `{}` to `{}`", rhs, lhs),
);
}
}
}
}

View File

@ -86,6 +86,7 @@ struct LintDetection {
slice_name: String,
method: SortingKind,
method_args: String,
slice_type: String,
}
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
if let Some(slice) = &args.get(0);
if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str());
if is_slice_of_primitives(cx, slice);
if let Some(slice_type) = is_slice_of_primitives(cx, slice);
then {
let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str })
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
} else {
None
}
@ -111,9 +112,10 @@ impl LateLintPass<'_> for StableSortPrimitive {
STABLE_SORT_PRIMITIVE,
expr.span,
format!(
"Use {} instead of {}",
"used {} instead of {} to sort primitive type `{}`",
detection.method.stable_name(),
detection.method.unstable_name(),
detection.method.stable_name()
detection.slice_type,
)
.as_str(),
"try",

View File

@ -86,12 +86,20 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
cx,
expr,
binop.node,
&["Add", "Sub", "Mul", "Div"],
&[
"Add", "Sub", "Mul", "Div", "Rem", "BitAnd", "BitOr", "BitXor", "Shl", "Shr",
],
&[
hir::BinOpKind::Add,
hir::BinOpKind::Sub,
hir::BinOpKind::Mul,
hir::BinOpKind::Div,
hir::BinOpKind::Rem,
hir::BinOpKind::BitAnd,
hir::BinOpKind::BitOr,
hir::BinOpKind::BitXor,
hir::BinOpKind::Shl,
hir::BinOpKind::Shr,
],
) {
span_lint(

View File

@ -1,7 +1,7 @@
use crate::utils::sugg::Sugg;
use crate::utils::{
differing_macro_contexts, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty,
SpanlessEq,
differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
walk_ptrs_ty,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -92,8 +92,8 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if rhs2.segments.len() == 1;
if ident.as_str() == rhs2.segments[0].ident.as_str();
if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
if eq_expr_value(cx, tmp_init, lhs1);
if eq_expr_value(cx, rhs1, lhs2);
then {
if let ExprKind::Field(ref lhs1, _) = lhs1.kind {
if let ExprKind::Field(ref lhs2, _) = lhs2.kind {
@ -193,7 +193,7 @@ enum Slice<'a> {
fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> {
if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind {
if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind {
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
if eq_expr_value(cx, lhs1, lhs2) {
let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(lhs1));
if matches!(ty.kind, ty::Slice(_))
@ -221,8 +221,8 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if !differing_macro_contexts(first.span, second.span);
if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind;
if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind;
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
then {
let lhs0 = Sugg::hir_opt(cx, lhs0);
let rhs0 = Sugg::hir_opt(cx, rhs0);

View File

@ -0,0 +1,122 @@
use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint};
use if_chain::if_chain;
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// **What it does:** Checks for uses of `to_string()` in `Display` traits.
///
/// **Why is this bad?** Usually `to_string` is implemented indirectly
/// via `Display`. Hence using it while implementing `Display` would
/// lead to infinite recursion.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// use std::fmt;
///
/// struct Structure(i32);
/// impl fmt::Display for Structure {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "{}", self.to_string())
/// }
/// }
///
/// ```
/// Use instead:
/// ```rust
/// use std::fmt;
///
/// struct Structure(i32);
/// impl fmt::Display for Structure {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// write!(f, "{}", self.0)
/// }
/// }
/// ```
pub TO_STRING_IN_DISPLAY,
correctness,
"`to_string` method used while implementing `Display` trait"
}
#[derive(Default)]
pub struct ToStringInDisplay {
in_display_impl: bool,
self_hir_id: Option<HirId>,
}
impl ToStringInDisplay {
pub fn new() -> Self {
Self {
in_display_impl: false,
self_hir_id: None,
}
}
}
impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]);
impl LateLintPass<'_> for ToStringInDisplay {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_display_impl(cx, item) {
self.in_display_impl = true;
}
}
fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if is_display_impl(cx, item) {
self.in_display_impl = false;
self.self_hir_id = None;
}
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
if_chain! {
if self.in_display_impl;
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
let body = cx.tcx.hir().body(*body_id);
if !body.params.is_empty();
then {
let self_param = &body.params[0];
self.self_hir_id = Some(self_param.pat.hir_id);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind;
if path.ident.name == sym!(to_string);
if match_trait_method(cx, expr, &paths::TO_STRING);
if self.in_display_impl;
if let ExprKind::Path(ref qpath) = args[0].kind;
if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id);
if let Some(self_hir_id) = self.self_hir_id;
if hir_id == self_hir_id;
then {
span_lint(
cx,
TO_STRING_IN_DISPLAY,
expr.span,
"using `to_string` in `fmt::Display` implementation might lead to infinite recursion",
);
}
}
}
}
fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if_chain! {
if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind;
if let Some(did) = trait_ref.trait_def_id();
then {
match_def_path(cx, did, &paths::DISPLAY_TRAIT)
} else {
false
}
}
}

View File

@ -1,5 +1,5 @@
use crate::utils::{
is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg,
span_lint_and_then, sugg,
};
use if_chain::if_chain;
@ -331,6 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::TRANSMUTE);
then {
// Avoid suggesting from/to bits in const contexts.
// See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
let const_context = in_constant(cx, e.hir_id);
let from_ty = cx.typeck_results().expr_ty(&args[0]);
let to_ty = cx.typeck_results().expr_ty(e);
@ -544,7 +548,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
},
)
},
(ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then(
(ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then(
cx,
TRANSMUTE_INT_TO_FLOAT,
e.span,
@ -567,7 +571,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
);
},
),
(ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then(
(ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then(
cx,
TRANSMUTE_FLOAT_TO_INT,
e.span,

View File

@ -2,6 +2,7 @@ use std::cmp;
use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_ast::attr;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef {
return;
}
for a in attrs {
if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) {
return;
if let Some(meta_items) = a.meta_item_list() {
if a.has_name(sym!(proc_macro_derive))
|| (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
{
return;
}
}
}
},

View File

@ -1,10 +1,10 @@
use crate::utils::{
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
snippet_with_macro_callsite, span_lint_and_sugg,
is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
span_lint_and_sugg,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath, LangItem, MatchSource};
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};

View File

@ -353,14 +353,25 @@ impl Types {
);
return; // don't recurse into the type
}
if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) {
if match_type_parameter(cx, qpath, &paths::BOX).is_some() {
let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] {
GenericArg::Type(ty) => match &ty.kind {
TyKind::Path(qpath) => qpath,
_ => return,
},
_ => return,
};
let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] {
GenericArg::Type(ty) => ty.span,
_ => return,
};
span_lint_and_sugg(
cx,
REDUNDANT_ALLOCATION,
hir_ty.span,
"usage of `Rc<Box<T>>`",
"try",
snippet(cx, span, "..").to_string(),
format!("Rc<{}>", snippet(cx, inner_span, "..")),
Applicability::MachineApplicable,
);
return; // don't recurse into the type

View File

@ -2,9 +2,9 @@
use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
use crate::utils::{over, span_lint_and_then};
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
@ -340,7 +340,7 @@ fn take_pat(from: &mut Pat) -> Pat {
id: DUMMY_NODE_ID,
kind: Wild,
span: DUMMY_SP,
tokens: None
tokens: None,
};
mem::replace(from, dummy)
}

View File

@ -0,0 +1,144 @@
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_ast::visit::FnKind;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::BytePos;
use crate::utils::span_lint_and_sugg;
declare_clippy_lint! {
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
///
/// **Why is this bad?** Such expressions add no value, but can make the code
/// less readable. Depending on formatting they can make a `break` or `return`
/// statement look like a function call.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn return_unit() -> () {
/// ()
/// }
/// ```
pub UNUSED_UNIT,
style,
"needless unit expression"
}
declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]);
impl EarlyLintPass for UnusedUnit {
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
if_chain! {
if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
if let ast::TyKind::Tup(ref vals) = ty.kind;
if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
then {
lint_unneeded_unit_return(cx, ty, span);
}
}
}
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
if_chain! {
if let Some(ref stmt) = block.stmts.last();
if let ast::StmtKind::Expr(ref expr) = stmt.kind;
if is_unit_expr(expr) && !stmt.span.from_expansion();
then {
let sp = expr.span;
span_lint_and_sugg(
cx,
UNUSED_UNIT,
sp,
"unneeded unit expression",
"remove the final `()`",
String::new(),
Applicability::MachineApplicable,
);
}
}
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
match e.kind {
ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
if is_unit_expr(expr) && !expr.span.from_expansion() {
span_lint_and_sugg(
cx,
UNUSED_UNIT,
expr.span,
"unneeded `()`",
"remove the `()`",
String::new(),
Applicability::MachineApplicable,
);
}
},
_ => (),
}
}
fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
let segments = &poly.trait_ref.path.segments;
if_chain! {
if segments.len() == 1;
if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
if let Some(args) = &segments[0].args;
if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
if let ast::FnRetTy::Ty(ty) = &generic_args.output;
if ty.kind.is_unit();
then {
lint_unneeded_unit_return(cx, ty, generic_args.span);
}
}
}
}
// get the def site
#[must_use]
fn get_def(span: Span) -> Option<Span> {
if span.from_expansion() {
Some(span.ctxt().outer_expn_data().def_site)
} else {
None
}
}
// is this expr a `()` unit?
fn is_unit_expr(expr: &ast::Expr) -> bool {
if let ast::ExprKind::Tup(ref vals) = expr.kind {
vals.is_empty()
} else {
false
}
}
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
fn_source
.rfind("->")
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
(
#[allow(clippy::cast_possible_truncation)]
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
Applicability::MachineApplicable,
)
})
} else {
(ty.span, Applicability::MaybeIncorrect)
};
span_lint_and_sugg(
cx,
UNUSED_UNIT,
ret_span,
"unneeded unit return type",
"remove the `-> ()`",
String::new(),
appl,
);
}

View File

@ -0,0 +1,140 @@
use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
declare_clippy_lint! {
/// **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`
///
/// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
///
/// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.
///
/// **Example:**
/// Before:
/// ```rust
/// fn divisible_by_3(i_str: String) -> Result<(), String> {
/// let i = i_str
/// .parse::<i32>()
/// .expect("cannot divide the input by three");
///
/// if i % 3 != 0 {
/// Err("Number is not divisible by 3")?
/// }
///
/// Ok(())
/// }
/// ```
///
/// After:
/// ```rust
/// fn divisible_by_3(i_str: String) -> Result<(), String> {
/// let i = i_str
/// .parse::<i32>()
/// .map_err(|e| format!("cannot divide the input by three: {}", e))?;
///
/// if i % 3 != 0 {
/// Err("Number is not divisible by 3")?
/// }
///
/// Ok(())
/// }
/// ```
pub UNWRAP_IN_RESULT,
restriction,
"functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
}
declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
if_chain! {
// first check if it's a method or function
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
// checking if its return type is `result` or `option`
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type))
|| is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type));
then {
lint_impl_body(cx, impl_item.span, impl_item);
}
}
}
}
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ImplItemKind};
struct FindExpectUnwrap<'a, 'tcx> {
lcx: &'a LateContext<'tcx>,
typeck_results: &'tcx ty::TypeckResults<'tcx>,
result: Vec<Span>,
}
impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
{
self.result.push(expr.span);
}
}
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0]));
if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type))
|| is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type))
{
self.result.push(expr.span);
}
}
// and check sub-expressions
intravisit::walk_expr(self, expr);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
}
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
if_chain! {
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
then {
let body = cx.tcx.hir().body(body_id);
let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
let mut fpu = FindExpectUnwrap {
lcx: cx,
typeck_results: cx.tcx.typeck(impl_item_def_id),
result: Vec::new(),
};
fpu.visit_expr(&body.value);
// if we've found one, lint
if !fpu.result.is_empty() {
span_lint_and_then(
cx,
UNWRAP_IN_RESULT,
impl_span,
"used unwrap or expect in a function that returns result or option",
move |diag| {
diag.help(
"unwrap and expect should not be used in a function that returns result or option" );
diag.span_note(fpu.result, "potential non-recoverable error(s)");
});
}
}
}
}

View File

@ -50,7 +50,7 @@ declare_clippy_lint! {
/// ```
pub USE_SELF,
nursery,
"Unnecessary structure name repetition whereas `Self` is applicable"
"unnecessary structure name repetition whereas `Self` is applicable"
}
declare_lint_pass!(UseSelf => [USE_SELF]);

View File

@ -1,3 +1,4 @@
use crate::utils::sugg::Sugg;
use crate::utils::{
get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet,
snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg,
@ -158,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
if TyS::same_type(a, b);
then {
let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par();
let sugg_msg =
format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
span_lint_and_sugg(
@ -167,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
e.span,
"useless conversion to the same type",
&sugg_msg,
sugg,
sugg.to_string(),
Applicability::MachineApplicable, // snippet
);
}

View File

@ -5,8 +5,8 @@
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
use crate::utils::{both, over};
use rustc_ast::{self as ast, *};
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, *};
use rustc_span::symbol::Ident;
use std::mem;

View File

@ -175,18 +175,15 @@ impl PrintVisitor {
}
fn print_qpath(&mut self, path: &QPath<'_>) {
match *path {
QPath::LangItem(lang_item, _) => {
println!(
" if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
self.current, lang_item,
);
},
_ => {
print!(" if match_qpath({}, &[", self.current);
print_path(path, &mut true);
println!("]);");
},
if let QPath::LangItem(lang_item, _) = *path {
println!(
" if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
self.current, lang_item,
);
} else {
print!(" if match_qpath({}, &[", self.current);
print_path(path, &mut true);
println!("]);");
}
}
}

View File

@ -138,7 +138,7 @@ define_Conf! {
(type_complexity_threshold, "type_complexity_threshold": u64, 250),
/// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have
(single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4),
/// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
/// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
(too_large_for_stack, "too_large_for_stack": u64, 200),
/// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger
(enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3),

View File

@ -56,43 +56,45 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
}
match expr.kind {
hir::ExprKind::Call(ref path, ref args) if matches!(
path.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
) => Some(Range {
start: Some(&args[0]),
end: Some(&args[1]),
limits: ast::RangeLimits::Closed,
}),
hir::ExprKind::Struct(ref path, ref fields, None) => {
match path {
hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
start: None,
end: None,
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: None,
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::Closed,
}),
hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
}),
_ => None,
}
hir::ExprKind::Call(ref path, ref args)
if matches!(
path.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
) =>
{
Some(Range {
start: Some(&args[0]),
end: Some(&args[1]),
limits: ast::RangeLimits::Closed,
})
},
hir::ExprKind::Struct(ref path, ref fields, None) => match path {
hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
start: None,
end: None,
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: None,
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
start: Some(get_field("start", fields)?),
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
}),
hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::Closed,
}),
hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
start: None,
end: Some(get_field("end", fields)?),
limits: ast::RangeLimits::HalfOpen,
}),
_ => None,
},
_ => None,
}

View File

@ -3,9 +3,9 @@ use crate::utils::differing_macro_contexts;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::{
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat,
FnRetTy, GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName,
Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy,
GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lint::LateContext;
use rustc_middle::ich::StableHashingContextProvider;
@ -23,9 +23,7 @@ pub struct SpanlessEq<'a, 'tcx> {
/// Context used to evaluate constant expressions.
cx: &'a LateContext<'tcx>,
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
/// If is true, never consider as equal expressions containing function
/// calls.
ignore_fn: bool,
allow_side_effects: bool,
}
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@ -33,13 +31,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
Self {
cx,
maybe_typeck_results: cx.maybe_typeck_results(),
ignore_fn: false,
allow_side_effects: true,
}
}
pub fn ignore_fn(self) -> Self {
/// Consider expressions containing potential side effects as not equal.
pub fn deny_side_effects(self) -> Self {
Self {
ignore_fn: true,
allow_side_effects: false,
..self
}
}
@ -67,7 +66,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
#[allow(clippy::similar_names)]
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
if self.ignore_fn && differing_macro_contexts(left.span, right.span) {
if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) {
return false;
}
@ -90,10 +89,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
},
(&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
},
(&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => {
lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
},
(&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r),
(&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => {
@ -108,7 +107,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
},
(&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
(&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
!self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
},
(&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt))
| (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => {
@ -134,7 +133,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
})
},
(&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => {
!self.ignore_fn && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
},
(&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => {
let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body));
@ -186,10 +185,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
}
pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
match (&left, &right) {
(FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) =>
li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp),
}
let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp)
}
/// Checks whether two patterns are the same.
@ -233,8 +230,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
(&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
},
(&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) =>
llang_item == rlang_item,
(&QPath::LangItem(llang_item, _), &QPath::LangItem(rlang_item, _)) => llang_item == rlang_item,
_ => false,
}
}
@ -352,6 +348,11 @@ pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
}
/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
}
/// Type used to hash an ast element. This is different from the `Hash` trait
/// on ast types as this
/// trait would consider IDs and spans.
@ -615,7 +616,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
},
QPath::LangItem(lang_item, ..) => {
lang_item.hash_stable(&mut self.cx.tcx.get_stable_hashing_context(), &mut self.s);
}
},
}
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
}
@ -727,7 +728,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
},
QPath::LangItem(lang_item, ..) => {
lang_item.hash(&mut self.s);
}
},
},
TyKind::OpaqueDef(_, arg_list) => {
self.hash_generic_args(arg_list);

View File

@ -1,7 +1,6 @@
use crate::utils::SpanlessEq;
use crate::utils::{
is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint,
span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty,
span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@ -493,7 +492,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
if let StmtKind::Semi(only_expr) = &stmts[0].kind;
if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind;
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
let mut sle = SpanlessEq::new(cx).ignore_fn();
let mut sle = SpanlessEq::new(cx).deny_side_effects();
then {
match &*ps.ident.as_str() {
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {

View File

@ -21,7 +21,7 @@ pub mod sugg;
pub mod usage;
pub use self::attrs::*;
pub use self::diagnostics::*;
pub use self::hir_utils::{both, over, SpanlessEq, SpanlessHash};
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use std::borrow::Cow;
use std::mem;
@ -42,7 +42,8 @@ use rustc_hir::{
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
use rustc_mir::const_eval;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
@ -574,7 +575,7 @@ pub fn snippet_block<'a, T: LintContext>(
}
/// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicabiliy`.
/// `snippet_with_applicability`.
pub fn snippet_block_with_applicability<'a, T: LintContext>(
cx: &T,
span: Span,
@ -866,6 +867,14 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx>
cx.tcx.erase_late_bound_regions(&ret_ty)
}
/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
})
}
/// Returns `true` if the given type is an `unsafe` function.
pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
match ty.kind {
@ -1304,7 +1313,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
}
}
// check if expr is calling method or function with #[must_use] attribyte
// check if expr is calling method or function with #[must_use] attribute
pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let did = match expr.kind {
ExprKind::Call(ref path, _) => if_chain! {
@ -1409,11 +1418,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
}
}
/// Returns true iff the given expression is a slice of primitives (as defined in the
/// `is_recursively_primitive_type` function).
pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Returns Option<String> where String is a textual representation of the type encapsulated in the
/// slice iff the given expression is a slice of primitives (as defined in the
/// `is_recursively_primitive_type` function) and None otherwise.
pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
match expr_type.kind {
let expr_kind = &expr_type.kind;
let is_primitive = match expr_kind {
ty::Slice(ref element_type)
| ty::Ref(
_,
@ -1424,7 +1435,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
_,
) => is_recursively_primitive_type(element_type),
_ => false,
};
if is_primitive {
// if we have wrappers like Array, Slice or Tuple, print these
// and get the type enclosed in the slice ref
match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind {
ty::Slice(..) => return Some("slice".into()),
ty::Array(..) => return Some("array".into()),
ty::Tuple(..) => return Some("tuple".into()),
_ => {
// is_recursively_primitive_type() should have taken care
// of the rest and we can rely on the type that is found
let refs_peeled = expr_type.peel_refs();
return Some(refs_peeled.walk().last().unwrap().to_string());
},
}
}
None
}
#[macro_export]

View File

@ -35,6 +35,8 @@ pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"];
pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"];
pub const FILE: [&str; 3] = ["std", "fs", "File"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
@ -84,6 +86,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
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"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];

View File

@ -1,13 +1,20 @@
use crate::consts::constant;
use crate::consts::{constant, Constant};
use crate::rustc_target::abi::LayoutOf;
use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
#[allow(clippy::module_name_repetitions)]
#[derive(Copy, Clone)]
pub struct UselessVec {
pub too_large_for_stack: u64,
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would
/// be possible.
@ -31,7 +38,7 @@ declare_clippy_lint! {
"useless `vec!`"
}
declare_lint_pass!(UselessVec => [USELESS_VEC]);
impl_lint_pass!(UselessVec => [USELESS_VEC]);
impl<'tcx> LateLintPass<'tcx> for UselessVec {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@ -42,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind;
if let Some(vec_args) = higher::vec_macro(cx, addressee);
then {
check_vec_macro(cx, &vec_args, expr.span);
self.check_vec_macro(cx, &vec_args, expr.span);
}
}
@ -60,46 +67,62 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
.ctxt()
.outer_expn_data()
.call_site;
check_vec_macro(cx, &vec_args, span);
self.check_vec_macro(cx, &vec_args, span);
}
}
}
}
fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
let mut applicability = Applicability::MachineApplicable;
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
if constant(cx, cx.typeck_results(), len).is_some() {
format!(
"&[{}; {}]",
snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
snippet_with_applicability(cx, len.span, "len", &mut applicability)
)
} else {
return;
}
},
higher::VecArgs::Vec(args) => {
if let Some(last) = args.iter().last() {
let span = args[0].span.to(last.span);
impl UselessVec {
fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) {
let mut applicability = Applicability::MachineApplicable;
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) {
#[allow(clippy::cast_possible_truncation)]
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
return;
}
format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
} else {
"&[]".into()
}
},
};
format!(
"&[{}; {}]",
snippet_with_applicability(cx, elem.span, "elem", &mut applicability),
snippet_with_applicability(cx, len.span, "len", &mut applicability)
)
} else {
return;
}
},
higher::VecArgs::Vec(args) => {
if let Some(last) = args.iter().last() {
#[allow(clippy::cast_possible_truncation)]
if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
return;
}
let span = args[0].span.to(last.span);
span_lint_and_sugg(
cx,
USELESS_VEC,
span,
"useless use of `vec!`",
"you can use a slice directly",
snippet,
applicability,
);
format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability))
} else {
"&[]".into()
}
},
};
span_lint_and_sugg(
cx,
USELESS_VEC,
span,
"useless use of `vec!`",
"you can use a slice directly",
snippet,
applicability,
);
}
}
fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 {
let ty = cx.typeck_results().expr_ty_adjusted(expr);
cx.layout_of(ty).map_or(0, |l| l.size.bytes())
}
/// Returns the item type of the vector (i.e., the `T` in `Vec<T>`).

View File

@ -195,13 +195,10 @@ impl WildcardImports {
}
}
// Allow "...prelude::*" imports.
// Allow "...prelude::..::*" imports.
// Many crates have a prelude, and it is imported as a glob by design.
fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
segments
.iter()
.last()
.map_or(false, |ps| ps.ident.as_str() == "prelude")
segments.iter().any(|ps| ps.ident.as_str() == "prelude")
}
// Allow "super::*" imports in tests.

View File

@ -237,7 +237,7 @@ impl EarlyLintPass for Write {
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
if mac.path == sym!(println) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if fmt_str.symbol == Symbol::intern("") {
span_lint_and_sugg(
cx,
@ -252,7 +252,7 @@ impl EarlyLintPass for Write {
}
} else if mac.path == sym!(print) {
span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), false) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
if check_newlines(&fmt_str) {
span_lint_and_then(
cx,
@ -273,7 +273,7 @@ impl EarlyLintPass for Write {
}
}
} else if mac.path == sym!(write) {
if let (Some(fmt_str), _) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) {
if check_newlines(&fmt_str) {
span_lint_and_then(
cx,
@ -294,16 +294,17 @@ impl EarlyLintPass for Write {
}
}
} else if mac.path == sym!(writeln) {
if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) {
if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
if fmt_str.symbol == Symbol::intern("") {
let mut applicability = Applicability::MachineApplicable;
let suggestion = expr.map_or_else(
|| {
applicability = Applicability::HasPlaceholders;
Cow::Borrowed("v")
},
|e| snippet_with_applicability(cx, e.span, "v", &mut Applicability::MachineApplicable),
);
// FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed
#[allow(clippy::option_if_let_else)]
let suggestion = if let Some(e) = expr {
snippet_with_applicability(cx, e.span, "v", &mut applicability)
} else {
applicability = Applicability::HasPlaceholders;
Cow::Borrowed("v")
};
span_lint_and_sugg(
cx,
@ -364,17 +365,11 @@ impl Write {
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(
&self,
cx: &EarlyContext<'a>,
tts: &TokenStream,
is_write: bool,
) -> (Option<StrLit>, Option<Expr>) {
fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
use rustc_parse_format::{
AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, ParseMode, Parser,
Piece,
};
let tts = tts.clone();
let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
let mut expr: Option<Expr> = None;

View File

@ -295,8 +295,14 @@ impl EarlyLintPass for FooFunctions {
Running our UI test should now produce output that contains the lint message.
According to [the rustc-dev-guide], the text should be matter of fact and avoid
capitalization and periods, unless multiple sentences are needed.
When code or an identifier must appear in a message or label, it should be
surrounded with single acute accents \`.
[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/diagnostics.rs
[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
## Adding the lint logic

View File

@ -661,6 +661,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "misc",
},
Lint {
name: "float_equality_without_abs",
group: "correctness",
desc: "float equality check without `.abs()`",
deprecation: None,
module: "float_equality_without_abs",
},
Lint {
name: "fn_address_comparisons",
group: "correctness",
@ -1037,7 +1044,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
group: "style",
desc: "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block",
deprecation: None,
module: "let_and_return",
module: "returns",
},
Lint {
name: "let_underscore_lock",
@ -1956,6 +1963,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "methods",
},
Lint {
name: "self_assignment",
group: "correctness",
desc: "explicit self-assignment",
deprecation: None,
module: "self_assignment",
},
Lint {
name: "serde_api_misuse",
group: "correctness",
@ -2012,6 +2026,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "methods",
},
Lint {
name: "single_char_push_str",
group: "style",
desc: "`push_str()` used with a single-character string literal as parameter",
deprecation: None,
module: "methods",
},
Lint {
name: "single_component_path_imports",
group: "style",
@ -2166,6 +2187,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "to_digit_is_some",
},
Lint {
name: "to_string_in_display",
group: "correctness",
desc: "`to_string` method used while implementing `Display` trait",
deprecation: None,
module: "to_string_in_display",
},
Lint {
name: "todo",
group: "restriction",
@ -2369,6 +2397,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "methods",
},
Lint {
name: "unnecessary_lazy_evaluations",
group: "style",
desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation",
deprecation: None,
module: "methods",
},
Lint {
name: "unnecessary_mut_passed",
group: "style",
@ -2479,7 +2514,14 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
group: "style",
desc: "needless unit expression",
deprecation: None,
module: "returns",
module: "unused_unit",
},
Lint {
name: "unwrap_in_result",
group: "restriction",
desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`",
deprecation: None,
module: "unwrap_in_result",
},
Lint {
name: "unwrap_used",
@ -2498,7 +2540,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
Lint {
name: "use_self",
group: "nursery",
desc: "Unnecessary structure name repetition whereas `Self` is applicable",
desc: "unnecessary structure name repetition whereas `Self` is applicable",
deprecation: None,
module: "use_self",
},

View File

@ -7,7 +7,7 @@ fn fmt() {
return;
}
// Skip this test if rustup nightly is unavailable
// Skip this test if nightly rustfmt is unavailable
let rustup_output = Command::new("rustup")
.args(&["component", "list", "--toolchain", "nightly"])
.output()
@ -19,12 +19,9 @@ fn fmt() {
}
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let dev_dir = root_dir.join("clippy_dev");
let target_dir = root_dir.join("target");
let target_dir = target_dir.to_str().unwrap();
let output = Command::new("cargo")
.current_dir(dev_dir)
.args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"])
.current_dir(root_dir)
.args(&["dev", "fmt", "--check"])
.output()
.unwrap();

View File

@ -19,3 +19,9 @@ mod extern_exports {
A,
}
}
pub mod prelude {
pub mod v1 {
pub struct PreludeModAnywhere;
}
}

View File

@ -2,7 +2,7 @@
#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)]
use std::borrow::Cow;
use std::cell::Cell;
use std::cell::{Cell, UnsafeCell};
use std::fmt::Display;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Once;
@ -30,6 +30,37 @@ impl Trait<u32> for u64 {
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
}
// This is just a pointer that can be safely dereferended,
// it's semantically the same as `&'static T`;
// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
// For more information, please see the issue #5918.
pub struct StaticRef<T> {
ptr: *const T,
}
impl<T> StaticRef<T> {
/// Create a new `StaticRef` from a raw pointer
///
/// ## Safety
///
/// Callers must pass in a reference to statically allocated memory which
/// does not overlap with other values.
pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
StaticRef { ptr }
}
}
impl<T> std::ops::Deref for StaticRef<T> {
type Target = T;
fn deref(&self) -> &'static T {
unsafe { &*self.ptr }
}
}
// use a tuple to make sure referencing a field behind a pointer isn't linted.
const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
fn main() {
ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
@ -82,4 +113,6 @@ fn main() {
assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
let _ = &CELL_REF.0;
}

View File

@ -1,5 +1,5 @@
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:34:5
--> $DIR/borrow_interior_mutable_const.rs:65:5
|
LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^
@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:35:16
--> $DIR/borrow_interior_mutable_const.rs:66:16
|
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability
| ^^^^^^
@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:38:22
--> $DIR/borrow_interior_mutable_const.rs:69:22
|
LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:39:25
--> $DIR/borrow_interior_mutable_const.rs:70:25
|
LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:40:27
--> $DIR/borrow_interior_mutable_const.rs:71:27
|
LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:41:26
--> $DIR/borrow_interior_mutable_const.rs:72:26
|
LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
| ^^^^^^^^^
@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:52:14
--> $DIR/borrow_interior_mutable_const.rs:83:14
|
LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:53:14
--> $DIR/borrow_interior_mutable_const.rs:84:14
|
LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:54:19
--> $DIR/borrow_interior_mutable_const.rs:85:19
|
LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:55:14
--> $DIR/borrow_interior_mutable_const.rs:86:14
|
LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:56:13
--> $DIR/borrow_interior_mutable_const.rs:87:13
|
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:62:13
--> $DIR/borrow_interior_mutable_const.rs:93:13
|
LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
| ^^^^^^^^^^^^
@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:67:5
--> $DIR/borrow_interior_mutable_const.rs:98:5
|
LL | CELL.set(2); //~ ERROR interior mutability
| ^^^^
@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:68:16
--> $DIR/borrow_interior_mutable_const.rs:99:16
|
LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
| ^^^^
@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:81:5
--> $DIR/borrow_interior_mutable_const.rs:112:5
|
LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
| ^^^^^^^^^^^
@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability
= help: assign this const to a local or static variable, and use the variable here
error: a `const` item with interior mutability should not be borrowed
--> $DIR/borrow_interior_mutable_const.rs:82:16
--> $DIR/borrow_interior_mutable_const.rs:113:16
|
LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability
| ^^^^^^^^^^^

View File

@ -0,0 +1,13 @@
#![warn(clippy::repeat_once)]
trait Repeat {
fn repeat(&self) {}
}
impl Repeat for usize {
fn repeat(&self) {}
}
fn main() {
let _ = 42.repeat();
}

View File

@ -1,4 +1,4 @@
error: Calling `subsec_millis()` is more concise than this calculation
error: calling `subsec_millis()` is more concise than this calculation
--> $DIR/duration_subsec.rs:10:24
|
LL | let bad_millis_1 = dur.subsec_micros() / 1_000;
@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000;
|
= note: `-D clippy::duration-subsec` implied by `-D warnings`
error: Calling `subsec_millis()` is more concise than this calculation
error: calling `subsec_millis()` is more concise than this calculation
--> $DIR/duration_subsec.rs:11:24
|
LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()`
error: Calling `subsec_micros()` is more concise than this calculation
error: calling `subsec_micros()` is more concise than this calculation
--> $DIR/duration_subsec.rs:16:22
|
LL | let bad_micros = dur.subsec_nanos() / 1_000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()`
error: Calling `subsec_micros()` is more concise than this calculation
error: calling `subsec_micros()` is more concise than this calculation
--> $DIR/duration_subsec.rs:21:13
|
LL | let _ = (&dur).subsec_nanos() / 1_000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()`
error: Calling `subsec_micros()` is more concise than this calculation
error: calling `subsec_micros()` is more concise than this calculation
--> $DIR/duration_subsec.rs:25:13
|
LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO;

View File

@ -1,4 +1,4 @@
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:8:5
|
LL | X = 0x1_0000_0000,
@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000,
|
= note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings`
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:15:5
|
LL | X = 0x1_0000_0000,
| ^^^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:18:5
|
LL | A = 0xFFFF_FFFF,
| ^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:25:5
|
LL | Z = 0xFFFF_FFFF,
| ^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:26:5
|
LL | A = 0x1_0000_0000,
| ^^^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:28:5
|
LL | C = (i32::MIN as isize) - 1,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:34:5
|
LL | Z = 0xFFFF_FFFF,
| ^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:35:5
|
LL | A = 0x1_0000_0000,
| ^^^^^^^^^^^^^^^^^
error: Clike enum variant discriminant is not portable to 32-bit targets
error: C-like enum variant discriminant is not portable to 32-bit targets
--> $DIR/enum_clike_unportable_variant.rs:40:5
|
LL | X = <usize as Trait>::Number,

View File

@ -1,4 +1,4 @@
error: Variant name ends with the enum's name
error: variant name ends with the enum's name
--> $DIR/enum_variants.rs:16:5
|
LL | cFoo,
@ -6,25 +6,25 @@ LL | cFoo,
|
= note: `-D clippy::enum-variant-names` implied by `-D warnings`
error: Variant name starts with the enum's name
error: variant name starts with the enum's name
--> $DIR/enum_variants.rs:27:5
|
LL | FoodGood,
| ^^^^^^^^
error: Variant name starts with the enum's name
error: variant name starts with the enum's name
--> $DIR/enum_variants.rs:28:5
|
LL | FoodMiddle,
| ^^^^^^^^^^
error: Variant name starts with the enum's name
error: variant name starts with the enum's name
--> $DIR/enum_variants.rs:29:5
|
LL | FoodBad,
| ^^^^^^^
error: All variants have the same prefix: `Food`
error: all variants have the same prefix: `Food`
--> $DIR/enum_variants.rs:26:1
|
LL | / enum Food {
@ -36,7 +36,7 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: All variants have the same prefix: `CallType`
error: all variants have the same prefix: `CallType`
--> $DIR/enum_variants.rs:36:1
|
LL | / enum BadCallType {
@ -48,7 +48,7 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: All variants have the same prefix: `Constant`
error: all variants have the same prefix: `Constant`
--> $DIR/enum_variants.rs:48:1
|
LL | / enum Consts {
@ -60,7 +60,7 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: All variants have the same prefix: `With`
error: all variants have the same prefix: `With`
--> $DIR/enum_variants.rs:82:1
|
LL | / enum Seallll {
@ -72,7 +72,7 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: All variants have the same prefix: `Prefix`
error: all variants have the same prefix: `Prefix`
--> $DIR/enum_variants.rs:88:1
|
LL | / enum NonCaps {
@ -84,7 +84,7 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: All variants have the same prefix: `With`
error: all variants have the same prefix: `With`
--> $DIR/enum_variants.rs:94:1
|
LL | / pub enum PubSeall {

View File

@ -0,0 +1,31 @@
#![warn(clippy::float_equality_without_abs)]
pub fn is_roughly_equal(a: f32, b: f32) -> bool {
(a - b) < f32::EPSILON
}
pub fn main() {
// all errors
is_roughly_equal(1.0, 2.0);
let a = 0.05;
let b = 0.0500001;
let _ = (a - b) < f32::EPSILON;
let _ = a - b < f32::EPSILON;
let _ = a - b.abs() < f32::EPSILON;
let _ = (a as f64 - b as f64) < f64::EPSILON;
let _ = 1.0 - 2.0 < f32::EPSILON;
let _ = f32::EPSILON > (a - b);
let _ = f32::EPSILON > a - b;
let _ = f32::EPSILON > a - b.abs();
let _ = f64::EPSILON > (a as f64 - b as f64);
let _ = f32::EPSILON > 1.0 - 2.0;
// those are correct
let _ = (a - b).abs() < f32::EPSILON;
let _ = (a as f64 - b as f64).abs() < f64::EPSILON;
let _ = f32::EPSILON > (a - b).abs();
let _ = f64::EPSILON > (a as f64 - b as f64).abs();
}

View File

@ -0,0 +1,92 @@
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:4:5
|
LL | (a - b) < f32::EPSILON
| -------^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(a - b).abs()`
|
= note: `-D clippy::float-equality-without-abs` implied by `-D warnings`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:13:13
|
LL | let _ = (a - b) < f32::EPSILON;
| -------^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(a - b).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:14:13
|
LL | let _ = a - b < f32::EPSILON;
| -----^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(a - b).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:15:13
|
LL | let _ = a - b.abs() < f32::EPSILON;
| -----------^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(a - b.abs()).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:16:13
|
LL | let _ = (a as f64 - b as f64) < f64::EPSILON;
| ---------------------^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(a as f64 - b as f64).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:17:13
|
LL | let _ = 1.0 - 2.0 < f32::EPSILON;
| ---------^^^^^^^^^^^^^^^
| |
| help: add `.abs()`: `(1.0 - 2.0).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:19:13
|
LL | let _ = f32::EPSILON > (a - b);
| ^^^^^^^^^^^^^^^-------
| |
| help: add `.abs()`: `(a - b).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:20:13
|
LL | let _ = f32::EPSILON > a - b;
| ^^^^^^^^^^^^^^^-----
| |
| help: add `.abs()`: `(a - b).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:21:13
|
LL | let _ = f32::EPSILON > a - b.abs();
| ^^^^^^^^^^^^^^^-----------
| |
| help: add `.abs()`: `(a - b.abs()).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:22:13
|
LL | let _ = f64::EPSILON > (a as f64 - b as f64);
| ^^^^^^^^^^^^^^^---------------------
| |
| help: add `.abs()`: `(a as f64 - b as f64).abs()`
error: float equality check without `.abs()`
--> $DIR/float_equality_without_abs.rs:23:13
|
LL | let _ = f32::EPSILON > 1.0 - 2.0;
| ^^^^^^^^^^^^^^^---------
| |
| help: add `.abs()`: `(1.0 - 2.0).abs()`
error: aborting due to 11 previous errors

View File

@ -1,22 +1,22 @@
error: Matching on `Some` with `ok()` is redundant
error: matching on `Some` with `ok()` is redundant
--> $DIR/if_let_some_result.rs:6:5
|
LL | if let Some(y) = x.parse().ok() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::if-let-some-result` implied by `-D warnings`
help: Consider matching on `Ok(y)` and removing the call to `ok` instead
help: consider matching on `Ok(y)` and removing the call to `ok` instead
|
LL | if let Ok(y) = x.parse() {
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: Matching on `Some` with `ok()` is redundant
error: matching on `Some` with `ok()` is redundant
--> $DIR/if_let_some_result.rs:24:9
|
LL | if let Some(y) = x . parse() . ok () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Consider matching on `Ok(y)` and removing the call to `ok` instead
help: consider matching on `Ok(y)` and removing the call to `ok` instead
|
LL | if let Ok(y) = x . parse() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,4 +1,4 @@
error: Unnecessary boolean `not` operation
error: unnecessary boolean `not` operation
--> $DIR/if_not_else.rs:9:5
|
LL | / if !bla() {
@ -11,7 +11,7 @@ LL | | }
= note: `-D clippy::if-not-else` implied by `-D warnings`
= help: remove the `!` and swap the blocks of the `if`/`else`
error: Unnecessary `!=` operation
error: unnecessary `!=` operation
--> $DIR/if_not_else.rs:14:5
|
LL | / if 4 != 5 {

View File

@ -1,4 +1,4 @@
error: Multiple implementations of this structure
error: multiple implementations of this structure
--> $DIR/impl.rs:10:1
|
LL | / impl MyStruct {
@ -7,7 +7,7 @@ LL | | }
| |_^
|
= note: `-D clippy::multiple-inherent-impl` implied by `-D warnings`
note: First implementation here
note: first implementation here
--> $DIR/impl.rs:6:1
|
LL | / impl MyStruct {
@ -15,7 +15,7 @@ LL | | fn first() {}
LL | | }
| |_^
error: Multiple implementations of this structure
error: multiple implementations of this structure
--> $DIR/impl.rs:24:5
|
LL | / impl super::MyStruct {
@ -23,7 +23,7 @@ LL | | fn third() {}
LL | | }
| |_____^
|
note: First implementation here
note: first implementation here
--> $DIR/impl.rs:6:1
|
LL | / impl MyStruct {

View File

@ -1,4 +1,4 @@
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:13:5
|
LL | / if u_8 > 0 {
@ -8,7 +8,7 @@ LL | | }
|
= note: `-D clippy::implicit-saturating-sub` implied by `-D warnings`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:20:13
|
LL | / if u_8 > 0 {
@ -16,7 +16,7 @@ LL | | u_8 -= 1;
LL | | }
| |_____________^ help: try: `u_8 = u_8.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:34:5
|
LL | / if u_16 > 0 {
@ -24,7 +24,7 @@ LL | | u_16 -= 1;
LL | | }
| |_____^ help: try: `u_16 = u_16.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:44:5
|
LL | / if u_32 != 0 {
@ -32,7 +32,7 @@ LL | | u_32 -= 1;
LL | | }
| |_____^ help: try: `u_32 = u_32.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:65:5
|
LL | / if u_64 > 0 {
@ -40,7 +40,7 @@ LL | | u_64 -= 1;
LL | | }
| |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:70:5
|
LL | / if 0 < u_64 {
@ -48,7 +48,7 @@ LL | | u_64 -= 1;
LL | | }
| |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:75:5
|
LL | / if 0 != u_64 {
@ -56,7 +56,7 @@ LL | | u_64 -= 1;
LL | | }
| |_____^ help: try: `u_64 = u_64.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:96:5
|
LL | / if u_usize > 0 {
@ -64,7 +64,7 @@ LL | | u_usize -= 1;
LL | | }
| |_____^ help: try: `u_usize = u_usize.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:108:5
|
LL | / if i_8 > i8::MIN {
@ -72,7 +72,7 @@ LL | | i_8 -= 1;
LL | | }
| |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:113:5
|
LL | / if i_8 > i8::MIN {
@ -80,7 +80,7 @@ LL | | i_8 -= 1;
LL | | }
| |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:118:5
|
LL | / if i_8 != i8::MIN {
@ -88,7 +88,7 @@ LL | | i_8 -= 1;
LL | | }
| |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:123:5
|
LL | / if i_8 != i8::MIN {
@ -96,7 +96,7 @@ LL | | i_8 -= 1;
LL | | }
| |_____^ help: try: `i_8 = i_8.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:133:5
|
LL | / if i_16 > i16::MIN {
@ -104,7 +104,7 @@ LL | | i_16 -= 1;
LL | | }
| |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:138:5
|
LL | / if i_16 > i16::MIN {
@ -112,7 +112,7 @@ LL | | i_16 -= 1;
LL | | }
| |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:143:5
|
LL | / if i_16 != i16::MIN {
@ -120,7 +120,7 @@ LL | | i_16 -= 1;
LL | | }
| |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:148:5
|
LL | / if i_16 != i16::MIN {
@ -128,7 +128,7 @@ LL | | i_16 -= 1;
LL | | }
| |_____^ help: try: `i_16 = i_16.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:158:5
|
LL | / if i_32 > i32::MIN {
@ -136,7 +136,7 @@ LL | | i_32 -= 1;
LL | | }
| |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:163:5
|
LL | / if i_32 > i32::MIN {
@ -144,7 +144,7 @@ LL | | i_32 -= 1;
LL | | }
| |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:168:5
|
LL | / if i_32 != i32::MIN {
@ -152,7 +152,7 @@ LL | | i_32 -= 1;
LL | | }
| |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:173:5
|
LL | / if i_32 != i32::MIN {
@ -160,7 +160,7 @@ LL | | i_32 -= 1;
LL | | }
| |_____^ help: try: `i_32 = i_32.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:183:5
|
LL | / if i64::MIN < i_64 {
@ -168,7 +168,7 @@ LL | | i_64 -= 1;
LL | | }
| |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:188:5
|
LL | / if i64::MIN != i_64 {
@ -176,7 +176,7 @@ LL | | i_64 -= 1;
LL | | }
| |_____^ help: try: `i_64 = i_64.saturating_sub(1);`
error: Implicitly performing saturating subtraction
error: implicitly performing saturating subtraction
--> $DIR/implicit_saturating_sub.rs:193:5
|
LL | / if i64::MIN < i_64 {

View File

@ -1,4 +1,4 @@
error: Unnecessary `>= y + 1` or `x - 1 >=`
error: unnecessary `>= y + 1` or `x - 1 >=`
--> $DIR/int_plus_one.rs:9:13
|
LL | let _ = x >= y + 1;
@ -6,19 +6,19 @@ LL | let _ = x >= y + 1;
|
= note: `-D clippy::int-plus-one` implied by `-D warnings`
error: Unnecessary `>= y + 1` or `x - 1 >=`
error: unnecessary `>= y + 1` or `x - 1 >=`
--> $DIR/int_plus_one.rs:10:13
|
LL | let _ = y + 1 <= x;
| ^^^^^^^^^^ help: change it to: `y < x`
error: Unnecessary `>= y + 1` or `x - 1 >=`
error: unnecessary `>= y + 1` or `x - 1 >=`
--> $DIR/int_plus_one.rs:12:13
|
LL | let _ = x - 1 >= y;
| ^^^^^^^^^^ help: change it to: `x > y`
error: Unnecessary `>= y + 1` or `x - 1 >=`
error: unnecessary `>= y + 1` or `x - 1 >=`
--> $DIR/int_plus_one.rs:13:13
|
LL | let _ = y <= x - 1;

View File

@ -1,4 +1,4 @@
error: Using `.iter().next()` on an array
error: using `.iter().next()` on an array
--> $DIR/iter_next_slice.rs:9:5
|
LL | s.iter().next();
@ -6,19 +6,19 @@ LL | s.iter().next();
|
= note: `-D clippy::iter-next-slice` implied by `-D warnings`
error: Using `.iter().next()` on a Slice without end index.
error: using `.iter().next()` on a Slice without end index
--> $DIR/iter_next_slice.rs:12:5
|
LL | s[2..].iter().next();
| ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)`
error: Using `.iter().next()` on a Slice without end index.
error: using `.iter().next()` on a Slice without end index
--> $DIR/iter_next_slice.rs:15:5
|
LL | v[5..].iter().next();
| ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)`
error: Using `.iter().next()` on an array
error: using `.iter().next()` on an array
--> $DIR/iter_next_slice.rs:18:5
|
LL | v.iter().next();

View File

@ -141,11 +141,3 @@ fn main() {
fn test_slice(b: &[u8]) {
if !b.is_empty() {}
}
mod issue_3807 {
// Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
// See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
fn no_suggestion() {
let _ = (0..42).len() == 0;
}
}

View File

@ -141,11 +141,3 @@ fn main() {
fn test_slice(b: &[u8]) {
if b.len() != 0 {}
}
mod issue_3807 {
// Avoid suggesting changes to ranges if the user did not enable `range_is_empty`.
// See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965
fn no_suggestion() {
let _ = (0..42).len() == 0;
}
}

View File

@ -1,15 +1,17 @@
// run-rustfix
#![feature(range_is_empty)]
#![warn(clippy::len_zero)]
#![allow(unused)]
#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956
// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
mod issue_3807 {
// With the feature enabled, `is_empty` should be suggested
fn suggestion_is_fine() {
fn suggestion_is_fine_range() {
let _ = (0..42).is_empty();
}
fn suggestion_is_fine_range_inclusive() {
let _ = (0_u8..=42).is_empty();
}
}
fn main() {}

View File

@ -1,15 +1,17 @@
// run-rustfix
#![feature(range_is_empty)]
#![warn(clippy::len_zero)]
#![allow(unused)]
#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956
// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this
mod issue_3807 {
// With the feature enabled, `is_empty` should be suggested
fn suggestion_is_fine() {
fn suggestion_is_fine_range() {
let _ = (0..42).len() == 0;
}
fn suggestion_is_fine_range_inclusive() {
let _ = (0_u8..=42).len() == 0;
}
}
fn main() {}

View File

@ -1,10 +1,16 @@
error: length comparison to zero
--> $DIR/len_zero_ranges.rs:11:17
--> $DIR/len_zero_ranges.rs:9:17
|
LL | let _ = (0..42).len() == 0;
| ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()`
|
= note: `-D clippy::len-zero` implied by `-D warnings`
error: aborting due to previous error
error: length comparison to zero
--> $DIR/len_zero_ranges.rs:13:17
|
LL | let _ = (0_u8..=42).len() == 0;
| ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()`
error: aborting due to 2 previous errors

View File

@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows {
}
}
mod issue_5729 {
use std::sync::Arc;
trait Foo {}
trait FooStorage {
fn foo_cloned(&self) -> Arc<dyn Foo>;
}
struct FooStorageImpl<T: Foo> {
foo: Arc<T>,
}
impl<T: Foo + 'static> FooStorage for FooStorageImpl<T> {
fn foo_cloned(&self) -> Arc<dyn Foo> {
let clone = Arc::clone(&self.foo);
clone
}
}
}
fn main() {}

View File

@ -27,5 +27,19 @@ LL |
LL | 5
|
error: aborting due to 2 previous errors
error: returning the result of a `let` binding from a block
--> $DIR/let_and_return.rs:154:13
|
LL | let clone = Arc::clone(&self.foo);
| ---------------------------------- unnecessary `let` binding
LL | clone
| ^^^^^
|
help: return the expression directly
|
LL |
LL | Arc::clone(&self.foo) as _
|
error: aborting due to 3 previous errors

View File

@ -1,40 +1,40 @@
error: You are using an explicit closure for copying elements
error: you are using an explicit closure for copying elements
--> $DIR/map_clone.rs:10:22
|
LL | let _: Vec<i8> = vec![5_i8; 6].iter().map(|x| *x).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()`
|
= note: `-D clippy::map-clone` implied by `-D warnings`
error: You are using an explicit closure for cloning elements
error: you are using an explicit closure for cloning elements
--> $DIR/map_clone.rs:11:26
|
LL | let _: Vec<String> = vec![String::new()].iter().map(|x| x.clone()).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()`
error: You are using an explicit closure for copying elements
error: you are using an explicit closure for copying elements
--> $DIR/map_clone.rs:12:23
|
LL | let _: Vec<u32> = vec![42, 43].iter().map(|&x| x).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()`
error: You are using an explicit closure for copying elements
error: you are using an explicit closure for copying elements
--> $DIR/map_clone.rs:14:26
|
LL | let _: Option<u64> = Some(&16).map(|b| *b);
| ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()`
| ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()`
error: You are using an explicit closure for copying elements
error: you are using an explicit closure for copying elements
--> $DIR/map_clone.rs:15:25
|
LL | let _: Option<u8> = Some(&1).map(|x| x.clone());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()`
error: You are needlessly cloning iterator elements
error: you are needlessly cloning iterator elements
--> $DIR/map_clone.rs:26:29
|
LL | let _ = std::env::args().map(|v| v.clone());
| ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call
| ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call
error: aborting due to 6 previous errors

View File

@ -10,6 +10,7 @@
clippy::non_ascii_literal,
clippy::new_without_default,
clippy::needless_pass_by_value,
clippy::needless_lifetimes,
clippy::print_stdout,
clippy::must_use_candidate,
clippy::use_self,
@ -33,71 +34,6 @@ use std::sync::{self, Arc};
use option_helpers::IteratorFalsePositives;
pub struct T;
impl T {
pub fn add(self, other: T) -> T {
self
}
// no error, not public interface
pub(crate) fn drop(&mut self) {}
// no error, private function
fn neg(self) -> Self {
self
}
// no error, private function
fn eq(&self, other: T) -> bool {
true
}
// No error; self is a ref.
fn sub(&self, other: T) -> &T {
self
}
// No error; different number of arguments.
fn div(self) -> T {
self
}
// No error; wrong return type.
fn rem(self, other: T) {}
// Fine
fn into_u32(self) -> u32 {
0
}
fn into_u16(&self) -> u16 {
0
}
fn to_something(self) -> u32 {
0
}
fn new(self) -> Self {
unimplemented!();
}
}
pub struct T1;
impl T1 {
// Shouldn't trigger lint as it is unsafe.
pub unsafe fn add(self, rhs: T1) -> T1 {
self
}
// Should not trigger lint since this is an async function.
pub async fn next(&mut self) -> Option<T1> {
None
}
}
struct Lt<'a> {
foo: &'a u32,
}
@ -171,6 +107,8 @@ impl BadNew {
}
}
struct T;
impl Mul<T> for T {
type Output = T;
// No error, obviously.

View File

@ -1,15 +1,5 @@
error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name
--> $DIR/methods.rs:39:5
|
LL | / pub fn add(self, other: T) -> T {
LL | | self
LL | | }
| |_____^
|
= note: `-D clippy::should-implement-trait` implied by `-D warnings`
error: methods called `new` usually return `Self`
--> $DIR/methods.rs:169:5
--> $DIR/methods.rs:105:5
|
LL | / fn new() -> i32 {
LL | | 0
@ -19,7 +9,7 @@ LL | | }
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
--> $DIR/methods.rs:188:13
--> $DIR/methods.rs:126:13
|
LL | let _ = v.iter().filter(|&x| *x < 0).next();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -28,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next();
= note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)`
error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead.
--> $DIR/methods.rs:191:13
--> $DIR/methods.rs:129:13
|
LL | let _ = v.iter().filter(|&x| {
| _____________^
@ -38,7 +28,7 @@ LL | | ).next();
| |___________________________^
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:208:22
--> $DIR/methods.rs:146:22
|
LL | let _ = v.iter().find(|&x| *x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)`
@ -46,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some();
= note: `-D clippy::search-is-some` implied by `-D warnings`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:209:20
--> $DIR/methods.rs:147:20
|
LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:210:20
--> $DIR/methods.rs:148:20
|
LL | let _ = (0..1).find(|x| *x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:211:22
--> $DIR/methods.rs:149:22
|
LL | let _ = v.iter().find(|x| **x == 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)`
error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:214:13
--> $DIR/methods.rs:152:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
@ -74,13 +64,13 @@ LL | | ).is_some();
| |______________________________^
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:220:22
--> $DIR/methods.rs:158:22
|
LL | let _ = v.iter().position(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:223:13
--> $DIR/methods.rs:161:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
@ -90,13 +80,13 @@ LL | | ).is_some();
| |______________________________^
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:229:22
--> $DIR/methods.rs:167:22
|
LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)`
error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`.
--> $DIR/methods.rs:232:13
--> $DIR/methods.rs:170:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
@ -105,5 +95,5 @@ LL | | }
LL | | ).is_some();
| |______________________________^
error: aborting due to 13 previous errors
error: aborting due to 12 previous errors

View File

@ -1,4 +1,4 @@
error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
error: the function `takes_an_immutable_reference` doesn't need a mutable reference
--> $DIR/mut_reference.rs:17:34
|
LL | takes_an_immutable_reference(&mut 42);
@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42);
|
= note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
error: The function/method `as_ptr` doesn't need a mutable reference
error: the function `as_ptr` doesn't need a mutable reference
--> $DIR/mut_reference.rs:19:12
|
LL | as_ptr(&mut 42);
| ^^^^^^^
error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference
error: the method `takes_an_immutable_reference` doesn't need a mutable reference
--> $DIR/mut_reference.rs:23:44
|
LL | my_struct.takes_an_immutable_reference(&mut 42);

View File

@ -1,4 +1,4 @@
error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:6:5
|
LL | Mutex::new(true);
@ -6,31 +6,31 @@ LL | Mutex::new(true);
|
= note: `-D clippy::mutex-atomic` implied by `-D warnings`
error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:7:5
|
LL | Mutex::new(5usize);
| ^^^^^^^^^^^^^^^^^^
error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:8:5
|
LL | Mutex::new(9isize);
| ^^^^^^^^^^^^^^^^^^
error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:10:5
|
LL | Mutex::new(&x as *const u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:11:5
|
LL | Mutex::new(&mut x as *mut u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:12:5
|
LL | Mutex::new(0u32);
@ -38,7 +38,7 @@ LL | Mutex::new(0u32);
|
= note: `-D clippy::mutex-integer` implied by `-D warnings`
error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`.
error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>`
--> $DIR/mutex_atomic.rs:13:5
|
LL | Mutex::new(0i32);

View File

@ -9,8 +9,14 @@
/// }
/// ```
///
/// This should, too.
/// With an explicit return type it should lint too
/// ```
/// fn main() -> () {
/// unimplemented!();
/// }
/// ```
///
/// This should, too.
/// ```rust
/// fn main() {
/// unimplemented!();
@ -18,7 +24,6 @@
/// ```
///
/// This one too.
///
/// ```no_run
/// fn main() {
/// unimplemented!();
@ -33,6 +38,20 @@ fn bad_doctests() {}
/// fn main(){}
/// ```
///
/// This shouldn't lint either, because main is async:
/// ```
/// async fn main() {
/// assert_eq!(42, ANSWER);
/// }
/// ```
///
/// Same here, because the return type is not the unit type:
/// ```
/// fn main() -> Result<()> {
/// Ok(())
/// }
/// ```
///
/// This shouldn't lint either, because there's a `static`:
/// ```
/// static ANSWER: i32 = 42;
@ -42,6 +61,15 @@ fn bad_doctests() {}
/// }
/// ```
///
/// This shouldn't lint either, because there's a `const`:
/// ```
/// fn main() {
/// assert_eq!(42, ANSWER);
/// }
///
/// const ANSWER: i32 = 42;
/// ```
///
/// Neither should this lint because of `extern crate`:
/// ```
/// #![feature(test)]
@ -51,8 +79,41 @@ fn bad_doctests() {}
/// }
/// ```
///
/// We should not lint ignored examples:
/// Neither should this lint because it has an extern block:
/// ```
/// extern {}
/// fn main() {
/// unimplemented!();
/// }
/// ```
///
/// This should not lint because there is another function defined:
/// ```
/// fn fun() {}
///
/// fn main() {
/// unimplemented!();
/// }
/// ```
///
/// We should not lint inside raw strings ...
/// ```
/// let string = r#"
/// fn main() {
/// unimplemented!();
/// }
/// "#;
/// ```
///
/// ... or comments
/// ```
/// // fn main() {
/// // let _inception = 42;
/// // }
/// let _inception = 42;
/// ```
///
/// We should not lint ignored examples:
/// ```rust,ignore
/// fn main() {
/// unimplemented!();
@ -60,7 +121,6 @@ fn bad_doctests() {}
/// ```
///
/// Or even non-rust examples:
///
/// ```text
/// fn main() {
/// is what starts the program

View File

@ -7,16 +7,22 @@ LL | /// fn main() {
= note: `-D clippy::needless-doctest-main` implied by `-D warnings`
error: needless `fn main` in doctest
--> $DIR/needless_doc_main.rs:15:4
--> $DIR/needless_doc_main.rs:14:4
|
LL | /// fn main() -> () {
| ^^^^^^^^^^^^^^^^^^
error: needless `fn main` in doctest
--> $DIR/needless_doc_main.rs:21:4
|
LL | /// fn main() {
| ^^^^^^^^^^^^
error: needless `fn main` in doctest
--> $DIR/needless_doc_main.rs:23:4
--> $DIR/needless_doc_main.rs:28:4
|
LL | /// fn main() {
| ^^^^^^^^^^^^
error: aborting due to 3 previous errors
error: aborting due to 4 previous errors

View File

@ -69,6 +69,23 @@ fn test_void_match(x: u32) {
}
}
fn read_line() -> String {
use std::io::BufRead;
let stdin = ::std::io::stdin();
return stdin.lock().lines().next().unwrap().unwrap();
}
fn borrows_but_not_last(value: bool) -> String {
if value {
use std::io::BufRead;
let stdin = ::std::io::stdin();
let _a = stdin.lock().lines().next().unwrap().unwrap();
String::from("test")
} else {
String::new()
}
}
fn main() {
let _ = test_end_of_fn();
let _ = test_no_semicolon();

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