Merge commit 'b52fb5234c' into clippyup
This commit is contained in:
parent
854f751b26
commit
98bf99e2f8
689 changed files with 15493 additions and 688 deletions
|
|
@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
|
|||
hir_id: body.value.hir_id,
|
||||
};
|
||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||
let expr_ty = typeck_results.expr_ty(&body.value);
|
||||
let expr_ty = typeck_results.expr_ty(body.value);
|
||||
|
||||
if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
|
||||
let return_expr_span = match &body.value.kind {
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
|
|||
|
||||
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
||||
if let ItemKind::Fn(_, _, eid) = item.kind {
|
||||
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
|
||||
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
|
@ -483,7 +483,7 @@ fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
|
|||
|
||||
fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
|
||||
match item.kind {
|
||||
ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
|
||||
ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -492,7 +492,7 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
|
|||
match item.kind {
|
||||
TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
|
||||
TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
|
||||
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
|
||||
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
125
clippy_lints/src/bool_to_int_with_if.rs
Normal file
125
clippy_lints/src/bool_to_int_with_if.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
use rustc_ast::{ExprPrecedence, LitKind};
|
||||
use rustc_hir::{Block, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Instead of using an if statement to convert a bool to an int,
|
||||
/// this lint suggests using a `from()` function or an `as` coercion.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Coercion or `from()` is idiomatic way to convert bool to a number.
|
||||
/// Both methods are guaranteed to return 1 for true, and 0 for false.
|
||||
///
|
||||
/// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # let condition = false;
|
||||
/// if condition {
|
||||
/// 1_i64
|
||||
/// } else {
|
||||
/// 0
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let condition = false;
|
||||
/// i64::from(condition);
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
/// # let condition = false;
|
||||
/// condition as i64;
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub BOOL_TO_INT_WITH_IF,
|
||||
style,
|
||||
"using if to convert bool to int"
|
||||
}
|
||||
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
||||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||
if !expr.span.from_expansion() {
|
||||
check_if_else(ctx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||
if let ExprKind::If(check, then, Some(else_)) = expr.kind
|
||||
&& let Some(then_lit) = int_literal(then)
|
||||
&& let Some(else_lit) = int_literal(else_)
|
||||
&& check_int_literal_equals_val(then_lit, 1)
|
||||
&& check_int_literal_equals_val(else_lit, 0)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability);
|
||||
let snippet_with_braces = {
|
||||
let need_parens = should_have_parentheses(check);
|
||||
let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")};
|
||||
format!("{left_paren}{snippet}{right_paren}")
|
||||
};
|
||||
|
||||
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
|
||||
|
||||
let suggestion = {
|
||||
let wrap_in_curly = is_else_clause(ctx.tcx, expr);
|
||||
let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")};
|
||||
format!(
|
||||
"{left_curly}{ty}::from({snippet}){right_curly}"
|
||||
)
|
||||
}; // when used in else clause if statement should be wrapped in curly braces
|
||||
|
||||
span_lint_and_then(ctx,
|
||||
BOOL_TO_INT_WITH_IF,
|
||||
expr.span,
|
||||
"boolean to int conversion using if",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"replace with from",
|
||||
suggestion,
|
||||
applicability,
|
||||
);
|
||||
diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options"));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// If block contains only a int literal expression, return literal expression
|
||||
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
|
||||
if let ExprKind::Block(block, _) = expr.kind
|
||||
&& let Block {
|
||||
stmts: [], // Shouldn't lint if statements with side effects
|
||||
expr: Some(expr),
|
||||
..
|
||||
} = block
|
||||
&& let ExprKind::Lit(lit) = &expr.kind
|
||||
&& let LitKind::Int(_, _) = lit.node
|
||||
{
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool {
|
||||
if let ExprKind::Lit(lit) = &expr.kind
|
||||
&& let LitKind::Int(val, _) = lit.node
|
||||
&& val == expected_value
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool {
|
||||
check.precedence().order() < ExprPrecedence::Cast.order()
|
||||
}
|
||||
|
|
@ -69,7 +69,10 @@ struct NumericFallbackVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>) -> Self {
|
||||
Self { ty_bounds: vec![TyBound::Nothing], cx }
|
||||
Self {
|
||||
ty_bounds: vec![TyBound::Nothing],
|
||||
cx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether a passed literal has potential to cause fallback or not.
|
||||
|
|
@ -126,21 +129,19 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::MethodCall(_, receiver, args, _) => {
|
||||
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
|
||||
for (expr, bound) in
|
||||
iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs())
|
||||
{
|
||||
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
|
||||
self.ty_bounds.push(TyBound::Ty(*bound));
|
||||
self.visit_expr(expr);
|
||||
self.ty_bounds.pop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::Struct(_, fields, base) => {
|
||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||
|
|
@ -175,15 +176,15 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::Lit(lit) => {
|
||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||
self.check_lit(lit, ty, expr.hir_id);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
|
|
@ -197,7 +198,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
} else {
|
||||
self.ty_bounds.push(TyBound::Nothing);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => self.ty_bounds.push(TyBound::Nothing),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -830,25 +830,22 @@ fn walk_parents<'tcx>(
|
|||
)
|
||||
{
|
||||
return Some(Position::MethodReceiverRefImpl)
|
||||
} else {
|
||||
return Some(Position::MethodReceiver)
|
||||
}
|
||||
return Some(Position::MethodReceiver);
|
||||
}
|
||||
args.iter()
|
||||
.position(|arg| arg.hir_id == child_id)
|
||||
.map(|i| {
|
||||
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
|
||||
if let ty::Param(param_ty) = ty.kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
}
|
||||
})
|
||||
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
|
||||
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
|
||||
if let ty::Param(param_ty) = ty.kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
}
|
||||
})
|
||||
},
|
||||
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
|
||||
ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Known problems
|
||||
/// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
|
||||
/// in generic types and the user defined `impl` maybe is more generalized or
|
||||
/// in generic types and the user defined `impl` may be more generalized or
|
||||
/// specialized than what derive will produce. This lint can't detect the manual `impl`
|
||||
/// has exactly equal bounds, and therefore this lint is disabled for types with
|
||||
/// generic parameters.
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
typeck_results: cx.tcx.typeck(item.def_id),
|
||||
panic_span: None,
|
||||
};
|
||||
fpu.visit_expr(&body.value);
|
||||
fpu.visit_expr(body.value);
|
||||
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
|
||||
}
|
||||
},
|
||||
|
|
@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
|||
typeck_results: cx.tcx.typeck(item.def_id),
|
||||
panic_span: None,
|
||||
};
|
||||
fpu.visit_expr(&body.value);
|
||||
fpu.visit_expr(body.value);
|
||||
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
|
||||
}
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ fn lint_for_missing_headers<'tcx>(
|
|||
if let Some(future) = cx.tcx.lang_items().future_trait();
|
||||
let typeck = cx.tcx.typeck_body(body_id);
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let ret_ty = typeck.expr_ty(&body.value);
|
||||
let ret_ty = typeck.expr_ty(body.value);
|
||||
if implements_trait(cx, ret_ty, future, &[]);
|
||||
if let ty::Opaque(_, subs) = ret_ty.kind();
|
||||
if let Some(gen) = subs.types().next();
|
||||
|
|
@ -828,7 +828,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
|||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
|
||||
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
};
|
||||
if body.value.span.from_expansion() {
|
||||
if body.params.is_empty() {
|
||||
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
|
||||
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) {
|
||||
// replace `|| vec![]` with `Vec::new`
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
let closure_ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
if_chain!(
|
||||
if !is_adjusted(cx, &body.value);
|
||||
if !is_adjusted(cx, body.value);
|
||||
if let ExprKind::Call(callee, args) = body.value.kind;
|
||||
if let ExprKind::Path(_) = callee.kind;
|
||||
if check_inputs(cx, body.params, None, args);
|
||||
|
|
@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
);
|
||||
|
||||
if_chain!(
|
||||
if !is_adjusted(cx, &body.value);
|
||||
if !is_adjusted(cx, body.value);
|
||||
if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
|
||||
if check_inputs(cx, body.params, Some(receiver), args);
|
||||
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
|
||||
|
|
@ -206,8 +206,7 @@ fn check_inputs(
|
|||
_ => false,
|
||||
}
|
||||
};
|
||||
std::iter::zip(params, receiver.into_iter().chain(call_args.iter()))
|
||||
.all(|(param, arg)| check_inputs(param, arg))
|
||||
std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
|
||||
}
|
||||
|
||||
fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
|
|||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
|
||||
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
|
||||
{
|
||||
|
|
@ -110,7 +110,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
|
|||
typeck_results: cx.tcx.typeck(impl_item.id.def_id),
|
||||
result: Vec::new(),
|
||||
};
|
||||
fpu.visit_expr(&body.value);
|
||||
fpu.visit_expr(body.value);
|
||||
|
||||
// if we've found one, lint
|
||||
if !fpu.result.is_empty() {
|
||||
|
|
|
|||
|
|
@ -238,23 +238,23 @@ fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
|
|||
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
// Check receiver
|
||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
|
||||
let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
"exp"
|
||||
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
|
||||
Some("exp")
|
||||
} else if F32(2.0) == value || F64(2.0) == value {
|
||||
"exp2"
|
||||
Some("exp2")
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
None
|
||||
} {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"exponent for bases 2 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check argument
|
||||
|
|
|
|||
|
|
@ -272,6 +272,6 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo
|
|||
cx,
|
||||
mutates_static: false,
|
||||
};
|
||||
intravisit::walk_expr(&mut v, &body.value);
|
||||
intravisit::walk_expr(&mut v, body.value);
|
||||
v.mutates_static
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use rustc_lint::{LateContext, LintContext};
|
|||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
|
|
@ -17,11 +16,12 @@ use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
|
|||
fn result_err_ty<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
decl: &hir::FnDecl<'tcx>,
|
||||
id: hir::def_id::LocalDefId,
|
||||
item_span: Span,
|
||||
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
|
||||
if !in_external_macro(cx.sess(), item_span)
|
||||
&& let hir::FnRetTy::Return(hir_ty) = decl.output
|
||||
&& let ty = hir_ty_to_ty(cx.tcx, hir_ty)
|
||||
&& let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).output())
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Result)
|
||||
&& let ty::Adt(_, substs) = ty.kind()
|
||||
{
|
||||
|
|
@ -34,7 +34,7 @@ fn result_err_ty<'tcx>(
|
|||
|
||||
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
|
||||
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
|
||||
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
|
||||
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
|
||||
{
|
||||
if cx.access_levels.is_exported(item.def_id) {
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
|
@ -47,7 +47,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, l
|
|||
pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
|
||||
// Don't lint if method is a trait's implementation, we can't do anything about those
|
||||
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
|
||||
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
|
||||
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
|
||||
&& trait_ref_of_method(cx, item.def_id).is_none()
|
||||
{
|
||||
if cx.access_levels.is_exported(item.def_id) {
|
||||
|
|
@ -61,7 +61,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem
|
|||
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
|
||||
if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span) {
|
||||
if cx.access_levels.is_exported(item.def_id) {
|
||||
check_result_unit_err(cx, err_ty, fn_header_span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
|
|||
return;
|
||||
}
|
||||
|
||||
let res_ty = cx.typeck_results().expr_ty(&body.value);
|
||||
let res_ty = cx.typeck_results().expr_ty(body.value);
|
||||
if res_ty.is_unit() || res_ty.is_never() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
|
|||
None => return,
|
||||
}
|
||||
} else {
|
||||
&body.value
|
||||
body.value
|
||||
};
|
||||
lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
|
|||
MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
|
||||
Finite => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
span_lint(cx, lint, expr.span, msg);
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
if method.ident.name == sym!(flat_map) && args.len() == 1 {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
|
||||
let body = cx.tcx.hir().body(body);
|
||||
return is_infinite(cx, &body.value);
|
||||
return is_infinite(cx, body.value);
|
||||
}
|
||||
}
|
||||
Finite
|
||||
|
|
@ -230,8 +230,10 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
}
|
||||
}
|
||||
if method.ident.name == sym!(last) && args.is_empty() {
|
||||
let not_double_ended =
|
||||
cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator).map_or(false, |id| {
|
||||
let not_double_ended = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::DoubleEndedIterator)
|
||||
.map_or(false, |id| {
|
||||
!implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
|
||||
});
|
||||
if not_double_ended {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
//! lint when there is a large size difference between variants on an enum
|
||||
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{Adt, Ty};
|
||||
use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
|
|
@ -17,7 +16,7 @@ declare_clippy_lint! {
|
|||
/// `enum`s.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Enum size is bounded by the largest variant. Having a
|
||||
/// Enum size is bounded by the largest variant. Having one
|
||||
/// large variant can penalize the memory layout of that enum.
|
||||
///
|
||||
/// ### Known problems
|
||||
|
|
@ -33,8 +32,9 @@ declare_clippy_lint! {
|
|||
/// use case it may be possible to store the large data in an auxiliary
|
||||
/// structure (e.g. Arena or ECS).
|
||||
///
|
||||
/// The lint will ignore generic types if the layout depends on the
|
||||
/// generics, even if the size difference will be large anyway.
|
||||
/// The lint will ignore the impact of generic types to the type layout by
|
||||
/// assuming every type parameter is zero-sized. Depending on your use case,
|
||||
/// this may lead to a false positive.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
|
|
@ -83,6 +83,38 @@ struct VariantInfo {
|
|||
fields_size: Vec<FieldInfo>,
|
||||
}
|
||||
|
||||
fn variants_size<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
adt: AdtDef<'tcx>,
|
||||
subst: &'tcx List<GenericArg<'tcx>>,
|
||||
) -> Vec<VariantInfo> {
|
||||
let mut variants_size = adt
|
||||
.variants()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, variant)| {
|
||||
let mut fields_size = variant
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, f)| FieldInfo {
|
||||
ind: i,
|
||||
size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
|
||||
|
||||
VariantInfo {
|
||||
ind: i,
|
||||
size: fields_size.iter().map(|info| info.size).sum(),
|
||||
fields_size,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
|
||||
variants_size
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
|
|
@ -92,36 +124,14 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
|||
}
|
||||
if let ItemKind::Enum(ref def, _) = item.kind {
|
||||
let ty = cx.tcx.type_of(item.def_id);
|
||||
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
|
||||
let (adt, subst) = match ty.kind() {
|
||||
Adt(adt, subst) => (adt, subst),
|
||||
_ => panic!("already checked whether this is an enum"),
|
||||
};
|
||||
if adt.variants().len() <= 1 {
|
||||
return;
|
||||
}
|
||||
let mut variants_size: Vec<VariantInfo> = Vec::new();
|
||||
for (i, variant) in adt.variants().iter().enumerate() {
|
||||
let mut fields_size = Vec::new();
|
||||
for (i, f) in variant.fields.iter().enumerate() {
|
||||
let ty = cx.tcx.type_of(f.did);
|
||||
// don't lint variants which have a field of generic type.
|
||||
match cx.layout_of(ty) {
|
||||
Ok(l) => {
|
||||
let fsize = l.size.bytes();
|
||||
fields_size.push(FieldInfo { ind: i, size: fsize });
|
||||
},
|
||||
Err(_) => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
let size: u64 = fields_size.iter().map(|info| info.size).sum();
|
||||
|
||||
variants_size.push(VariantInfo {
|
||||
ind: i,
|
||||
size,
|
||||
fields_size,
|
||||
});
|
||||
}
|
||||
|
||||
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
|
||||
let variants_size = variants_size(cx, *adt, subst);
|
||||
|
||||
let mut difference = variants_size[0].size - variants_size[1].size;
|
||||
if difference > self.maximum_size_difference_allowed {
|
||||
|
|
@ -129,20 +139,30 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
|||
span_lint_and_then(
|
||||
cx,
|
||||
LARGE_ENUM_VARIANT,
|
||||
def.variants[variants_size[0].ind].span,
|
||||
item.span,
|
||||
"large size difference between variants",
|
||||
|diag| {
|
||||
diag.span_label(
|
||||
def.variants[variants_size[0].ind].span,
|
||||
&format!("this variant is {} bytes", variants_size[0].size),
|
||||
item.span,
|
||||
format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
|
||||
);
|
||||
diag.span_note(
|
||||
diag.span_label(
|
||||
def.variants[variants_size[0].ind].span,
|
||||
format!("the largest variant contains at least {} bytes", variants_size[0].size),
|
||||
);
|
||||
diag.span_label(
|
||||
def.variants[variants_size[1].ind].span,
|
||||
&format!("and the second-largest variant is {} bytes:", variants_size[1].size),
|
||||
&if variants_size[1].fields_size.is_empty() {
|
||||
"the second-largest variant carries no data at all".to_owned()
|
||||
} else {
|
||||
format!(
|
||||
"the second-largest variant contains at least {} bytes",
|
||||
variants_size[1].size
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let fields = def.variants[variants_size[0].ind].data.fields();
|
||||
variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
if is_copy(cx, ty) || maybe_copy(cx, ty) {
|
||||
diag.span_note(
|
||||
|
|
|
|||
|
|
@ -370,7 +370,8 @@ fn check_for_is_empty<'tcx>(
|
|||
}
|
||||
|
||||
fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
|
||||
if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
|
||||
if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
|
||||
{
|
||||
// check if we are in an is_empty() method
|
||||
if let Some(name) = get_item_name(cx, method) {
|
||||
if name.as_str() == "is_empty" {
|
||||
|
|
@ -378,12 +379,23 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
|
|||
}
|
||||
}
|
||||
|
||||
check_len(cx, span, method_path.ident.name, receiver, args, &lit.node, op, compare_to);
|
||||
check_len(
|
||||
cx,
|
||||
span,
|
||||
method_path.ident.name,
|
||||
receiver,
|
||||
args,
|
||||
&lit.node,
|
||||
op,
|
||||
compare_to,
|
||||
);
|
||||
} else {
|
||||
check_empty_expr(cx, span, method, lit, op);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(flip1995): Figure out how to reduce the number of arguments
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_len(
|
||||
cx: &LateContext<'_>,
|
||||
span: Span,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ store.register_lints(&[
|
|||
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
|
||||
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
|
||||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
bool_to_int_with_if::BOOL_TO_INT_WITH_IF,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
|
|
@ -436,7 +437,7 @@ store.register_lints(&[
|
|||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ARITHMETIC,
|
||||
operators::ARITHMETIC_SIDE_EFFECTS,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
operators::BAD_BIT_MASK,
|
||||
operators::CMP_NAN,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
|
||||
LintId::of(module_style::MOD_MODULE_FILES),
|
||||
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
|
||||
LintId::of(operators::ARITHMETIC),
|
||||
LintId::of(operators::ARITHMETIC_SIDE_EFFECTS),
|
||||
LintId::of(operators::FLOAT_ARITHMETIC),
|
||||
LintId::of(operators::FLOAT_CMP_CONST),
|
||||
LintId::of(operators::INTEGER_ARITHMETIC),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ mod attrs;
|
|||
mod await_holding_invalid;
|
||||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod bool_to_int_with_if;
|
||||
mod booleans;
|
||||
mod borrow_deref_ref;
|
||||
mod cargo;
|
||||
|
|
@ -523,7 +524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
#[cfg(feature = "internal")]
|
||||
{
|
||||
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -544,8 +545,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
|
||||
}
|
||||
|
||||
let arithmetic_allowed = conf.arithmetic_allowed.clone();
|
||||
store.register_late_pass(move |_| Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
|
||||
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
|
||||
arithmetic_side_effects_allowed.clone(),
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
||||
store.register_late_pass(|_| Box::new(utils::author::Author));
|
||||
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
||||
|
|
@ -901,6 +906,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
|
||||
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
|
||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
||||
store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ fn could_use_elision<'tcx>(
|
|||
let mut checker = BodyLifetimeChecker {
|
||||
lifetimes_used_in_body: false,
|
||||
};
|
||||
checker.visit_expr(&body.value);
|
||||
checker.visit_expr(body.value);
|
||||
if checker.lifetimes_used_in_body {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
|||
},
|
||||
ExprKind::Closure(&Closure { body, .. }) => {
|
||||
let body = self.cx.tcx.hir().body(body);
|
||||
self.visit_expr(&body.value);
|
||||
self.visit_expr(body.value);
|
||||
},
|
||||
_ => walk_expr(self, expr),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
|
|||
after_loop: false,
|
||||
used_iter: false,
|
||||
};
|
||||
v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
|
||||
v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
|
||||
v.used_iter
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
|||
arm.pat.span,
|
||||
&format!("`Err({})` matches all errors", ident_bind_name),
|
||||
None,
|
||||
"match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
|
||||
"match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,12 +203,8 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>,
|
|||
let left_pos = left_pos.as_opt_usize();
|
||||
let right_pos = right_pos.as_opt_usize();
|
||||
let len = max(
|
||||
left_in.len() + {
|
||||
if left_pos.is_some() { 1 } else { 0 }
|
||||
},
|
||||
right_in.len() + {
|
||||
if right_pos.is_some() { 1 } else { 0 }
|
||||
},
|
||||
left_in.len() + usize::from(left_pos.is_some()),
|
||||
right_in.len() + usize::from(right_pos.is_some()),
|
||||
);
|
||||
let mut left_pos = left_pos.unwrap_or(usize::MAX);
|
||||
let mut right_pos = right_pos.unwrap_or(usize::MAX);
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap {
|
|||
match arg.kind {
|
||||
hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
||||
if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) {
|
||||
true
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ pub(super) fn check(
|
|||
if Some(id) == cx.tcx.lang_items().option_some_variant();
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0].0).peel_refs();
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs();
|
||||
|
||||
if *self_ty.kind() != ty::Str {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ pub(super) fn check(
|
|||
receiver: &Expr<'_>,
|
||||
args: &[Expr<'_>],
|
||||
) {
|
||||
let arg = if method_name == sym::clone && args.is_empty() { receiver } else { return };
|
||||
let arg = if method_name == sym::clone && args.is_empty() {
|
||||
receiver
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
|
|||
},
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||
let body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&body.value);
|
||||
let closure_expr = peel_blocks(body.value);
|
||||
let arg_id = body.params[0].pat.hir_id;
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
|
|||
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::NONE, .., name, None
|
||||
|
|
|
|||
|
|
@ -825,8 +825,9 @@ declare_clippy_lint! {
|
|||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
|
||||
/// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
|
||||
/// `unwrap_or_default` instead.
|
||||
/// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
|
||||
/// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
|
||||
/// etc. instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The function will always be called and potentially
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
|
|
@ -11,6 +11,7 @@ use super::MUT_MUTEX_LOCK;
|
|||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
|
||||
if_chain! {
|
||||
if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut));
|
||||
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
|
|||
let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
||||
|
||||
// Only proceed if this is a call on some object of type std::fs::OpenOptions
|
||||
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 1 {
|
||||
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
|
||||
let argument_option = match arguments[0].kind {
|
||||
ExprKind::Lit(ref span) => {
|
||||
if let Spanned {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
|
|||
}),
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
||||
match &closure_expr.kind {
|
||||
hir::ExprKind::MethodCall(_, receiver, [], _) => {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
|
|||
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
|
||||
let arg_snippet = snippet(cx, fn_decl_span, "..");
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
|
||||
if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
|
||||
if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
|
||||
if Some(id) == cx.tcx.lang_items().option_some_variant();
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ pub(super) fn check<'tcx>(
|
|||
receiver: &'tcx hir::Expr<'_>,
|
||||
args: &'tcx [hir::Expr<'_>],
|
||||
) {
|
||||
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
|
||||
/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
|
||||
/// `or_insert(T::new())` or `or_insert(T::default())`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_unwrap_or_default(
|
||||
cx: &LateContext<'_>,
|
||||
|
|
@ -43,7 +44,11 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if_chain! {
|
||||
if !or_has_args;
|
||||
if name == "unwrap_or";
|
||||
if let Some(sugg) = match name {
|
||||
"unwrap_or" => Some("unwrap_or_default"),
|
||||
"or_insert" => Some("or_default"),
|
||||
_ => None,
|
||||
};
|
||||
if let hir::ExprKind::Path(ref qpath) = fun.kind;
|
||||
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
|
||||
let path = last_path_segment(qpath).ident.name;
|
||||
|
|
@ -59,7 +64,7 @@ pub(super) fn check<'tcx>(
|
|||
method_span.with_hi(span.hi()),
|
||||
&format!("use of `{}` followed by a call to `{}`", name, path),
|
||||
"try this",
|
||||
"unwrap_or_default()".to_string(),
|
||||
format!("{}()", sugg),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
|
|
@ -83,7 +88,7 @@ pub(super) fn check<'tcx>(
|
|||
fun_span: Option<Span>,
|
||||
) {
|
||||
// (path, fn_has_argument, methods, suffix)
|
||||
static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
|
||||
const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
|
||||
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hi
|
|||
if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
|
||||
if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
|
||||
if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
|
||||
then {
|
||||
if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
|
||||
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
|
||||
// A variable is used mutably inside of the closure. Suppress the lint.
|
||||
if !map_mutated_vars.is_empty() {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -21,14 +21,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
|
|||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
|
||||
let body = cx.tcx.hir().body(body);
|
||||
let arg_id = body.params[0].pat.hir_id;
|
||||
let mutates_arg =
|
||||
mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
|
||||
let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, &body.value);
|
||||
let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
|
||||
let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
|
||||
|
||||
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
|
||||
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
|
||||
|
||||
let mut return_visitor = ReturnVisitor::new(cx, arg_id);
|
||||
return_visitor.visit_expr(&body.value);
|
||||
return_visitor.visit_expr(body.value);
|
||||
found_mapping |= return_visitor.found_mapping;
|
||||
found_filtering |= return_visitor.found_filtering;
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
|
|||
let sugg = if !found_filtering {
|
||||
if name == "filter_map" { "map" } else { "map(..).next()" }
|
||||
} else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
|
||||
match cx.typeck_results().expr_ty(&body.value).kind() {
|
||||
match cx.typeck_results().expr_ty(body.value).kind() {
|
||||
ty::Adt(adt, subst)
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ pub(super) fn check(
|
|||
// Extract the body of the closure passed to fold
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
||||
// Check if the closure body is of the form `acc <op> some_expr(x)`
|
||||
if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
|
|||
] = &closure_body.params;
|
||||
if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind;
|
||||
if method_path.ident.name == sym::cmp;
|
||||
if is_trait_method(cx, &closure_body.value, sym::Ord);
|
||||
if is_trait_method(cx, closure_body.value, sym::Ord);
|
||||
then {
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
|
||||
left_expr,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
use super::implicit_clone::is_clone_like;
|
||||
use super::unnecessary_iter_cloned::{self, is_into_iter};
|
||||
use crate::rustc_middle::ty::Subst;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{
|
||||
get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
|
||||
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
|
||||
use clippy_utils::visitors::find_all_ret_expressions;
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
|
||||
use rustc_middle::ty::EarlyBinder;
|
||||
use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
|
||||
use rustc_typeck::check::{FnCtxt, Inherited};
|
||||
use std::cmp::max;
|
||||
|
||||
use super::UNNECESSARY_TO_OWNED;
|
||||
|
|
@ -34,7 +38,7 @@ pub fn check<'tcx>(
|
|||
then {
|
||||
if is_cloned_or_copied(cx, method_name, method_def_id) {
|
||||
unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
|
||||
} else if is_to_owned_like(cx, method_name, method_def_id) {
|
||||
} else if is_to_owned_like(cx, expr, method_name, method_def_id) {
|
||||
// At this point, we know the call is of a `to_owned`-like function. The functions
|
||||
// `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
|
||||
// based on its context, that is, whether it is a referent in an `AddrOf` expression, an
|
||||
|
|
@ -246,17 +250,12 @@ fn check_other_call_arg<'tcx>(
|
|||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
|
||||
if let Some((callee_def_id, call_substs, call_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
|
||||
if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
|
||||
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
|
||||
let index = if let Some(call_receiver) = call_receiver {
|
||||
std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
|
||||
} else {
|
||||
call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
|
||||
};
|
||||
if let Some(i) = index;
|
||||
if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
|
||||
if let Some(input) = fn_sig.inputs().get(i);
|
||||
let (input, n_refs) = peel_mid_ty_refs(*input);
|
||||
if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
|
||||
if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input);
|
||||
if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
|
||||
if let [trait_predicate] = trait_predicates
|
||||
.iter()
|
||||
|
|
@ -264,52 +263,13 @@ fn check_other_call_arg<'tcx>(
|
|||
.collect::<Vec<_>>()[..];
|
||||
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
|
||||
if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
|
||||
if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
// If the callee has type parameters, they could appear in `projection_predicate.ty` or the
|
||||
// types of `trait_predicate.trait_ref.substs`.
|
||||
if if trait_predicate.def_id() == deref_trait_id {
|
||||
if let [projection_predicate] = projection_predicates[..] {
|
||||
let normalized_ty =
|
||||
cx.tcx
|
||||
.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
|
||||
implements_trait(cx, receiver_ty, deref_trait_id, &[])
|
||||
&& get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
|
||||
.map_or(false, |ty| ty::TermKind::Ty(ty) == normalized_ty.unpack())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else if trait_predicate.def_id() == as_ref_trait_id {
|
||||
let composed_substs = compose_substs(
|
||||
cx,
|
||||
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
|
||||
call_substs,
|
||||
);
|
||||
// if `expr` is a `String` and generic target is [u8], skip
|
||||
// (https://github.com/rust-lang/rust-clippy/issues/9317).
|
||||
if let [subst] = composed_substs[..]
|
||||
&& let GenericArgKind::Type(arg_ty) = subst.unpack()
|
||||
&& arg_ty.is_slice()
|
||||
&& let inner_ty = arg_ty.builtin_index().unwrap()
|
||||
&& let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
|
||||
&& let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
|
||||
&& is_type_diagnostic_item(cx, self_ty, sym::String) {
|
||||
false
|
||||
} else {
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if can_change_type(cx, maybe_arg, receiver_ty);
|
||||
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
|
||||
// `Target = T`.
|
||||
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
|
||||
let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
|
||||
// If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
|
||||
// `T` must not be instantiated with a reference
|
||||
// (https://github.com/rust-lang/rust-clippy/issues/8507).
|
||||
if (n_refs == 0 && !receiver_ty.is_ref())
|
||||
|| trait_predicate.def_id() != as_ref_trait_id
|
||||
|| !fn_sig.output().contains(input);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
|
|
@ -359,11 +319,11 @@ fn get_callee_substs_and_args<'tcx>(
|
|||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(_, recv, args, _) = expr.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
then {
|
||||
let substs = cx.typeck_results().node_substs(expr.hir_id);
|
||||
return Some((method_def_id, substs, Some(receiver), args));
|
||||
return Some((method_def_id, substs, Some(recv), args));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
@ -395,22 +355,103 @@ fn get_input_traits_and_projections<'tcx>(
|
|||
(trait_predicates, projection_predicates)
|
||||
}
|
||||
|
||||
/// Composes two substitutions by applying the latter to the types of the former.
|
||||
fn compose_substs<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
left: &[GenericArg<'tcx>],
|
||||
right: SubstsRef<'tcx>,
|
||||
) -> Vec<GenericArg<'tcx>> {
|
||||
left.iter()
|
||||
.map(|arg| {
|
||||
if let GenericArgKind::Type(arg_ty) = arg.unpack() {
|
||||
let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
|
||||
GenericArg::from(normalized_ty)
|
||||
} else {
|
||||
*arg
|
||||
fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
|
||||
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
match node {
|
||||
Node::Stmt(_) => return true,
|
||||
Node::Block(..) => continue,
|
||||
Node::Item(item) => {
|
||||
if let ItemKind::Fn(_, _, body_id) = &item.kind
|
||||
&& let output_ty = return_ty(cx, item.hir_id())
|
||||
&& let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
|
||||
&& Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
|
||||
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id());
|
||||
fn_ctxt.can_coerce(ty, output_ty)
|
||||
}) {
|
||||
if has_lifetime(output_ty) && has_lifetime(ty) {
|
||||
return false;
|
||||
}
|
||||
let body = cx.tcx.hir().body(*body_id);
|
||||
let body_expr = &body.value;
|
||||
let mut count = 0;
|
||||
return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
Node::Expr(parent_expr) => {
|
||||
if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
|
||||
{
|
||||
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
|
||||
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
|
||||
&& let Some(param_ty) = fn_sig.inputs().get(arg_index)
|
||||
&& let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
|
||||
{
|
||||
if fn_sig
|
||||
.inputs()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| *i != arg_index)
|
||||
.any(|(_, ty)| ty.contains(*param_ty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
|
||||
.caller_bounds().iter().filter(|predicate| {
|
||||
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == *param_ty {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
let new_subst = cx.tcx.mk_substs(
|
||||
call_substs.iter()
|
||||
.enumerate()
|
||||
.map(|(i, t)|
|
||||
if i == (*param_index as usize) {
|
||||
GenericArg::from(ty)
|
||||
} else {
|
||||
t
|
||||
}));
|
||||
|
||||
if trait_predicates.any(|predicate| {
|
||||
let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
|
||||
let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
|
||||
!cx.tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let output_ty = fn_sig.output();
|
||||
if output_ty.contains(*param_ty) {
|
||||
if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions(
|
||||
new_subst, cx.param_env, output_ty) {
|
||||
expr = parent_expr;
|
||||
ty = new_ty;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if let ExprKind::Block(..) = parent_expr.kind {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn has_lifetime(ty: Ty<'_>) -> bool {
|
||||
ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
|
||||
}
|
||||
|
||||
/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
|
||||
|
|
@ -421,10 +462,10 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
|
|||
|
||||
/// Returns true if the named method can be used to convert the receiver to its "owned"
|
||||
/// representation.
|
||||
fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||
is_clone_like(cx, method_name.as_str(), method_def_id)
|
||||
|| is_cow_into_owned(cx, method_name, method_def_id)
|
||||
|| is_to_string(cx, method_name, method_def_id)
|
||||
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
|
||||
}
|
||||
|
||||
/// Returns true if the named method is `Cow::into_owned`.
|
||||
|
|
@ -432,7 +473,27 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D
|
|||
method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
|
||||
}
|
||||
|
||||
/// Returns true if the named method is `ToString::to_string`.
|
||||
fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
|
||||
method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)
|
||||
/// Returns true if the named method is `ToString::to_string` and it's called on a type that
|
||||
/// is string-like i.e. implements `AsRef<str>` or `Deref<str>`.
|
||||
fn is_to_string_on_string_like<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
call_expr: &'a Expr<'a>,
|
||||
method_name: Symbol,
|
||||
method_def_id: DefId,
|
||||
) -> bool {
|
||||
if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
|
||||
&& let [generic_arg] = substs.as_slice()
|
||||
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
|
||||
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
|
||||
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
|
||||
&& (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) ||
|
||||
implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ use clippy_utils::{
|
|||
diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
|
||||
ty::is_type_diagnostic_item,
|
||||
};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{sym, symbol};
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
|
@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if_chain! {
|
||||
if is_option || is_result;
|
||||
if is_default_equivalent_call(cx, u_arg);
|
||||
if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
|
|
@ -44,3 +45,22 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
|
||||
let body = cx.tcx.hir().body(body);
|
||||
|
||||
if body.params.is_empty()
|
||||
&& let hir::Expr{ kind, .. } = &body.value
|
||||
&& let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
|
||||
&& ident == &symbol::Ident::from_str("to_string")
|
||||
&& let hir::Expr{ kind, .. } = self_arg
|
||||
&& let hir::ExprKind::Lit(lit) = kind
|
||||
&& let LitKind::Str(symbol::kw::Empty, _) = lit.node
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{match_trait_method, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -84,19 +83,16 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
|
|||
}
|
||||
},
|
||||
ExprKind::MethodCall(path, receiver, args @ [_], _) => {
|
||||
if_chain! {
|
||||
if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
|
||||
then {
|
||||
if path.ident.name == sym!(max) {
|
||||
fetch_const(cx, Some(receiver), args, MinMax::Max)
|
||||
} else if path.ident.name == sym!(min) {
|
||||
fetch_const(cx, Some(receiver), args, MinMax::Min)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) {
|
||||
if path.ident.name == sym!(max) {
|
||||
fetch_const(cx, Some(receiver), args, MinMax::Max)
|
||||
} else if path.ident.name == sym!(min) {
|
||||
fetch_const(cx, Some(receiver), args, MinMax::Min)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
|
|
@ -109,18 +105,18 @@ fn fetch_const<'a>(
|
|||
args: &'a [Expr<'a>],
|
||||
m: MinMax,
|
||||
) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
|
||||
let mut args = receiver.into_iter().chain(args.into_iter());
|
||||
let arg0 = args.next()?;
|
||||
let arg1 = args.next()?;
|
||||
let mut args = receiver.into_iter().chain(args);
|
||||
let first_arg = args.next()?;
|
||||
let second_arg = args.next()?;
|
||||
if args.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
constant_simple(cx, cx.typeck_results(), arg0).map_or_else(
|
||||
|| constant_simple(cx, cx.typeck_results(), arg1).map(|c| (m, c, arg0)),
|
||||
constant_simple(cx, cx.typeck_results(), first_arg).map_or_else(
|
||||
|| constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)),
|
||||
|c| {
|
||||
if constant_simple(cx, cx.typeck_results(), arg1).is_none() {
|
||||
if constant_simple(cx, cx.typeck_results(), second_arg).is_none() {
|
||||
// otherwise ignore
|
||||
Some((m, c, arg1))
|
||||
Some((m, c, second_arg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
if let ExprKind::Block(..) = body.value.kind;
|
||||
then {
|
||||
let mut ret_collector = RetCollector::default();
|
||||
ret_collector.visit_expr(&body.value);
|
||||
ret_collector.visit_expr(body.value);
|
||||
|
||||
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
|
||||
if ret_collector.ret_in_loop {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_middle::ty::{self, ConstKind};
|
|||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{kw, Ident};
|
||||
use rustc_span::Span;
|
||||
use std::iter;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -234,11 +235,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
|||
})) => (
|
||||
def_id.to_def_id(),
|
||||
FnKind::TraitFn,
|
||||
if sig.decl.implicit_self.has_implicit_self() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
usize::from(sig.decl.implicit_self.has_implicit_self()),
|
||||
),
|
||||
Some(Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(ref sig, _),
|
||||
|
|
@ -253,11 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
|||
(
|
||||
trait_item_id,
|
||||
FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
|
||||
if sig.decl.implicit_self.has_implicit_self() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
},
|
||||
usize::from(sig.decl.implicit_self.has_implicit_self()),
|
||||
)
|
||||
} else {
|
||||
(def_id.to_def_id(), FnKind::Fn, 0)
|
||||
|
|
@ -310,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
|||
&& has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
|
||||
}) =>
|
||||
{
|
||||
if let Some(idx) = std::iter::once(receiver).chain(args.iter()).position(|arg| arg.hir_id == child_id) {
|
||||
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
|
||||
param.uses.push(Usage::new(span, idx));
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
#![allow(
|
||||
// False positive
|
||||
clippy::match_same_arms
|
||||
)]
|
||||
|
||||
use super::ARITHMETIC;
|
||||
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Arithmetic {
|
||||
allowed: FxHashSet<String>,
|
||||
// Used to check whether expressions are constants, such as in enum discriminants and consts
|
||||
const_span: Option<Span>,
|
||||
expr_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Arithmetic => [ARITHMETIC]);
|
||||
|
||||
impl Arithmetic {
|
||||
#[must_use]
|
||||
pub fn new(mut allowed: FxHashSet<String>) -> Self {
|
||||
allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
|
||||
Self {
|
||||
allowed,
|
||||
const_span: None,
|
||||
expr_span: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given `expr` has any of the inner `allowed` elements.
|
||||
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
self.allowed.contains(
|
||||
cx.typeck_results()
|
||||
.expr_ty(expr)
|
||||
.to_string()
|
||||
.split('<')
|
||||
.next()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Arithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.expr_span.is_some() {
|
||||
return;
|
||||
}
|
||||
if let Some(span) = self.const_span && span.contains(expr.span) {
|
||||
return;
|
||||
}
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
let (
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Sub
|
||||
| hir::BinOpKind::Mul
|
||||
| hir::BinOpKind::Div
|
||||
| hir::BinOpKind::Rem
|
||||
| hir::BinOpKind::Shl
|
||||
| hir::BinOpKind::Shr
|
||||
) = op.node else {
|
||||
return;
|
||||
};
|
||||
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
|
||||
return;
|
||||
}
|
||||
self.issue_lint(cx, expr);
|
||||
},
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
|
||||
// CTFE already takes care of things like `-1` that do not overflow.
|
||||
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
|
||||
self.issue_lint(cx, expr);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
|
||||
match cx.tcx.hir().body_owner_kind(body_owner) {
|
||||
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => {
|
||||
let body_span = cx.tcx.def_span(body_owner);
|
||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
self.const_span = Some(body_span);
|
||||
},
|
||||
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||
let body_span = cx.tcx.hir().span(body_owner);
|
||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
self.const_span = None;
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if Some(expr.span) == self.expr_span {
|
||||
self.expr_span = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
173
clippy_lints/src/operators/arithmetic_side_effects.rs
Normal file
173
clippy_lints/src/operators/arithmetic_side_effects.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
#![allow(
|
||||
// False positive
|
||||
clippy::match_same_arms
|
||||
)]
|
||||
|
||||
use super::ARITHMETIC_SIDE_EFFECTS;
|
||||
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
|
||||
use rustc_ast as ast;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
|
||||
const HARD_CODED_ALLOWED: &[&str] = &[
|
||||
"f32",
|
||||
"f64",
|
||||
"std::num::Saturating",
|
||||
"std::string::String",
|
||||
"std::num::Wrapping",
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArithmeticSideEffects {
|
||||
allowed: FxHashSet<String>,
|
||||
// Used to check whether expressions are constants, such as in enum discriminants and consts
|
||||
const_span: Option<Span>,
|
||||
expr_span: Option<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
|
||||
|
||||
impl ArithmeticSideEffects {
|
||||
#[must_use]
|
||||
pub fn new(mut allowed: FxHashSet<String>) -> Self {
|
||||
allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
|
||||
Self {
|
||||
allowed,
|
||||
const_span: None,
|
||||
expr_span: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
|
||||
/// won't overflow.
|
||||
fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
|
||||
if !Self::is_literal_integer(rhs, rhs_refs) {
|
||||
return false;
|
||||
}
|
||||
if let hir::BinOpKind::Div | hir::BinOpKind::Mul = op.node
|
||||
&& let hir::ExprKind::Lit(ref lit) = rhs.kind
|
||||
&& let ast::LitKind::Int(1, _) = lit.node
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
|
||||
/// already handled by the CTFE.
|
||||
fn has_valid_bin_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
|
||||
Self::is_literal_integer(lhs, lhs_refs) && Self::is_literal_integer(rhs, rhs_refs)
|
||||
}
|
||||
|
||||
/// Checks if the given `expr` has any of the inner `allowed` elements.
|
||||
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
self.allowed.contains(
|
||||
cx.typeck_results()
|
||||
.expr_ty(expr)
|
||||
.to_string()
|
||||
.split('<')
|
||||
.next()
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
|
||||
fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool {
|
||||
let is_integral = expr_refs.is_integral();
|
||||
let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_));
|
||||
is_integral && is_literal
|
||||
}
|
||||
|
||||
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, "arithmetic detected");
|
||||
self.expr_span = Some(expr.span);
|
||||
}
|
||||
|
||||
/// Manages when the lint should be triggered. Operations in constant environments, hard coded
|
||||
/// types, custom allowed types and non-constant operations that won't overflow are ignored.
|
||||
fn manage_bin_ops(
|
||||
&mut self,
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
op: &Spanned<hir::BinOpKind>,
|
||||
lhs: &hir::Expr<'_>,
|
||||
rhs: &hir::Expr<'_>,
|
||||
) {
|
||||
if constant_simple(cx, cx.typeck_results(), expr).is_some() {
|
||||
return;
|
||||
}
|
||||
if !matches!(
|
||||
op.node,
|
||||
hir::BinOpKind::Add
|
||||
| hir::BinOpKind::Sub
|
||||
| hir::BinOpKind::Mul
|
||||
| hir::BinOpKind::Div
|
||||
| hir::BinOpKind::Rem
|
||||
| hir::BinOpKind::Shl
|
||||
| hir::BinOpKind::Shr
|
||||
) {
|
||||
return;
|
||||
};
|
||||
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
|
||||
return;
|
||||
}
|
||||
let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs();
|
||||
let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs();
|
||||
let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs);
|
||||
if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) {
|
||||
return;
|
||||
}
|
||||
self.issue_lint(cx, expr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) {
|
||||
return;
|
||||
}
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
self.manage_bin_ops(cx, expr, op, lhs, rhs);
|
||||
},
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
|
||||
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
|
||||
self.issue_lint(cx, expr);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||
let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
|
||||
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
|
||||
if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
|
||||
let body_span = cx.tcx.hir().span_with_body(body_owner);
|
||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
self.const_span = Some(body_span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||
let body_span = cx.tcx.hir().span(body_owner);
|
||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
||||
return;
|
||||
}
|
||||
self.const_span = None;
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if Some(expr.span) == self.expr_span {
|
||||
self.expr_span = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ mod ptr_eq;
|
|||
mod self_assignment;
|
||||
mod verbose_bit_mask;
|
||||
|
||||
pub(crate) mod arithmetic;
|
||||
pub(crate) mod arithmetic_side_effects;
|
||||
|
||||
use rustc_hir::{Body, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -61,25 +61,29 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for any kind of arithmetic operation of any type.
|
||||
/// Checks any kind of arithmetic operation of any type.
|
||||
///
|
||||
/// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
|
||||
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
|
||||
/// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered
|
||||
/// away.
|
||||
/// or can panic (`/`, `%`).
|
||||
///
|
||||
/// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant
|
||||
/// environments, allowed types and non-constant operations that won't overflow are ignored.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Integer overflow will trigger a panic in debug builds or will wrap in
|
||||
/// release mode. Division by zero will cause a panic in either mode. In some applications one
|
||||
/// wants explicitly checked, wrapping or saturating arithmetic.
|
||||
/// For integers, overflow will trigger a panic in debug builds or wrap the result in
|
||||
/// release mode; division by zero will cause a panic in either mode. As a result, it is
|
||||
/// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
|
||||
///
|
||||
/// #### Example
|
||||
/// ```rust
|
||||
/// # let a = 0;
|
||||
/// a + 1;
|
||||
/// // `n` can be any number, including `i32::MAX`.
|
||||
/// fn foo(n: i32) -> i32 {
|
||||
/// n + 1
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Third-party types also tend to overflow.
|
||||
/// Third-party types can also overflow or present unwanted side-effects.
|
||||
///
|
||||
/// #### Example
|
||||
/// ```ignore,rust
|
||||
|
|
@ -88,11 +92,11 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
///
|
||||
/// ### Allowed types
|
||||
/// Custom allowed types can be specified through the "arithmetic-allowed" filter.
|
||||
/// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub ARITHMETIC,
|
||||
pub ARITHMETIC_SIDE_EFFECTS,
|
||||
restriction,
|
||||
"any arithmetic expression that could overflow or panic"
|
||||
"any arithmetic expression that can cause side effects like overflows or panics"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -785,7 +789,7 @@ pub struct Operators {
|
|||
}
|
||||
impl_lint_pass!(Operators => [
|
||||
ABSURD_EXTREME_COMPARISONS,
|
||||
ARITHMETIC,
|
||||
ARITHMETIC_SIDE_EFFECTS,
|
||||
INTEGER_ARITHMETIC,
|
||||
FLOAT_ARITHMETIC,
|
||||
ASSIGN_OP_PATTERN,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
|
|||
}
|
||||
true
|
||||
})
|
||||
.visit_expr(&body.value);
|
||||
.visit_expr(body.value);
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
|
|||
if let Some(args) = args
|
||||
&& !args.is_empty()
|
||||
&& body.map_or(true, |body| {
|
||||
sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
|
||||
sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
|
|
@ -664,7 +664,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
|
|||
results,
|
||||
skip_count,
|
||||
};
|
||||
v.visit_expr(&body.value);
|
||||
v.visit_expr(body.value);
|
||||
v.results
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
|||
// exclusive range plus one: `x..(y+1)`
|
||||
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if expr.span.can_be_used_for_suggestions();
|
||||
if let Some(higher::Range {
|
||||
start,
|
||||
end: Some(end),
|
||||
|
|
@ -357,14 +358,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
}) = higher::Range::hir(expr);
|
||||
if let Some(y) = y_plus_one(cx, end);
|
||||
then {
|
||||
let span = if expr.span.from_expansion() {
|
||||
expr.span
|
||||
.ctxt()
|
||||
.outer_expn_data()
|
||||
.call_site
|
||||
} else {
|
||||
expr.span
|
||||
};
|
||||
let span = expr.span;
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
RANGE_PLUS_ONE,
|
||||
|
|
@ -399,6 +393,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
// inclusive range minus one: `x..=(y-1)`
|
||||
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if expr.span.can_be_used_for_suggestions();
|
||||
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
|
||||
if let Some(y) = y_minus_one(cx, end);
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
|||
} else {
|
||||
RetReplacement::Empty
|
||||
};
|
||||
check_final_expr(cx, &body.value, Some(body.value.span), replacement);
|
||||
check_final_expr(cx, body.value, Some(body.value.span), replacement);
|
||||
},
|
||||
FnKind::ItemFn(..) | FnKind::Method(..) => {
|
||||
if let ExprKind::Block(block, _) = body.value.kind {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||
]
|
||||
.iter()
|
||||
.find(|&(ts, _)| ts.iter().any(|&t| Ok(trait_id) == cx.tcx.lang_items().require(t)));
|
||||
if count_binops(&body.value) == 1;
|
||||
if count_binops(body.value) == 1;
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
|
|||
let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
|
||||
for (i, trait_name) in arg_indices {
|
||||
if i < args.len() {
|
||||
match check_arg(cx, &args[i]) {
|
||||
match check_arg(cx, args[i]) {
|
||||
Some((span, None)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
|
||||
lint_unit_args(cx, expr, &args_to_recover.as_slice());
|
||||
lint_unit_args(cx, expr, args_to_recover.as_slice());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
|
||||
// Check if all return expression respect the following condition and collect them.
|
||||
let mut suggs = Vec::new();
|
||||
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
|
||||
let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| {
|
||||
if_chain! {
|
||||
if !ret_expr.span.from_expansion();
|
||||
// Check if a function call.
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ fn collect_unwrap_info<'tcx>(
|
|||
let name = method_name.ident.as_str();
|
||||
if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name);
|
||||
then {
|
||||
assert!(args.len() == 0);
|
||||
assert!(args.is_empty());
|
||||
let unwrappable = match name {
|
||||
"is_some" | "is_ok" => true,
|
||||
"is_err" | "is_none" => false,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
// check for `expect`
|
||||
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
|
||||
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
|
||||
{
|
||||
|
|
@ -93,7 +93,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
|||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
|
||||
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
|
||||
{
|
||||
|
|
@ -114,7 +114,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc
|
|||
typeck_results: cx.tcx.typeck(impl_item.def_id),
|
||||
result: Vec::new(),
|
||||
};
|
||||
fpu.visit_expr(&body.value);
|
||||
fpu.visit_expr(body.value);
|
||||
|
||||
// if we've found one, lint
|
||||
if !fpu.result.is_empty() {
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
|
|||
let hir = cx.tcx.hir();
|
||||
if let Some(body_id) = hir.maybe_body_owned_by(hir_id.expect_owner()) {
|
||||
check_node(cx, hir_id, |v| {
|
||||
v.expr(&v.bind("expr", &hir.body(body_id).value));
|
||||
v.expr(&v.bind("expr", hir.body(body_id).value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ define_Conf! {
|
|||
/// Lint: Arithmetic.
|
||||
///
|
||||
/// Suppress checking of the passed type names.
|
||||
(arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
|
||||
(arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
|
||||
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
|
||||
///
|
||||
/// Suppress lints whenever the suggested change would cause breakage for other crates.
|
||||
|
|
|
|||
|
|
@ -505,7 +505,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
|||
.hir_id(),
|
||||
),
|
||||
);
|
||||
collector.visit_expr(&cx.tcx.hir().body(body_id).value);
|
||||
collector.visit_expr(cx.tcx.hir().body(body_id).value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -653,7 +653,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
|
|||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
||||
if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind;
|
||||
let fn_name = path.ident;
|
||||
if let Some(sugg) = self.map.get(fn_name.as_str());
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
|
|
@ -685,9 +685,8 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
|
|||
let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
|
||||
if_chain! {
|
||||
if let ["expn_data", "outer_expn"] = method_names.as_slice();
|
||||
let args = arg_lists[1];
|
||||
if args.len() == 1;
|
||||
let self_arg = &args.0;
|
||||
let (self_arg, args)= arg_lists[1];
|
||||
if args.is_empty();
|
||||
let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
|
||||
then {
|
||||
|
|
@ -734,30 +733,30 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
|||
if and_then_args.len() == 5;
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
let only_expr = peel_blocks_with_stmt(&body.value);
|
||||
if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
|
||||
if let ExprKind::Path(..) = span_call_args[0].kind;
|
||||
let only_expr = peel_blocks_with_stmt(body.value);
|
||||
if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
|
||||
if let ExprKind::Path(..) = recv.kind;
|
||||
then {
|
||||
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
|
||||
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
||||
match ps.ident.as_str() {
|
||||
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
|
||||
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
|
||||
},
|
||||
"span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
|
||||
let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
|
||||
"span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
|
||||
},
|
||||
"span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
|
||||
let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
|
||||
"span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
|
||||
},
|
||||
"help" => {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
|
||||
}
|
||||
"note" => {
|
||||
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
|
||||
}
|
||||
_ => (),
|
||||
|
|
@ -798,9 +797,9 @@ fn span_suggestion_snippets<'a, 'hir>(
|
|||
cx: &LateContext<'_>,
|
||||
span_call_args: &'hir [Expr<'hir>],
|
||||
) -> SpanSuggestionSnippets<'a> {
|
||||
let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
|
||||
let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
|
||||
let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
|
||||
let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
|
||||
|
||||
SpanSuggestionSnippets {
|
||||
help: help_snippet,
|
||||
|
|
@ -954,7 +953,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
|
|||
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
|
||||
if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
return path_to_matched_type(cx, &body.value);
|
||||
return path_to_matched_type(cx, body.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1046,7 +1045,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
|
|||
if el_ty.is_str();
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
|
||||
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
|
||||
let path: Vec<&str> = path.iter().map(|x| {
|
||||
if let Constant::Str(s) = x {
|
||||
s.as_str()
|
||||
|
|
@ -1177,7 +1176,7 @@ impl InterningDefinedSymbol {
|
|||
};
|
||||
if_chain! {
|
||||
// is a method call
|
||||
if let ExprKind::MethodCall(_, [item], _) = call.kind;
|
||||
if let ExprKind::MethodCall(_, item, [], _) = call.kind;
|
||||
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
|
||||
let ty = cx.typeck_results().expr_ty(item);
|
||||
// ...on either an Ident or a Symbol
|
||||
|
|
|
|||
|
|
@ -1145,8 +1145,8 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
|
|||
self.add_single_span_suggestion();
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(path, arg, _arg_span) => {
|
||||
let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
|
||||
ExprKind::MethodCall(path, recv, _, _arg_span) => {
|
||||
let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
|
||||
if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
|
||||
let called_method = path.ident.name.as_str().to_string();
|
||||
for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ impl VecPushSearcher {
|
|||
},
|
||||
ExprKind::Unary(UnOp::Deref, _) | ExprKind::Index(..) if !needs_mut => {
|
||||
let mut last_place = parent;
|
||||
while let Some(parent) = get_parent_expr(cx, parent) {
|
||||
while let Some(parent) = get_parent_expr(cx, last_place) {
|
||||
if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..))
|
||||
|| matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -805,7 +805,11 @@ fn check_newlines(fmtstr: &StrLit) -> bool {
|
|||
let contents = fmtstr.symbol.as_str();
|
||||
|
||||
let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
|
||||
let c = c.unwrap();
|
||||
let c = match c {
|
||||
Ok(c) => c,
|
||||
Err(e) if !e.is_fatal() => return,
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
|
||||
if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
|
||||
should_lint = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue