Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
cc63143bbf
213 changed files with 3146 additions and 1693 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{eq_expr_value, get_parent_expr};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -46,7 +46,7 @@ fn collect_replace_calls<'tcx>(
|
|||
let mut methods = VecDeque::new();
|
||||
let mut from_args = VecDeque::new();
|
||||
|
||||
let _: Option<()> = for_each_expr(expr, |e| {
|
||||
let _: Option<()> = for_each_expr_without_closures(expr, |e| {
|
||||
if let Some(("replace", _, [from, to], _, _)) = method_call(e) {
|
||||
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||
methods.push_front(e);
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ mod map_flatten;
|
|||
mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod mut_mutex_lock;
|
||||
mod needless_character_iteration;
|
||||
mod needless_collect;
|
||||
mod needless_option_as_deref;
|
||||
mod needless_option_take;
|
||||
|
|
@ -93,7 +94,6 @@ mod seek_from_current;
|
|||
mod seek_to_start_instead_of_rewind;
|
||||
mod single_char_add_str;
|
||||
mod single_char_insert_string;
|
||||
mod single_char_pattern;
|
||||
mod single_char_push_string;
|
||||
mod skip_while_next;
|
||||
mod stable_sort_primitive;
|
||||
|
|
@ -1140,38 +1140,6 @@ declare_clippy_lint! {
|
|||
"not returning type containing `Self` in a `new` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for string methods that receive a single-character
|
||||
/// `str` as an argument, e.g., `_.split("x")`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While this can make a perf difference on some systems,
|
||||
/// benchmarks have proven inconclusive. But at least using a
|
||||
/// char literal makes it clear that we are looking at a single
|
||||
/// character.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Does not catch multi-byte unicode characters. This is by
|
||||
/// design, on many machines, splitting by a non-ascii char is
|
||||
/// actually slower. Please do your own measurements instead of
|
||||
/// relying solely on the results of this lint.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// _.split("x");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// _.split('x');
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub SINGLE_CHAR_PATTERN,
|
||||
pedantic,
|
||||
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calling `.step_by(0)` on iterators which panics.
|
||||
|
|
@ -4084,12 +4052,33 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// println!("the string is empty");
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
#[clippy::version = "1.79.0"]
|
||||
pub CONST_IS_EMPTY,
|
||||
suspicious,
|
||||
"is_empty() called on strings known at compile time"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if an iterator is used to check if a string is ascii.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `str` type already implements the `is_ascii` method.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// "foo".chars().all(|c| c.is_ascii());
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// "foo".is_ascii();
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub NEEDLESS_CHARACTER_ITERATION,
|
||||
suspicious,
|
||||
"is_ascii() called on a char iterator"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
|
|
@ -4147,7 +4136,6 @@ impl_lint_pass!(Methods => [
|
|||
FLAT_MAP_OPTION,
|
||||
INEFFICIENT_TO_STRING,
|
||||
NEW_RET_NO_SELF,
|
||||
SINGLE_CHAR_PATTERN,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
SEARCH_IS_SOME,
|
||||
FILTER_NEXT,
|
||||
|
|
@ -4255,6 +4243,7 @@ impl_lint_pass!(Methods => [
|
|||
UNNECESSARY_RESULT_MAP_OR_ELSE,
|
||||
MANUAL_C_STR_LITERALS,
|
||||
UNNECESSARY_GET_THEN_CHECK,
|
||||
NEEDLESS_CHARACTER_ITERATION,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
|
@ -4301,7 +4290,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
|
||||
single_char_add_str::check(cx, expr, receiver, args);
|
||||
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
|
||||
single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
|
||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv);
|
||||
},
|
||||
ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||
|
|
@ -4462,6 +4450,7 @@ impl Methods {
|
|||
},
|
||||
("all", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
needless_character_iteration::check(cx, expr, recv, arg, true);
|
||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
||||
iter_overeager_cloned::check(
|
||||
cx,
|
||||
|
|
@ -4482,6 +4471,7 @@ impl Methods {
|
|||
},
|
||||
("any", [arg]) => {
|
||||
unused_enumerate_index::check(cx, expr, recv, arg);
|
||||
needless_character_iteration::check(cx, expr, recv, arg, false);
|
||||
match method_call(recv) {
|
||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||
cx,
|
||||
|
|
|
|||
124
clippy_lints/src/methods/needless_character_iteration.rs
Normal file
124
clippy_lints/src/methods/needless_character_iteration.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, StmtKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::utils::get_last_chain_binding_hir_id;
|
||||
use super::NEEDLESS_CHARACTER_ITERATION;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
|
||||
|
||||
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
|
||||
while let ExprKind::AddrOf(_, _, e) = expr.kind {
|
||||
expr = e;
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
fn handle_expr(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
first_param: HirId,
|
||||
span: Span,
|
||||
before_chars: Span,
|
||||
revert: bool,
|
||||
is_all: bool,
|
||||
) {
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(method, receiver, [], _) => {
|
||||
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
|
||||
// `is_ascii`, then only `.all()` should warn.
|
||||
if revert != is_all
|
||||
&& method.ident.name.as_str() == "is_ascii"
|
||||
&& path_to_local_id(receiver, first_param)
|
||||
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
|
||||
&& *char_arg_ty.kind() == ty::Char
|
||||
&& let Some(snippet) = snippet_opt(cx, before_chars)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_CHARACTER_ITERATION,
|
||||
span,
|
||||
"checking if a string is ascii using iterators",
|
||||
"try",
|
||||
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
ExprKind::Block(block, _) => {
|
||||
if block.stmts.iter().any(|stmt| !matches!(stmt.kind, StmtKind::Let(_))) {
|
||||
// If there is something else than let bindings, then better not emit the lint.
|
||||
return;
|
||||
}
|
||||
if let Some(block_expr) = block.expr
|
||||
// First we ensure that this is a "binding chain" (each statement is a binding
|
||||
// of the previous one) and that it is a binding of the closure argument.
|
||||
&& let Some(last_chain_binding_id) =
|
||||
get_last_chain_binding_hir_id(first_param, block.stmts)
|
||||
{
|
||||
handle_expr(
|
||||
cx,
|
||||
block_expr,
|
||||
last_chain_binding_id,
|
||||
span,
|
||||
before_chars,
|
||||
revert,
|
||||
is_all,
|
||||
);
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all),
|
||||
ExprKind::Call(fn_path, [arg]) => {
|
||||
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
|
||||
// `is_ascii`, then only `.all()` should warn.
|
||||
if revert != is_all
|
||||
&& let ExprKind::Path(path) = fn_path.kind
|
||||
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
|
||||
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
|
||||
&& path_to_local_id(peels_expr_ref(arg), first_param)
|
||||
&& let Some(snippet) = snippet_opt(cx, before_chars)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_CHARACTER_ITERATION,
|
||||
span,
|
||||
"checking if a string is ascii using iterators",
|
||||
"try",
|
||||
format!("{}{snippet}.is_ascii()", if revert { "!" } else { "" }),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
|
||||
&& let body = cx.tcx.hir().body(body)
|
||||
&& let Some(first_param) = body.params.first()
|
||||
&& let ExprKind::MethodCall(method, mut recv, [], _) = recv.kind
|
||||
&& method.ident.name.as_str() == "chars"
|
||||
&& let str_ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs()
|
||||
&& *str_ty.kind() == ty::Str
|
||||
{
|
||||
let expr_start = recv.span;
|
||||
while let ExprKind::MethodCall(_, new_recv, _, _) = recv.kind {
|
||||
recv = new_recv;
|
||||
}
|
||||
let body_expr = peel_blocks(body.value);
|
||||
|
||||
handle_expr(
|
||||
cx,
|
||||
body_expr,
|
||||
first_param.pat.hir_id,
|
||||
recv.span.with_hi(call_expr.span.hi()),
|
||||
recv.span.with_hi(expr_start.hi()),
|
||||
false,
|
||||
is_all,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let map = cx.tcx.hir();
|
||||
let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id));
|
||||
reference_visitor.visit_body(&body);
|
||||
reference_visitor.visit_body(body);
|
||||
|
||||
if reference_visitor.found_reference {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
|||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::deref_closure_args;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
|
||||
use clippy_utils::{is_receiver_of_method_call, is_trait_method, strip_pat_refs};
|
||||
use hir::ExprKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -156,13 +156,3 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
|
||||
&& receiver.hir_id == expr.hir_id
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
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 clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
|
||||
use rustc_ast::BorrowKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::SINGLE_CHAR_ADD_STR;
|
||||
|
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
|
|||
/// lint for length-1 `str`s as argument for `insert_str`
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &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, false) {
|
||||
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[1], &mut applicability, false) {
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
|
||||
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
||||
|
|
@ -25,4 +25,43 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[1].kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||
{
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||
let extension_string =
|
||||
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
|
||||
let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
|
||||
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
|
||||
|
||||
let sugg = format!("{base_string_snippet}.insert({pos_arg}, {deref_string}{extension_string})");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
expr.span,
|
||||
"calling `insert_str()` using a single-character converted to string",
|
||||
"consider using `insert` without `to_string()`",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
if cx.typeck_results().expr_ty(expr).is_ref()
|
||||
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& ty.is_char()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
cx.typeck_results().expr_ty(expr).is_char()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
use super::utils::get_hint_if_single_char_arg;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
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;
|
||||
|
||||
const PATTERN_METHODS: [(&str, usize); 22] = [
|
||||
("contains", 0),
|
||||
("starts_with", 0),
|
||||
("ends_with", 0),
|
||||
("find", 0),
|
||||
("rfind", 0),
|
||||
("split", 0),
|
||||
("split_inclusive", 0),
|
||||
("rsplit", 0),
|
||||
("split_terminator", 0),
|
||||
("rsplit_terminator", 0),
|
||||
("splitn", 1),
|
||||
("rsplitn", 1),
|
||||
("split_once", 0),
|
||||
("rsplit_once", 0),
|
||||
("matches", 0),
|
||||
("rmatches", 0),
|
||||
("match_indices", 0),
|
||||
("rmatch_indices", 0),
|
||||
("trim_start_matches", 0),
|
||||
("trim_end_matches", 0),
|
||||
("replace", 0),
|
||||
("replacen", 0),
|
||||
];
|
||||
|
||||
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
_expr: &hir::Expr<'_>,
|
||||
method_name: Symbol,
|
||||
receiver: &hir::Expr<'_>,
|
||||
args: &[hir::Expr<'_>],
|
||||
) {
|
||||
for &(method, pos) in &PATTERN_METHODS {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind()
|
||||
&& ty.is_str()
|
||||
&& method_name.as_str() == method
|
||||
&& args.len() > pos
|
||||
&& let arg = &args[pos]
|
||||
&& let mut applicability = Applicability::MachineApplicable
|
||||
&& let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_PATTERN,
|
||||
arg.span,
|
||||
"single-character string constant used as pattern",
|
||||
"consider using a `char`",
|
||||
hint,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
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 clippy_utils::source::{snippet_with_applicability, str_literal_to_char_literal};
|
||||
use rustc_ast::BorrowKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::SINGLE_CHAR_ADD_STR;
|
||||
|
|
@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
|
|||
/// lint for length-1 `str`s as argument for `push_str`
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
|
||||
if let Some(extension_string) = str_literal_to_char_literal(cx, &args[0], &mut applicability, false) {
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||
let sugg = format!("{base_string_snippet}.push({extension_string})");
|
||||
|
|
@ -24,4 +24,42 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, arg) = &args[0].kind
|
||||
&& let ExprKind::MethodCall(path_segment, method_arg, _, _) = &arg.kind
|
||||
&& path_segment.ident.name == rustc_span::sym::to_string
|
||||
&& (is_ref_char(cx, method_arg) || is_char(cx, method_arg))
|
||||
{
|
||||
let base_string_snippet =
|
||||
snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
|
||||
let extension_string =
|
||||
snippet_with_applicability(cx, method_arg.span.source_callsite(), "..", &mut applicability);
|
||||
let deref_string = if is_ref_char(cx, method_arg) { "*" } else { "" };
|
||||
|
||||
let sugg = format!("{base_string_snippet}.push({deref_string}{extension_string})");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_ADD_STR,
|
||||
expr.span,
|
||||
"calling `push_str()` using a single-character converted to string",
|
||||
"consider using `push` without `to_string()`",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ref_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
if cx.typeck_results().expr_ty(expr).is_ref()
|
||||
&& let rustc_middle::ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& ty.is_char()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_char(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
cx.typeck_results().expr_ty(expr).is_char()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::consts::{constant, Constant};
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -209,7 +209,7 @@ fn indirect_usage<'tcx>(
|
|||
}) = stmt.kind
|
||||
{
|
||||
let mut path_to_binding = None;
|
||||
let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
|
||||
let _: Option<!> = for_each_expr(cx, init_expr, |e| {
|
||||
if path_to_local_id(e, binding) {
|
||||
path_to_binding = Some(e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed;
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
|
||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
|
|
@ -26,7 +26,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
|
|||
|
||||
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
|
||||
|
||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
||||
let _: Option<!> = for_each_expr_without_closures(body.value, |e| {
|
||||
if let hir::ExprKind::Ret(Some(e)) = &e.kind {
|
||||
let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
|
||||
found_mapping |= found_mapping_res;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::higher::ForLoop;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -61,7 +61,7 @@ pub fn check_for_loop_iter(
|
|||
fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
|
||||
let mut change = false;
|
||||
if let ExprKind::Block(block, ..) = body.kind {
|
||||
for_each_expr(block, |e| {
|
||||
for_each_expr_without_closures(block, |e| {
|
||||
match e.kind {
|
||||
ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
|
||||
change |= !can_mut_borrow_both(cx, caller, assignee);
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::utils::get_last_chain_binding_hir_id;
|
||||
use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
|
||||
|
|
@ -25,22 +26,6 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
|
|||
);
|
||||
}
|
||||
|
||||
fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
||||
for stmt in statements {
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
||||
&& let hir::def::Res::Local(local_hir_id) = path.res
|
||||
&& local_hir_id == hir_id
|
||||
{
|
||||
hir_id = local.pat.hir_id;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(hir_id)
|
||||
}
|
||||
|
||||
fn handle_qpath(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_parent_expr, path_to_local_id, usage};
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
|
@ -49,48 +46,6 @@ pub(super) fn derefs_to_slice<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_hint_if_single_char_arg(
|
||||
cx: &LateContext<'_>,
|
||||
arg: &Expr<'_>,
|
||||
applicability: &mut Applicability,
|
||||
ascii_only: bool,
|
||||
) -> Option<String> {
|
||||
if let ExprKind::Lit(lit) = &arg.kind
|
||||
&& let ast::LitKind::Str(r, style) = lit.node
|
||||
&& let string = r.as_str()
|
||||
&& let len = if ascii_only {
|
||||
string.len()
|
||||
} else {
|
||||
string.chars().count()
|
||||
}
|
||||
&& len == 1
|
||||
{
|
||||
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!(
|
||||
"'{}'",
|
||||
match ch {
|
||||
"'" => "\\'",
|
||||
r"\" => "\\\\",
|
||||
"\\\"" => "\"", // no need to escape `"` in `'"'`
|
||||
_ => ch,
|
||||
}
|
||||
);
|
||||
|
||||
Some(hint)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
|
||||
/// use of `CloneOrCopyVisitor`.
|
||||
pub(super) fn clone_or_copy_needed<'tcx>(
|
||||
|
|
@ -175,3 +130,19 @@ impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
|
|||
.any(|hir_id| path_to_local_id(expr, *hir_id))
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
||||
for stmt in statements {
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
||||
&& let rustc_hir::def::Res::Local(local_hir_id) = path.res
|
||||
&& local_hir_id == hir_id
|
||||
{
|
||||
hir_id = local.pat.hir_id;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(hir_id)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue