Merge commit '3d0b0e66afdfaa519d8855b338b35b4605775945' into clippyup
This commit is contained in:
parent
48b4aeabf8
commit
282c59820b
|
@ -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"]
|
||||
|
|
|
@ -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: |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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`!");
|
||||
},
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!(
|
||||
"{}.{}()",
|
||||
|
|
|
@ -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",
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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`",
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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:
|
|||
®ex::INVALID_REGEX,
|
||||
®ex::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(®ex::INVALID_REGEX),
|
||||
LintId::of(®ex::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(®ex::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(®ex::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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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("__") &&
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)`,
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
|
@ -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)");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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!("]);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]) => {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"];
|
||||
|
|
|
@ -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>`).
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -19,3 +19,9 @@ mod extern_exports {
|
|||
A,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub mod v1 {
|
||||
pub struct PreludeModAnywhere;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
| ^^^^^^^^^^^
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue