mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-19 23:48:44 +00:00
Merge remote-tracking branch 'upstream/master' into sync-from-rust
This commit is contained in:
commit
c2799c861d
110 changed files with 3014 additions and 926 deletions
|
@ -1690,6 +1690,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 +1700,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 +1725,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 +1755,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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,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
|
||||
}
|
||||
}
|
|
@ -218,7 +218,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 +284,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 +296,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,6 +311,7 @@ mod unnested_or_patterns;
|
|||
mod unsafe_removed_from_name;
|
||||
mod unused_io_amount;
|
||||
mod unused_self;
|
||||
mod unused_unit;
|
||||
mod unwrap;
|
||||
mod use_self;
|
||||
mod useless_conversion;
|
||||
|
@ -586,7 +588,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 +678,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 +686,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 +772,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 +792,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,6 +845,7 @@ 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,
|
||||
&use_self::USE_SELF,
|
||||
|
@ -930,11 +936,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 +1023,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 +1032,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 +1092,7 @@ 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 self_assignment::SelfAssignment);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
|
||||
|
@ -1284,7 +1292,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 +1356,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 +1364,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 +1422,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 +1437,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 +1471,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 +1512,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 +1543,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 +1567,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 +1577,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),
|
||||
|
@ -1704,10 +1718,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;
|
||||
|
@ -799,7 +800,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 +1307,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 +1387,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 +1415,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 +1436,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 +1492,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 +1512,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 +1547,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 +1573,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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2280,7 +2373,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 +2392,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 +2711,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 +2729,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 +2762,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 +3222,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 +3243,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"
|
||||
|
@ -3403,38 +3532,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]
|
||||
|
|
111
clippy_lints/src/methods/unnecessary_lazy_eval.rs
Normal file
111
clippy_lints/src/methods/unnecessary_lazy_eval.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::UNNECESSARY_LAZY_EVALUATIONS;
|
||||
|
||||
// Return true if the expression is an accessor of any of the arguments
|
||||
fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool {
|
||||
params.iter().any(|arg| {
|
||||
if_chain! {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind;
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind;
|
||||
if let [p, ..] = path.segments;
|
||||
then {
|
||||
ident.name == p.ident.name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool {
|
||||
paths.iter().any(|candidate| match_qpath(path, candidate))
|
||||
}
|
||||
|
||||
fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool {
|
||||
match expr.kind {
|
||||
// Closures returning literals can be unconditionally simplified
|
||||
hir::ExprKind::Lit(_) => true,
|
||||
|
||||
hir::ExprKind::Index(ref object, ref index) => {
|
||||
// arguments are not being indexed into
|
||||
if expr_uses_argument(object, params) {
|
||||
false
|
||||
} else {
|
||||
// arguments are not used as index
|
||||
!expr_uses_argument(index, params)
|
||||
}
|
||||
},
|
||||
|
||||
// Reading fields can be simplified if the object is not an argument of the closure
|
||||
hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params),
|
||||
|
||||
// Paths can be simplified if the root is not the argument, this also covers None
|
||||
hir::ExprKind::Path(_) => !expr_uses_argument(expr, params),
|
||||
|
||||
// Calls to Some, Ok, Err can be considered literals if they don't derive an argument
|
||||
hir::ExprKind::Call(ref func, ref args) => if_chain! {
|
||||
if variant_calls; // Disable lint when rules conflict with bind_instead_of_map
|
||||
if let hir::ExprKind::Path(ref path) = func.kind;
|
||||
if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]);
|
||||
then {
|
||||
// Recursively check all arguments
|
||||
args.iter().all(|arg| can_simplify(arg, params, variant_calls))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
|
||||
// For anything more complex than the above, a closure is probably the right solution,
|
||||
// or the case is handled by an other lint
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
|
||||
/// replaced with `<fn>(return value of simple closure)`
|
||||
pub(super) fn lint<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
args: &'tcx [hir::Expr<'_>],
|
||||
allow_variant_calls: bool,
|
||||
simplify_using: &str,
|
||||
) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));
|
||||
|
||||
if is_option || is_result {
|
||||
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
|
||||
let body = cx.tcx.hir().body(eid);
|
||||
let ex = &body.value;
|
||||
let params = &body.params;
|
||||
|
||||
if can_simplify(ex, params, allow_variant_calls) {
|
||||
let msg = if is_option {
|
||||
"unnecessary closure used to substitute value for `Option::None`"
|
||||
} else {
|
||||
"unnecessary closure used to substitute value for `Result::Err`"
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_LAZY_EVALUATIONS,
|
||||
expr.span,
|
||||
msg,
|
||||
&format!("Use `{}` instead", simplify_using),
|
||||
format!(
|
||||
"{0}.{1}({2})",
|
||||
snippet(cx, args[0].span, ".."),
|
||||
simplify_using,
|
||||
snippet(cx, ex.span, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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,223 @@ 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(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");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
51
clippy_lints/src/self_assignment.rs
Normal file
51
clippy_lints/src/self_assignment.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use crate::utils::{eq_expr_value, snippet, span_lint};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit self-assignments.
|
||||
///
|
||||
/// **Why is this bad?** Self-assignments are redundant and unlikely to be
|
||||
/// intentional.
|
||||
///
|
||||
/// **Known problems:** If expression contains any deref coercions or
|
||||
/// indexing operations they are assumed not to have any side effects.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Event {
|
||||
/// id: usize,
|
||||
/// x: i32,
|
||||
/// y: i32,
|
||||
/// }
|
||||
///
|
||||
/// fn copy_position(a: &mut Event, b: &Event) {
|
||||
/// a.x = b.x;
|
||||
/// a.y = a.y;
|
||||
/// }
|
||||
/// ```
|
||||
pub SELF_ASSIGNMENT,
|
||||
correctness,
|
||||
"explicit self-assignment"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
|
||||
if eq_expr_value(cx, lhs, rhs) {
|
||||
let lhs = snippet(cx, lhs.span, "<lhs>");
|
||||
let rhs = snippet(cx, rhs.span, "<rhs>");
|
||||
span_lint(
|
||||
cx,
|
||||
SELF_ASSIGNMENT,
|
||||
expr.span,
|
||||
&format!("self-assignment of `{}` to `{}`", rhs, lhs),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -111,9 +111,9 @@ impl LateLintPass<'_> for StableSortPrimitive {
|
|||
STABLE_SORT_PRIMITIVE,
|
||||
expr.span,
|
||||
format!(
|
||||
"Use {} instead of {}",
|
||||
detection.method.unstable_name(),
|
||||
detection.method.stable_name()
|
||||
"used {} instead of {}",
|
||||
detection.method.stable_name(),
|
||||
detection.method.unstable_name()
|
||||
)
|
||||
.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);
|
||||
|
|
100
clippy_lints/src/to_string_in_display.rs
Normal file
100
clippy_lints/src/to_string_in_display.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, 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,
|
||||
}
|
||||
|
||||
impl ToStringInDisplay {
|
||||
pub fn new() -> Self {
|
||||
Self { in_display_impl: false }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind;
|
||||
if path.ident.name == sym!(to_string);
|
||||
if match_trait_method(cx, expr, &paths::TO_STRING);
|
||||
if self.in_display_impl;
|
||||
|
||||
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,
|
||||
|
|
|
@ -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};
|
||||
|
|
144
clippy_lints/src/unused_unit.rs
Normal file
144
clippy_lints/src/unused_unit.rs
Normal file
|
@ -0,0 +1,144 @@
|
|||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_ast::visit::FnKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use crate::utils::span_lint_and_sugg;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unit (`()`) expressions that can be removed.
|
||||
///
|
||||
/// **Why is this bad?** Such expressions add no value, but can make the code
|
||||
/// less readable. Depending on formatting they can make a `break` or `return`
|
||||
/// statement look like a function call.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn return_unit() -> () {
|
||||
/// ()
|
||||
/// }
|
||||
/// ```
|
||||
pub UNUSED_UNIT,
|
||||
style,
|
||||
"needless unit expression"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]);
|
||||
|
||||
impl EarlyLintPass for UnusedUnit {
|
||||
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
|
||||
if_chain! {
|
||||
if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
|
||||
if let ast::TyKind::Tup(ref vals) = ty.kind;
|
||||
if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
|
||||
then {
|
||||
lint_unneeded_unit_return(cx, ty, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
|
||||
if_chain! {
|
||||
if let Some(ref stmt) = block.stmts.last();
|
||||
if let ast::StmtKind::Expr(ref expr) = stmt.kind;
|
||||
if is_unit_expr(expr) && !stmt.span.from_expansion();
|
||||
then {
|
||||
let sp = expr.span;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSED_UNIT,
|
||||
sp,
|
||||
"unneeded unit expression",
|
||||
"remove the final `()`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
match e.kind {
|
||||
ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
|
||||
if is_unit_expr(expr) && !expr.span.from_expansion() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSED_UNIT,
|
||||
expr.span,
|
||||
"unneeded `()`",
|
||||
"remove the `()`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) {
|
||||
let segments = &poly.trait_ref.path.segments;
|
||||
|
||||
if_chain! {
|
||||
if segments.len() == 1;
|
||||
if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str());
|
||||
if let Some(args) = &segments[0].args;
|
||||
if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
|
||||
if let ast::FnRetTy::Ty(ty) = &generic_args.output;
|
||||
if ty.kind.is_unit();
|
||||
then {
|
||||
lint_unneeded_unit_return(cx, ty, generic_args.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the def site
|
||||
#[must_use]
|
||||
fn get_def(span: Span) -> Option<Span> {
|
||||
if span.from_expansion() {
|
||||
Some(span.ctxt().outer_expn_data().def_site)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// is this expr a `()` unit?
|
||||
fn is_unit_expr(expr: &ast::Expr) -> bool {
|
||||
if let ast::ExprKind::Tup(ref vals) = expr.kind {
|
||||
vals.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
|
||||
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
|
||||
fn_source
|
||||
.rfind("->")
|
||||
.map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
|
||||
(
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
(ty.span, Applicability::MaybeIncorrect)
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSED_UNIT,
|
||||
ret_span,
|
||||
"unneeded unit return type",
|
||||
"remove the `-> ()`",
|
||||
String::new(),
|
||||
appl,
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -84,6 +84,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>`).
|
||||
|
|
|
@ -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,7 +294,7 @@ 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(
|
||||
|
@ -364,17 +364,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
|
||||
|
||||
|
|
|
@ -1037,7 +1037,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 +1956,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 +2019,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 +2180,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 +2390,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 +2507,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
|
|||
group: "style",
|
||||
desc: "needless unit expression",
|
||||
deprecation: None,
|
||||
module: "returns",
|
||||
module: "unused_unit",
|
||||
},
|
||||
Lint {
|
||||
name: "unwrap_used",
|
||||
|
@ -2498,7 +2526,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",
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
return String::from("test");
|
||||
} else {
|
||||
return String::new();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = test_end_of_fn();
|
||||
let _ = test_no_semicolon();
|
||||
|
|
|
@ -72,5 +72,17 @@ error: unneeded `return` statement
|
|||
LL | _ => return,
|
||||
| ^^^^^^ help: replace `return` with an empty block: `{}`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
error: unneeded `return` statement
|
||||
--> $DIR/needless_return.rs:83:9
|
||||
|
|
||||
LL | return String::from("test");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
|
||||
|
||||
error: unneeded `return` statement
|
||||
--> $DIR/needless_return.rs:85:9
|
||||
|
|
||||
LL | return String::new();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
|
|
|
@ -48,6 +48,14 @@ fn main() {
|
|||
let _ = -1f64.to_degrees();
|
||||
let _ = -1f64.to_radians();
|
||||
|
||||
// Chains containing any non-odd function should trigger (issue #5924)
|
||||
let _ = -(1.0_f64.cos().cos());
|
||||
let _ = -(1.0_f64.cos().sin());
|
||||
let _ = -(1.0_f64.sin().cos());
|
||||
|
||||
// Chains of odd functions shouldn't trigger
|
||||
let _ = -1f64.sin().sin();
|
||||
|
||||
let b = 3;
|
||||
trip!(b * 8);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,14 @@ fn main() {
|
|||
let _ = -1f64.to_degrees();
|
||||
let _ = -1f64.to_radians();
|
||||
|
||||
// Chains containing any non-odd function should trigger (issue #5924)
|
||||
let _ = -1.0_f64.cos().cos();
|
||||
let _ = -1.0_f64.cos().sin();
|
||||
let _ = -1.0_f64.sin().cos();
|
||||
|
||||
// Chains of odd functions shouldn't trigger
|
||||
let _ = -1f64.sin().sin();
|
||||
|
||||
let b = 3;
|
||||
trip!(b * 8);
|
||||
}
|
||||
|
|
|
@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call
|
|||
LL | -1f32.abs();
|
||||
| ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
error: unary minus has lower precedence than method call
|
||||
--> $DIR/precedence.rs:52:13
|
||||
|
|
||||
LL | let _ = -1.0_f64.cos().cos();
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())`
|
||||
|
||||
error: unary minus has lower precedence than method call
|
||||
--> $DIR/precedence.rs:53:13
|
||||
|
|
||||
LL | let _ = -1.0_f64.cos().sin();
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())`
|
||||
|
||||
error: unary minus has lower precedence than method call
|
||||
--> $DIR/precedence.rs:54:13
|
||||
|
|
||||
LL | let _ = -1.0_f64.sin().cos();
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn test5(a: Rc<bool>) {}
|
|||
|
||||
// Rc<Box<T>>
|
||||
|
||||
pub fn test6(a: Box<bool>) {}
|
||||
pub fn test6(a: Rc<bool>) {}
|
||||
|
||||
// Box<&T>
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ error: usage of `Rc<Box<T>>`
|
|||
--> $DIR/redundant_allocation.rs:36:17
|
||||
|
|
||||
LL | pub fn test6(a: Rc<Box<bool>>) {}
|
||||
| ^^^^^^^^^^^^^ help: try: `Box<bool>`
|
||||
| ^^^^^^^^^^^^^ help: try: `Rc<bool>`
|
||||
|
||||
error: usage of `Box<&T>`
|
||||
--> $DIR/redundant_allocation.rs:40:22
|
||||
|
|
|
@ -86,4 +86,12 @@ fn main() {
|
|||
for a in vec_a {
|
||||
vec12.push(2u8.pow(a.kind));
|
||||
}
|
||||
|
||||
// Fix #5902
|
||||
let mut vec13: Vec<u8> = Vec::new();
|
||||
let mut item = 0;
|
||||
for _ in 0..10 {
|
||||
vec13.push(item);
|
||||
item += 10;
|
||||
}
|
||||
}
|
||||
|
|
67
tests/ui/self_assignment.rs
Normal file
67
tests/ui/self_assignment.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
#![warn(clippy::self_assignment)]
|
||||
|
||||
pub struct S<'a> {
|
||||
a: i32,
|
||||
b: [i32; 10],
|
||||
c: Vec<Vec<i32>>,
|
||||
e: &'a mut i32,
|
||||
f: &'a mut i32,
|
||||
}
|
||||
|
||||
pub fn positives(mut a: usize, b: &mut u32, mut s: S) {
|
||||
a = a;
|
||||
*b = *b;
|
||||
s = s;
|
||||
s.a = s.a;
|
||||
s.b[10] = s.b[5 + 5];
|
||||
s.c[0][1] = s.c[0][1];
|
||||
s.b[a] = s.b[a];
|
||||
*s.e = *s.e;
|
||||
s.b[a + 10] = s.b[10 + a];
|
||||
|
||||
let mut t = (0, 1);
|
||||
t.1 = t.1;
|
||||
t.0 = (t.0);
|
||||
}
|
||||
|
||||
pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) {
|
||||
dbg!(&a);
|
||||
a = *b;
|
||||
dbg!(&a);
|
||||
s.b[1] += s.b[1];
|
||||
s.b[1] = s.b[2];
|
||||
s.c[1][0] = s.c[0][1];
|
||||
s.b[a] = s.b[*b];
|
||||
s.b[a + 10] = s.b[a + 11];
|
||||
*s.e = *s.f;
|
||||
|
||||
let mut t = (0, 1);
|
||||
t.0 = t.1;
|
||||
}
|
||||
|
||||
#[allow(clippy::eval_order_dependence)]
|
||||
pub fn negatives_side_effects() {
|
||||
let mut v = vec![1, 2, 3, 4, 5];
|
||||
let mut i = 0;
|
||||
v[{
|
||||
i += 1;
|
||||
i
|
||||
}] = v[{
|
||||
i += 1;
|
||||
i
|
||||
}];
|
||||
|
||||
fn next(n: &mut usize) -> usize {
|
||||
let v = *n;
|
||||
*n += 1;
|
||||
v
|
||||
}
|
||||
|
||||
let mut w = vec![1, 2, 3, 4, 5];
|
||||
let mut i = 0;
|
||||
let i = &mut i;
|
||||
w[next(i)] = w[next(i)];
|
||||
w[next(i)] = w[next(i)];
|
||||
}
|
||||
|
||||
fn main() {}
|
70
tests/ui/self_assignment.stderr
Normal file
70
tests/ui/self_assignment.stderr
Normal file
|
@ -0,0 +1,70 @@
|
|||
error: self-assignment of `a` to `a`
|
||||
--> $DIR/self_assignment.rs:12:5
|
||||
|
|
||||
LL | a = a;
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `-D clippy::self-assignment` implied by `-D warnings`
|
||||
|
||||
error: self-assignment of `*b` to `*b`
|
||||
--> $DIR/self_assignment.rs:13:5
|
||||
|
|
||||
LL | *b = *b;
|
||||
| ^^^^^^^
|
||||
|
||||
error: self-assignment of `s` to `s`
|
||||
--> $DIR/self_assignment.rs:14:5
|
||||
|
|
||||
LL | s = s;
|
||||
| ^^^^^
|
||||
|
||||
error: self-assignment of `s.a` to `s.a`
|
||||
--> $DIR/self_assignment.rs:15:5
|
||||
|
|
||||
LL | s.a = s.a;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: self-assignment of `s.b[5 + 5]` to `s.b[10]`
|
||||
--> $DIR/self_assignment.rs:16:5
|
||||
|
|
||||
LL | s.b[10] = s.b[5 + 5];
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: self-assignment of `s.c[0][1]` to `s.c[0][1]`
|
||||
--> $DIR/self_assignment.rs:17:5
|
||||
|
|
||||
LL | s.c[0][1] = s.c[0][1];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: self-assignment of `s.b[a]` to `s.b[a]`
|
||||
--> $DIR/self_assignment.rs:18:5
|
||||
|
|
||||
LL | s.b[a] = s.b[a];
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: self-assignment of `*s.e` to `*s.e`
|
||||
--> $DIR/self_assignment.rs:19:5
|
||||
|
|
||||
LL | *s.e = *s.e;
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]`
|
||||
--> $DIR/self_assignment.rs:20:5
|
||||
|
|
||||
LL | s.b[a + 10] = s.b[10 + a];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: self-assignment of `t.1` to `t.1`
|
||||
--> $DIR/self_assignment.rs:23:5
|
||||
|
|
||||
LL | t.1 = t.1;
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: self-assignment of `(t.0)` to `t.0`
|
||||
--> $DIR/self_assignment.rs:24:5
|
||||
|
|
||||
LL | t.0 = (t.0);
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
83
tests/ui/should_impl_trait/corner_cases.rs
Normal file
83
tests/ui/should_impl_trait/corner_cases.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
// edition:2018
|
||||
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::must_use_candidate,
|
||||
clippy::unused_self,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::wrong_self_convention
|
||||
)]
|
||||
|
||||
use std::ops::Mul;
|
||||
use std::rc::{self, Rc};
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
fn main() {}
|
||||
|
||||
pub struct T1;
|
||||
impl T1 {
|
||||
// corner cases: should not lint
|
||||
|
||||
// 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: Self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// No error; self is a ref.
|
||||
fn sub(&self, other: Self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
// No error; different number of arguments.
|
||||
fn div(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
// No error; wrong return type.
|
||||
fn rem(self, other: Self) {}
|
||||
|
||||
// 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 fn next<'b>(&'b mut self) -> Option<&'b mut T1> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct T2;
|
||||
impl T2 {
|
||||
// Shouldn't trigger lint as it is unsafe.
|
||||
pub unsafe fn add(self, rhs: Self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
// Should not trigger lint since this is an async function.
|
||||
pub async fn next(&mut self) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
}
|
87
tests/ui/should_impl_trait/method_list_1.rs
Normal file
87
tests/ui/should_impl_trait/method_list_1.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// edition:2018
|
||||
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::must_use_candidate,
|
||||
clippy::unused_self,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::wrong_self_convention
|
||||
)]
|
||||
|
||||
use std::ops::Mul;
|
||||
use std::rc::{self, Rc};
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
fn main() {}
|
||||
pub struct T;
|
||||
|
||||
impl T {
|
||||
// *****************************************
|
||||
// trait method list part 1, should lint all
|
||||
// *****************************************
|
||||
pub fn add(self, other: T) -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn as_mut(&mut self) -> &mut T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> &T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn bitand(self, rhs: T) -> T {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn bitor(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn bitxor(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> &str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&mut self) -> &mut str {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn clone(&self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn cmp(&self, other: &Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn default() -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn deref(&self) -> &Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn deref_mut(&mut self) -> &mut Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn div(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn drop(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
// **********
|
||||
// part 1 end
|
||||
// **********
|
||||
}
|
143
tests/ui/should_impl_trait/method_list_1.stderr
Normal file
143
tests/ui/should_impl_trait/method_list_1.stderr
Normal file
|
@ -0,0 +1,143 @@
|
|||
error: method `add` can be confused for the standard trait method `std::ops::Add::add`
|
||||
--> $DIR/method_list_1.rs:25:5
|
||||
|
|
||||
LL | / pub fn add(self, other: T) -> T {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: `-D clippy::should-implement-trait` implied by `-D warnings`
|
||||
= help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name
|
||||
|
||||
error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut`
|
||||
--> $DIR/method_list_1.rs:29:5
|
||||
|
|
||||
LL | / pub fn as_mut(&mut self) -> &mut T {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name
|
||||
|
||||
error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref`
|
||||
--> $DIR/method_list_1.rs:33:5
|
||||
|
|
||||
LL | / pub fn as_ref(&self) -> &T {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name
|
||||
|
||||
error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand`
|
||||
--> $DIR/method_list_1.rs:37:5
|
||||
|
|
||||
LL | / pub fn bitand(self, rhs: T) -> T {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name
|
||||
|
||||
error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor`
|
||||
--> $DIR/method_list_1.rs:41:5
|
||||
|
|
||||
LL | / pub fn bitor(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name
|
||||
|
||||
error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor`
|
||||
--> $DIR/method_list_1.rs:45:5
|
||||
|
|
||||
LL | / pub fn bitxor(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name
|
||||
|
||||
error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow`
|
||||
--> $DIR/method_list_1.rs:49:5
|
||||
|
|
||||
LL | / pub fn borrow(&self) -> &str {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name
|
||||
|
||||
error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut`
|
||||
--> $DIR/method_list_1.rs:53:5
|
||||
|
|
||||
LL | / pub fn borrow_mut(&mut self) -> &mut str {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name
|
||||
|
||||
error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone`
|
||||
--> $DIR/method_list_1.rs:57:5
|
||||
|
|
||||
LL | / pub fn clone(&self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name
|
||||
|
||||
error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp`
|
||||
--> $DIR/method_list_1.rs:61:5
|
||||
|
|
||||
LL | / pub fn cmp(&self, other: &Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name
|
||||
|
||||
error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref`
|
||||
--> $DIR/method_list_1.rs:69:5
|
||||
|
|
||||
LL | / pub fn deref(&self) -> &Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name
|
||||
|
||||
error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut`
|
||||
--> $DIR/method_list_1.rs:73:5
|
||||
|
|
||||
LL | / pub fn deref_mut(&mut self) -> &mut Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name
|
||||
|
||||
error: method `div` can be confused for the standard trait method `std::ops::Div::div`
|
||||
--> $DIR/method_list_1.rs:77:5
|
||||
|
|
||||
LL | / pub fn div(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name
|
||||
|
||||
error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop`
|
||||
--> $DIR/method_list_1.rs:81:5
|
||||
|
|
||||
LL | / pub fn drop(&mut self) {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
88
tests/ui/should_impl_trait/method_list_2.rs
Normal file
88
tests/ui/should_impl_trait/method_list_2.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
// edition:2018
|
||||
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::missing_errors_doc,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::must_use_candidate,
|
||||
clippy::unused_self,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::missing_safety_doc,
|
||||
clippy::wrong_self_convention
|
||||
)]
|
||||
|
||||
use std::ops::Mul;
|
||||
use std::rc::{self, Rc};
|
||||
use std::sync::{self, Arc};
|
||||
|
||||
fn main() {}
|
||||
pub struct T;
|
||||
|
||||
impl T {
|
||||
// *****************************************
|
||||
// trait method list part 2, should lint all
|
||||
// *****************************************
|
||||
|
||||
pub fn eq(&self, other: &Self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn from_iter<T>(iter: T) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str) -> Result<Self, Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn hash(&self, state: &mut T) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn index(&self, index: usize) -> &Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn index_mut(&mut self, index: usize) -> &mut Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn mul(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn neg(self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn not(self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn rem(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn shl(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn shr(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn sub(self, rhs: Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
// **********
|
||||
// part 2 end
|
||||
// **********
|
||||
}
|
153
tests/ui/should_impl_trait/method_list_2.stderr
Normal file
153
tests/ui/should_impl_trait/method_list_2.stderr
Normal file
|
@ -0,0 +1,153 @@
|
|||
error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq`
|
||||
--> $DIR/method_list_2.rs:26:5
|
||||
|
|
||||
LL | / pub fn eq(&self, other: &Self) -> bool {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= note: `-D clippy::should-implement-trait` implied by `-D warnings`
|
||||
= help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name
|
||||
|
||||
error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter`
|
||||
--> $DIR/method_list_2.rs:30:5
|
||||
|
|
||||
LL | / pub fn from_iter<T>(iter: T) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name
|
||||
|
||||
error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str`
|
||||
--> $DIR/method_list_2.rs:34:5
|
||||
|
|
||||
LL | / pub fn from_str(s: &str) -> Result<Self, Self> {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name
|
||||
|
||||
error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash`
|
||||
--> $DIR/method_list_2.rs:38:5
|
||||
|
|
||||
LL | / pub fn hash(&self, state: &mut T) {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name
|
||||
|
||||
error: method `index` can be confused for the standard trait method `std::ops::Index::index`
|
||||
--> $DIR/method_list_2.rs:42:5
|
||||
|
|
||||
LL | / pub fn index(&self, index: usize) -> &Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name
|
||||
|
||||
error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut`
|
||||
--> $DIR/method_list_2.rs:46:5
|
||||
|
|
||||
LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name
|
||||
|
||||
error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter`
|
||||
--> $DIR/method_list_2.rs:50:5
|
||||
|
|
||||
LL | / pub fn into_iter(self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name
|
||||
|
||||
error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul`
|
||||
--> $DIR/method_list_2.rs:54:5
|
||||
|
|
||||
LL | / pub fn mul(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name
|
||||
|
||||
error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg`
|
||||
--> $DIR/method_list_2.rs:58:5
|
||||
|
|
||||
LL | / pub fn neg(self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name
|
||||
|
||||
error: method `next` can be confused for the standard trait method `std::iter::Iterator::next`
|
||||
--> $DIR/method_list_2.rs:62:5
|
||||
|
|
||||
LL | / pub fn next(&mut self) -> Option<Self> {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name
|
||||
|
||||
error: method `not` can be confused for the standard trait method `std::ops::Not::not`
|
||||
--> $DIR/method_list_2.rs:66:5
|
||||
|
|
||||
LL | / pub fn not(self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name
|
||||
|
||||
error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem`
|
||||
--> $DIR/method_list_2.rs:70:5
|
||||
|
|
||||
LL | / pub fn rem(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name
|
||||
|
||||
error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl`
|
||||
--> $DIR/method_list_2.rs:74:5
|
||||
|
|
||||
LL | / pub fn shl(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name
|
||||
|
||||
error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr`
|
||||
--> $DIR/method_list_2.rs:78:5
|
||||
|
|
||||
LL | / pub fn shr(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name
|
||||
|
||||
error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub`
|
||||
--> $DIR/method_list_2.rs:82:5
|
||||
|
|
||||
LL | / pub fn sub(self, rhs: Self) -> Self {
|
||||
LL | | unimplemented!()
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
|
||||
= help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name
|
||||
|
||||
error: aborting due to 15 previous errors
|
||||
|
15
tests/ui/single_char_push_str.fixed
Normal file
15
tests/ui/single_char_push_str.fixed
Normal file
|
@ -0,0 +1,15 @@
|
|||
// run-rustfix
|
||||
#![warn(clippy::single_char_push_str)]
|
||||
|
||||
fn main() {
|
||||
let mut string = String::new();
|
||||
string.push('R');
|
||||
string.push('\'');
|
||||
|
||||
string.push('u');
|
||||
string.push_str("st");
|
||||
string.push_str("");
|
||||
string.push('\x52');
|
||||
string.push('\u{0052}');
|
||||
string.push('a');
|
||||
}
|
15
tests/ui/single_char_push_str.rs
Normal file
15
tests/ui/single_char_push_str.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// run-rustfix
|
||||
#![warn(clippy::single_char_push_str)]
|
||||
|
||||
fn main() {
|
||||
let mut string = String::new();
|
||||
string.push_str("R");
|
||||
string.push_str("'");
|
||||
|
||||
string.push('u');
|
||||
string.push_str("st");
|
||||
string.push_str("");
|
||||
string.push_str("\x52");
|
||||
string.push_str("\u{0052}");
|
||||
string.push_str(r##"a"##);
|
||||
}
|
34
tests/ui/single_char_push_str.stderr
Normal file
34
tests/ui/single_char_push_str.stderr
Normal file
|
@ -0,0 +1,34 @@
|
|||
error: calling `push_str()` using a single-character string literal
|
||||
--> $DIR/single_char_push_str.rs:6:5
|
||||
|
|
||||
LL | string.push_str("R");
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
|
||||
|
|
||||
= note: `-D clippy::single-char-push-str` implied by `-D warnings`
|
||||
|
||||
error: calling `push_str()` using a single-character string literal
|
||||
--> $DIR/single_char_push_str.rs:7:5
|
||||
|
|
||||
LL | string.push_str("'");
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
|
||||
|
||||
error: calling `push_str()` using a single-character string literal
|
||||
--> $DIR/single_char_push_str.rs:12:5
|
||||
|
|
||||
LL | string.push_str("/x52");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
|
||||
|
||||
error: calling `push_str()` using a single-character string literal
|
||||
--> $DIR/single_char_push_str.rs:13:5
|
||||
|
|
||||
LL | string.push_str("/u{0052}");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
|
||||
|
||||
error: calling `push_str()` using a single-character string literal
|
||||
--> $DIR/single_char_push_str.rs:14:5
|
||||
|
|
||||
LL | string.push_str(r##"a"##);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:7:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
|
@ -6,37 +6,37 @@ LL | vec.sort();
|
|||
|
|
||||
= note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:9:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:11:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:13:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:15:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:17:5
|
||||
|
|
||||
LL | vec.sort();
|
||||
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
||||
|
||||
error: Use sort_unstable instead of sort
|
||||
error: used sort instead of sort_unstable
|
||||
--> $DIR/stable_sort_primitive.rs:19:5
|
||||
|
|
||||
LL | arr.sort();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![warn(clippy::suspicious_arithmetic_impl)]
|
||||
use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub};
|
||||
use std::ops::{
|
||||
Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Foo(u32);
|
||||
|
@ -61,6 +63,54 @@ impl Div for Foo {
|
|||
}
|
||||
}
|
||||
|
||||
impl Rem for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn rem(self, other: Self) -> Self {
|
||||
Foo(self.0 / other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn bitand(self, other: Self) -> Self {
|
||||
Foo(self.0 | other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn bitor(self, other: Self) -> Self {
|
||||
Foo(self.0 ^ other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn bitxor(self, other: Self) -> Self {
|
||||
Foo(self.0 & other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn shl(self, other: Self) -> Self {
|
||||
Foo(self.0 >> other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr for Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn shr(self, other: Self) -> Self {
|
||||
Foo(self.0 << other.0)
|
||||
}
|
||||
}
|
||||
|
||||
struct Bar(i32);
|
||||
|
||||
impl Add for Bar {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: suspicious use of binary operator in `Add` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:11:20
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:13:20
|
||||
|
|
||||
LL | Foo(self.0 - other.0)
|
||||
| ^
|
||||
|
@ -7,7 +7,7 @@ LL | Foo(self.0 - other.0)
|
|||
= note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings`
|
||||
|
||||
error: suspicious use of binary operator in `AddAssign` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:17:23
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:19:23
|
||||
|
|
||||
LL | *self = *self - other;
|
||||
| ^
|
||||
|
@ -15,10 +15,46 @@ LL | *self = *self - other;
|
|||
= note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default
|
||||
|
||||
error: suspicious use of binary operator in `MulAssign` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:30:16
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:32:16
|
||||
|
|
||||
LL | self.0 /= other.0;
|
||||
| ^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: suspicious use of binary operator in `Rem` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:70:20
|
||||
|
|
||||
LL | Foo(self.0 / other.0)
|
||||
| ^
|
||||
|
||||
error: suspicious use of binary operator in `BitAnd` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:78:20
|
||||
|
|
||||
LL | Foo(self.0 | other.0)
|
||||
| ^
|
||||
|
||||
error: suspicious use of binary operator in `BitOr` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:86:20
|
||||
|
|
||||
LL | Foo(self.0 ^ other.0)
|
||||
| ^
|
||||
|
||||
error: suspicious use of binary operator in `BitXor` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:94:20
|
||||
|
|
||||
LL | Foo(self.0 & other.0)
|
||||
| ^
|
||||
|
||||
error: suspicious use of binary operator in `Shl` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:102:20
|
||||
|
|
||||
LL | Foo(self.0 >> other.0)
|
||||
| ^^
|
||||
|
||||
error: suspicious use of binary operator in `Shr` impl
|
||||
--> $DIR/suspicious_arithmetic_impl.rs:110:20
|
||||
|
|
||||
LL | Foo(self.0 << other.0)
|
||||
| ^^
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
|
|
55
tests/ui/to_string_in_display.rs
Normal file
55
tests/ui/to_string_in_display.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#![warn(clippy::to_string_in_display)]
|
||||
#![allow(clippy::inherent_to_string_shadow_display)]
|
||||
|
||||
use std::fmt;
|
||||
|
||||
struct A;
|
||||
impl A {
|
||||
fn fmt(&self) {
|
||||
self.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
trait B {
|
||||
fn fmt(&self) {}
|
||||
}
|
||||
|
||||
impl B for A {
|
||||
fn fmt(&self) {
|
||||
self.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for A {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt(a: A) {
|
||||
a.to_string();
|
||||
}
|
||||
|
||||
struct C;
|
||||
|
||||
impl C {
|
||||
fn to_string(&self) -> String {
|
||||
String::from("I am C")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for C {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A;
|
||||
a.to_string();
|
||||
a.fmt();
|
||||
fmt(a);
|
||||
|
||||
let c = C;
|
||||
c.to_string();
|
||||
}
|
10
tests/ui/to_string_in_display.stderr
Normal file
10
tests/ui/to_string_in_display.stderr
Normal file
|
@ -0,0 +1,10 @@
|
|||
error: Using to_string in fmt::Display implementation might lead to infinite recursion
|
||||
--> $DIR/to_string_in_display.rs:25:25
|
||||
|
|
||||
LL | write!(f, "{}", self.to_string())
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::to-string-in-display` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
#![feature(const_fn_transmute)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate core;
|
||||
|
@ -81,9 +82,26 @@ fn int_to_bool() {
|
|||
}
|
||||
|
||||
#[warn(clippy::transmute_int_to_float)]
|
||||
fn int_to_float() {
|
||||
let _: f32 = unsafe { std::mem::transmute(0_u32) };
|
||||
let _: f32 = unsafe { std::mem::transmute(0_i32) };
|
||||
mod int_to_float {
|
||||
fn test() {
|
||||
let _: f32 = unsafe { std::mem::transmute(0_u32) };
|
||||
let _: f32 = unsafe { std::mem::transmute(0_i32) };
|
||||
let _: f64 = unsafe { std::mem::transmute(0_u64) };
|
||||
let _: f64 = unsafe { std::mem::transmute(0_i64) };
|
||||
}
|
||||
|
||||
mod issue_5747 {
|
||||
const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) };
|
||||
const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) };
|
||||
|
||||
const fn from_bits_32(v: i32) -> f32 {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
|
||||
const fn from_bits_64(v: u64) -> f64 {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: transmute from a type (`&T`) to itself
|
||||
--> $DIR/transmute.rs:19:20
|
||||
--> $DIR/transmute.rs:20:20
|
||||
|
|
||||
LL | let _: &'a T = core::intrinsics::transmute(t);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t);
|
|||
= note: `-D clippy::useless-transmute` implied by `-D warnings`
|
||||
|
||||
error: transmute from a reference to a pointer
|
||||
--> $DIR/transmute.rs:23:23
|
||||
--> $DIR/transmute.rs:24:23
|
||||
|
|
||||
LL | let _: *const T = core::intrinsics::transmute(t);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
|
||||
|
||||
error: transmute from a reference to a pointer
|
||||
--> $DIR/transmute.rs:25:21
|
||||
--> $DIR/transmute.rs:26:21
|
||||
|
|
||||
LL | let _: *mut T = core::intrinsics::transmute(t);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
|
||||
|
||||
error: transmute from a reference to a pointer
|
||||
--> $DIR/transmute.rs:27:23
|
||||
--> $DIR/transmute.rs:28:23
|
||||
|
|
||||
LL | let _: *const U = core::intrinsics::transmute(t);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
|
||||
|
||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||
--> $DIR/transmute.rs:33:27
|
||||
--> $DIR/transmute.rs:34:27
|
||||
|
|
||||
LL | let _: Vec<i32> = core::intrinsics::transmute(my_vec());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||
--> $DIR/transmute.rs:35:27
|
||||
--> $DIR/transmute.rs:36:27
|
||||
|
|
||||
LL | let _: Vec<i32> = core::mem::transmute(my_vec());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||
--> $DIR/transmute.rs:37:27
|
||||
--> $DIR/transmute.rs:38:27
|
||||
|
|
||||
LL | let _: Vec<i32> = std::intrinsics::transmute(my_vec());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||
--> $DIR/transmute.rs:39:27
|
||||
--> $DIR/transmute.rs:40:27
|
||||
|
|
||||
LL | let _: Vec<i32> = std::mem::transmute(my_vec());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`std::vec::Vec<i32>`) to itself
|
||||
--> $DIR/transmute.rs:41:27
|
||||
--> $DIR/transmute.rs:42:27
|
||||
|
|
||||
LL | let _: Vec<i32> = my_transmute(my_vec());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from an integer to a pointer
|
||||
--> $DIR/transmute.rs:43:31
|
||||
--> $DIR/transmute.rs:44:31
|
||||
|
|
||||
LL | let _: *const usize = std::mem::transmute(5_isize);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
|
||||
|
||||
error: transmute from an integer to a pointer
|
||||
--> $DIR/transmute.rs:47:31
|
||||
--> $DIR/transmute.rs:48:31
|
||||
|
|
||||
LL | let _: *const usize = std::mem::transmute(1 + 1usize);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
|
||||
|
||||
error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
|
||||
--> $DIR/transmute.rs:62:24
|
||||
--> $DIR/transmute.rs:63:24
|
||||
|
|
||||
LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr);
|
|||
= note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
|
||||
|
||||
error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
|
||||
--> $DIR/transmute.rs:64:24
|
||||
--> $DIR/transmute.rs:65:24
|
||||
|
|
||||
LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
|
||||
--> $DIR/transmute.rs:66:31
|
||||
--> $DIR/transmute.rs:67:31
|
||||
|
|
||||
LL | let _: *const Usize = core::intrinsics::transmute(my_int());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
|
||||
--> $DIR/transmute.rs:68:29
|
||||
--> $DIR/transmute.rs:69:29
|
||||
|
|
||||
LL | let _: *mut Usize = core::intrinsics::transmute(my_int());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: transmute from a `u32` to a `char`
|
||||
--> $DIR/transmute.rs:74:28
|
||||
--> $DIR/transmute.rs:75:28
|
||||
|
|
||||
LL | let _: char = unsafe { std::mem::transmute(0_u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
|
||||
|
@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) };
|
|||
= note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
|
||||
|
||||
error: transmute from a `i32` to a `char`
|
||||
--> $DIR/transmute.rs:75:28
|
||||
--> $DIR/transmute.rs:76:28
|
||||
|
|
||||
LL | let _: char = unsafe { std::mem::transmute(0_i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
|
||||
|
||||
error: transmute from a `u8` to a `bool`
|
||||
--> $DIR/transmute.rs:80:28
|
||||
--> $DIR/transmute.rs:81:28
|
||||
|
|
||||
LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
|
||||
|
@ -115,21 +115,33 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) };
|
|||
= note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
|
||||
|
||||
error: transmute from a `u32` to a `f32`
|
||||
--> $DIR/transmute.rs:85:27
|
||||
--> $DIR/transmute.rs:87:31
|
||||
|
|
||||
LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
|
||||
LL | let _: f32 = unsafe { std::mem::transmute(0_u32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
|
||||
|
|
||||
= note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
|
||||
|
||||
error: transmute from a `i32` to a `f32`
|
||||
--> $DIR/transmute.rs:86:27
|
||||
--> $DIR/transmute.rs:88:31
|
||||
|
|
||||
LL | let _: f32 = unsafe { std::mem::transmute(0_i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
|
||||
LL | let _: f32 = unsafe { std::mem::transmute(0_i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
|
||||
|
||||
error: transmute from a `u64` to a `f64`
|
||||
--> $DIR/transmute.rs:89:31
|
||||
|
|
||||
LL | let _: f64 = unsafe { std::mem::transmute(0_u64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
|
||||
|
||||
error: transmute from a `i64` to a `f64`
|
||||
--> $DIR/transmute.rs:90:31
|
||||
|
|
||||
LL | let _: f64 = unsafe { std::mem::transmute(0_i64) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
|
||||
|
||||
error: transmute from a `&[u8]` to a `&str`
|
||||
--> $DIR/transmute.rs:90:28
|
||||
--> $DIR/transmute.rs:108:28
|
||||
|
|
||||
LL | let _: &str = unsafe { std::mem::transmute(b) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
|
||||
|
@ -137,10 +149,10 @@ LL | let _: &str = unsafe { std::mem::transmute(b) };
|
|||
= note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
|
||||
|
||||
error: transmute from a `&mut [u8]` to a `&mut str`
|
||||
--> $DIR/transmute.rs:91:32
|
||||
--> $DIR/transmute.rs:109:32
|
||||
|
|
||||
LL | let _: &mut str = unsafe { std::mem::transmute(mb) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
|
||||
|
||||
error: aborting due to 22 previous errors
|
||||
error: aborting due to 24 previous errors
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#[warn(clippy::transmute_float_to_int)]
|
||||
#![feature(const_fn_transmute)]
|
||||
#![warn(clippy::transmute_float_to_int)]
|
||||
|
||||
fn float_to_int() {
|
||||
let _: u32 = unsafe { std::mem::transmute(1f32) };
|
||||
|
@ -9,4 +10,17 @@ fn float_to_int() {
|
|||
let _: u64 = unsafe { std::mem::transmute(-1.0) };
|
||||
}
|
||||
|
||||
mod issue_5747 {
|
||||
const VALUE32: i32 = unsafe { std::mem::transmute(1f32) };
|
||||
const VALUE64: u64 = unsafe { std::mem::transmute(1f64) };
|
||||
|
||||
const fn to_bits_32(v: f32) -> u32 {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
|
||||
const fn to_bits_64(v: f64) -> i64 {
|
||||
unsafe { std::mem::transmute(v) }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue