Auto merge of #6896 - TaKO8Ki:refactor-lints-in-methods-module, r=phansch

Refactor lints in methods module

This PR refactors methods lints other than the lints I refactored in https://github.com/rust-lang/rust-clippy/pull/6826 and moves some functions to methods/utils.rs.
Basically, I follow the instruction described in #6680.

**For ease of review, I refactored step by step, keeping each commit small.**

closes https://github.com/rust-lang/rust-clippy/issues/6886
cc: `@phansch,` `@flip1995,` `@Y-Nak`

changelog: Move lints in methods module to their own modules and some function to methods/utils.rs.
This commit is contained in:
bors 2021-03-22 19:36:22 +00:00
commit 029777f029
27 changed files with 388 additions and 333 deletions

View File

@ -0,0 +1,54 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{method_chain_args, single_segment_path};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::Lint;
use rustc_middle::ty;
use rustc_span::sym;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
pub(super) fn check(
cx: &LateContext<'_>,
info: &crate::methods::BinaryExprInfo<'_>,
chain_methods: &[&str],
lint: &'static Lint,
suggest: &str,
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
if arg_char.len() == 1;
if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym::Some;
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
if *self_ty.kind() != ty::Str {
return false;
}
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
applicability,
);
return true;
}
}
false
}

View File

@ -0,0 +1,44 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::method_chain_args;
use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::Lint;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
info: &crate::methods::BinaryExprInfo<'_>,
chain_methods: &[&str],
lint: &'static Lint,
suggest: &str,
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Lit(ref lit) = info.other.kind;
if let ast::LitKind::Char(c) = lit.node;
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
c),
applicability,
);
true
} else {
false
}
}
}

View File

@ -0,0 +1,13 @@
use crate::methods::chars_cmp;
use rustc_lint::LateContext;
use super::CHARS_LAST_CMP;
/// Checks for the `CHARS_LAST_CMP` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
true
} else {
chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
}
}

View File

@ -0,0 +1,13 @@
use crate::methods::chars_cmp_with_unwrap;
use rustc_lint::LateContext;
use super::CHARS_LAST_CMP;
/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
true
} else {
chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
}
}

View File

@ -0,0 +1,8 @@
use rustc_lint::LateContext;
use super::CHARS_NEXT_CMP;
/// Checks for the `CHARS_NEXT_CMP` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
}

View File

@ -0,0 +1,8 @@
use rustc_lint::LateContext;
use super::CHARS_NEXT_CMP;
/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
}

View File

@ -4,14 +4,20 @@ use clippy_utils::ty::is_copy;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty;
use rustc_span::symbol::{sym, Symbol};
use std::iter;
use super::CLONE_DOUBLE_REF;
use super::CLONE_ON_COPY;
/// Checks for the `CLONE_ON_COPY` lint.
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
if !(args.len() == 1 && method_name == sym::clone) {
return;
}
let arg = &args[0];
let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
let ty = cx.typeck_results().expr_ty(expr);
if let ty::Ref(_, inner, _) = arg_ty.kind() {
if let ty::Ref(_, innermost, _) = inner.kind() {

View File

@ -6,11 +6,15 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::symbol::sym;
use rustc_span::symbol::{sym, Symbol};
use super::CLONE_ON_REF_PTR;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
if !(args.len() == 1 && method_name == sym::clone) {
return;
}
let arg = &args[0];
let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
if let ty::Adt(_, subst) = obj_ty.kind() {

View File

@ -7,12 +7,7 @@ use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter().flat_map()` for `Iterators`
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
_filter_args: &'tcx [hir::Expr<'_>],
_map_args: &'tcx [hir::Expr<'_>],
) {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter().flat_map()` is an Iterator
if is_trait_method(cx, expr, sym::Iterator) {
let msg = "called `filter(..).flat_map(..)` on an `Iterator`";

View File

@ -7,12 +7,7 @@ use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter_map().flat_map()` for `Iterators`
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
_filter_args: &'tcx [hir::Expr<'_>],
_map_args: &'tcx [hir::Expr<'_>],
) {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter_map().flat_map()` is an Iterator
if is_trait_method(cx, expr, sym::Iterator) {
let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";

View File

@ -7,12 +7,7 @@ use rustc_span::sym;
use super::FILTER_MAP;
/// lint use of `filter_map().map()` for `Iterators`
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
_filter_args: &'tcx [hir::Expr<'_>],
_map_args: &'tcx [hir::Expr<'_>],
) {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// lint if caller of `.filter_map().map()` is an Iterator
if is_trait_method(cx, expr, sym::Iterator) {
let msg = "called `filter_map(..).map(..)` on an `Iterator`";

View File

@ -1,20 +1,22 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, paths, sugg};
use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::ExprKind;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::Ty;
use rustc_span::sym;
use super::FROM_ITER_INSTEAD_OF_COLLECT;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let ty = cx.typeck_results().expr_ty(expr);
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
if_chain! {
if let hir::ExprKind::Path(path) = func_kind;
if match_qpath(path, &["from_iter"]);
let ty = cx.typeck_results().expr_ty(expr);
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);

View File

@ -1,4 +1,4 @@
use crate::methods::derefs_to_slice;
use super::utils::derefs_to_slice;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, match_type};

View File

@ -1,4 +1,3 @@
use super::INEFFICIENT_TO_STRING;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
@ -8,14 +7,18 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::sym;
use rustc_span::symbol::{sym, Symbol};
use super::INEFFICIENT_TO_STRING;
/// Checks for the `INEFFICIENT_TO_STRING` lint
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
if_chain! {
if args.len() == 1 && method_name == sym!(to_string);
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
let self_ty = substs.type_at(0);
let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
if deref_count >= 1;
@ -32,7 +35,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr
self_ty, deref_self_ty
));
let mut applicability = Applicability::MachineApplicable;
let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
diag.span_suggestion(
expr.span,
"try dereferencing the receiver",

View File

@ -1,32 +1,43 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
use clippy_utils::ty::has_iter_method;
use clippy_utils::{match_trait_method, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{sym, Symbol};
use super::INTO_ITER_ON_REF;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) {
if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
return;
}
if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
span_lint_and_sugg(
cx,
INTO_ITER_ON_REF,
method_span,
&format!(
"this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
method_name, kind,
),
"call directly",
method_name.to_string(),
Applicability::MachineApplicable,
);
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
method_span: Span,
method_name: Symbol,
args: &[hir::Expr<'_>],
) {
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
if_chain! {
if let ty::Ref(..) = self_ty.kind();
if method_name == sym::into_iter;
if is_trait_method(cx, expr, sym::IntoIterator);
if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
then {
span_lint_and_sugg(
cx,
INTO_ITER_ON_REF,
method_span,
&format!(
"this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
method_name, kind,
),
"call directly",
method_name.to_string(),
Applicability::MachineApplicable,
);
}
}
}

View File

@ -1,4 +1,4 @@
use crate::methods::derefs_to_slice;
use crate::methods::utils::derefs_to_slice;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;

View File

@ -1,4 +1,4 @@
use crate::methods::derefs_to_slice;
use super::utils::derefs_to_slice;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths;
use clippy_utils::source::snippet_with_applicability;

View File

@ -1,4 +1,4 @@
use crate::methods::derefs_to_slice;
use super::utils::derefs_to_slice;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
@ -47,12 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
);
}
}
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type)
|| matches!(
&cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
ty::Array(_, _)
)
{
} else if is_vec_or_array(cx, caller_expr) {
// caller is a Vec or an Array
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
@ -69,3 +64,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
);
}
}
fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type)
|| matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
}

View File

@ -1,4 +1,4 @@
use crate::methods::derefs_to_slice;
use super::utils::derefs_to_slice;
use crate::methods::iter_nth_zero;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;

View File

@ -12,14 +12,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir
// lint if caller of skip is an Iterator
if is_trait_method(cx, expr, sym::Iterator) {
if let [caller, n] = skip_args {
let hint = format!(".nth({})", snippet(cx, n.span, ".."));
span_lint_and_sugg(
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(),
"called `skip(..).next()` on an iterator",
"use `nth` instead",
hint,
format!(".nth({})", snippet(cx, n.span, "..")),
Applicability::MachineApplicable,
);
}

View File

@ -1,5 +1,11 @@
mod bind_instead_of_map;
mod bytes_nth;
mod chars_cmp;
mod chars_cmp_with_unwrap;
mod chars_last_cmp;
mod chars_last_cmp_with_unwrap;
mod chars_next_cmp;
mod chars_next_cmp_with_unwrap;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod expect_fun_call;
@ -36,6 +42,7 @@ mod option_map_or_none;
mod option_map_unwrap_or;
mod or_fun_call;
mod search_is_some;
mod single_char_add_str;
mod single_char_insert_string;
mod single_char_pattern;
mod single_char_push_string;
@ -48,23 +55,21 @@ mod unnecessary_fold;
mod unnecessary_lazy_eval;
mod unwrap_used;
mod useless_asref;
mod utils;
mod wrong_self_convention;
mod zst_offset;
use bind_instead_of_map::BindInsteadOfMap;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
use clippy_utils::{
contains_return, get_trait_def_id, in_macro, iter_input_pats, match_def_path, match_qpath, method_calls,
method_chain_args, paths, return_ty, single_segment_path, SpanlessEq,
contains_return, get_trait_def_id, in_macro, iter_input_pats, match_qpath, method_calls, paths, return_ty,
SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_semver::RustcVersion;
@ -1718,11 +1723,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]),
["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]),
["map", "filter"] => filter_map::check(cx, expr, false),
["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["map", "filter_map"] => filter_map_map::check(cx, expr),
["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()),
["map", "find"] => filter_map::check(cx, expr, true),
["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", "filter"] => filter_flat_map::check(cx, expr),
["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr),
["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]),
["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]),
[option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => {
@ -1809,46 +1814,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
match expr.kind {
hir::ExprKind::Call(ref func, ref args) => {
if let hir::ExprKind::Path(path) = &func.kind {
if match_qpath(path, &["from_iter"]) {
from_iter_instead_of_collect::check(cx, expr, args);
}
}
from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
},
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
if args.len() == 1 && method_call.ident.name == sym::clone {
clone_on_copy::check(cx, expr, &args[0], self_ty);
clone_on_ref_ptr::check(cx, expr, &args[0]);
}
if args.len() == 1 && method_call.ident.name == sym!(to_string) {
inefficient_to_string::check(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) {
single_char_push_string::check(cx, expr, args);
} else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
single_char_insert_string::check(cx, expr, args);
}
}
match self_ty.kind() {
ty::Ref(_, ty, _) if *ty.kind() == ty::Str => {
for &(method, pos) in &PATTERN_METHODS {
if method_call.ident.name.as_str() == method && args.len() > pos {
single_char_pattern::check(cx, expr, &args[pos]);
}
}
},
ty::Ref(..) if method_call.ident.name == sym::into_iter => {
into_iter_on_ref::check(cx, expr, self_ty, *method_span);
},
_ => (),
}
clone_on_copy::check(cx, expr, method_call.ident.name, args);
clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
inefficient_to_string::check(cx, expr, method_call.ident.name, args);
single_char_add_str::check(cx, expr, args);
into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
single_char_pattern::check(cx, expr, method_call.ident.name, args);
},
hir::ExprKind::Binary(op, ref lhs, ref rhs)
if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
@ -2015,47 +1991,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
extract_msrv_attr!(LateContext);
}
fn derefs_to_slice<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
ty: Ty<'tcx>,
) -> Option<&'tcx hir::Expr<'tcx>> {
fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
match ty.kind() {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
ty::Array(_, size) => size
.try_eval_usize(cx.tcx, cx.param_env)
.map_or(false, |size| size < 32),
ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false,
}
}
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
Some(&args[0])
} else {
None
}
} else {
match ty.kind() {
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
ty::Ref(_, inner, _) => {
if may_slice(cx, inner) {
Some(expr)
} else {
None
}
},
_ => None,
}
}
}
/// Used for `lint_binary_expr_with_method_call`.
#[derive(Copy, Clone)]
struct BinaryExprInfo<'a> {
@ -2068,7 +2003,7 @@ struct BinaryExprInfo<'a> {
/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
macro_rules! lint_with_both_lhs_and_rhs {
($func:ident, $cx:expr, $info:ident) => {
($func:expr, $cx:expr, $info:ident) => {
if !$func($cx, $info) {
::std::mem::swap(&mut $info.chain, &mut $info.other);
if $func($cx, $info) {
@ -2078,145 +2013,10 @@ fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExpr
};
}
lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info);
lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info);
lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info);
lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info);
}
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
fn lint_chars_cmp(
cx: &LateContext<'_>,
info: &BinaryExprInfo<'_>,
chain_methods: &[&str],
lint: &'static Lint,
suggest: &str,
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
if arg_char.len() == 1;
if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym::Some;
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
if *self_ty.kind() != ty::Str {
return false;
}
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
applicability,
);
return true;
}
}
false
}
/// Checks for the `CHARS_NEXT_CMP` lint.
fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
}
/// Checks for the `CHARS_LAST_CMP` lint.
fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
true
} else {
lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
}
}
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
fn lint_chars_cmp_with_unwrap<'tcx>(
cx: &LateContext<'tcx>,
info: &BinaryExprInfo<'_>,
chain_methods: &[&str],
lint: &'static Lint,
suggest: &str,
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Lit(ref lit) = info.other.kind;
if let ast::LitKind::Char(c) = lit.node;
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
c),
applicability,
);
true
} else {
false
}
}
}
/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
}
/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
true
} else {
lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
}
}
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;
let string = r.as_str();
if string.chars().count() == 1;
then {
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"##
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
} else {
// for regular string: "a"
&snip[1..(snip.len() - 1)]
};
let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
Some(hint)
} else {
None
}
}
lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
}
const FN_HEADER: hir::FnHeader = hir::FnHeader {

View File

@ -0,0 +1,14 @@
use crate::methods::{single_char_insert_string, single_char_push_string};
use clippy_utils::{match_def_path, paths};
use rustc_hir as hir;
use rustc_lint::LateContext;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
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) {
single_char_push_string::check(cx, expr, args);
} else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
single_char_insert_string::check(cx, expr, args);
}
}
}

View File

@ -1,4 +1,4 @@
use crate::methods::get_hint_if_single_char_arg;
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;

View File

@ -1,23 +1,35 @@
use crate::methods::get_hint_if_single_char_arg;
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::symbol::Symbol;
use super::SINGLE_CHAR_PATTERN;
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
pub(super) fn check(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,
);
pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
for &(method, pos) in &crate::methods::PATTERN_METHODS {
if_chain! {
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
if *ty.kind() == ty::Str;
if method_name.as_str() == method && args.len() > pos;
let arg = &args[pos];
let mut applicability = Applicability::MachineApplicable;
if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability);
then {
span_lint_and_sugg(
cx,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
"try using a `char` instead",
hint,
applicability,
);
}
}
}
}

View File

@ -1,4 +1,4 @@
use crate::methods::get_hint_if_single_char_arg;
use super::utils::get_hint_if_single_char_arg;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;

View File

@ -12,34 +12,35 @@ use super::STRING_EXTEND_CHARS;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
let arg = &args[1];
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
let target = &arglists[0][0];
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str {
""
} else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
"&"
} else {
return;
};
if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
return;
}
let arg = &args[1];
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
let target = &arglists[0][0];
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str {
""
} else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
"&"
} else {
return;
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
"try this",
format!(
"{}.push_str({}{})",
snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
ref_str,
snippet_with_applicability(cx, target.span, "..", &mut applicability)
),
applicability,
);
}
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
"try this",
format!(
"{}.push_str({}{})",
snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
ref_str,
snippet_with_applicability(cx, target.span, "..", &mut applicability)
),
applicability,
);
}
}

View File

@ -0,0 +1,78 @@
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_middle::ty::Ty;
use rustc_span::symbol::sym;
pub(super) fn derefs_to_slice<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
ty: Ty<'tcx>,
) -> Option<&'tcx hir::Expr<'tcx>> {
fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
match ty.kind() {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
ty::Array(_, size) => size
.try_eval_usize(cx.tcx, cx.param_env)
.map_or(false, |size| size < 32),
ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false,
}
}
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
Some(&args[0])
} else {
None
}
} else {
match ty.kind() {
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
ty::Ref(_, inner, _) => {
if may_slice(cx, inner) {
Some(expr)
} else {
None
}
},
_ => None,
}
}
}
pub(super) 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;
let string = r.as_str();
if string.chars().count() == 1;
then {
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"##
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
} else {
// for regular string: "a"
&snip[1..(snip.len() - 1)]
};
let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
Some(hint)
} else {
None
}
}
}