Merge branch 'master' into mul-add-negative
This commit is contained in:
commit
15431b36bb
116 changed files with 1903 additions and 1405 deletions
|
|
@ -64,6 +64,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
|
|
@ -72,6 +73,7 @@ declare_clippy_lint! {
|
|||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
|
|
@ -103,6 +105,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
|
|
@ -111,6 +114,7 @@ declare_clippy_lint! {
|
|||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
|
|||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::GeneratorInteriorTypeCause;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use crate::utils::conf::DisallowedType;
|
||||
|
||||
|
|
@ -276,9 +276,9 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedTy
|
|||
}
|
||||
|
||||
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
match_def_path(cx, def_id, &paths::MUTEX_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
|
||||
cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ use clippy_utils::get_parent_expr;
|
|||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_block_with_applicability;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{BlockCheckMode, Closure, Expr, ExprKind};
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -44,39 +45,6 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]);
|
||||
|
||||
struct ExVisitor<'a, 'tcx> {
|
||||
found_block: Option<&'tcx Expr<'tcx>>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = expr.kind {
|
||||
// do not lint if the closure is called using an iterator (see #1141)
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(self.cx, expr);
|
||||
if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind;
|
||||
let caller = self.cx.typeck_results().expr_ty(self_arg);
|
||||
if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
if implements_trait(self.cx, caller, iter_id, &[]);
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let body = self.cx.tcx.hir().body(body);
|
||||
let ex = &body.value;
|
||||
if let ExprKind::Block(block, _) = ex.kind {
|
||||
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
|
||||
self.found_block = Some(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
|
||||
const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
|
||||
instead, move the block or closure higher and bind it with a `let`";
|
||||
|
|
@ -144,11 +112,31 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let mut visitor = ExVisitor { found_block: None, cx };
|
||||
walk_expr(&mut visitor, cond);
|
||||
if let Some(block) = visitor.found_block {
|
||||
span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE);
|
||||
}
|
||||
let _: Option<!> = for_each_expr(cond, |e| {
|
||||
if let ExprKind::Closure(closure) = e.kind {
|
||||
// do not lint if the closure is called using an iterator (see #1141)
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, e);
|
||||
if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind;
|
||||
let caller = cx.typeck_results().expr_ty(self_arg);
|
||||
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
if implements_trait(cx, caller, iter_id, &[]);
|
||||
then {
|
||||
return ControlFlow::Continue(Descend::No);
|
||||
}
|
||||
}
|
||||
|
||||
let body = cx.tcx.hir().body(closure.body);
|
||||
let ex = &body.value;
|
||||
if let ExprKind::Block(block, _) = ex.kind {
|
||||
if !body.value.span.from_expansion() && !block.stmts.is_empty() {
|
||||
span_lint(cx, BLOCKS_IN_IF_CONDITIONS, ex.span, COMPLEX_BLOCK_MESSAGE);
|
||||
return ControlFlow::Continue(Descend::No);
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ 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, sugg::Sugg};
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -56,13 +56,9 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx
|
|||
&& let Some(then_lit) = int_literal(then)
|
||||
&& let Some(else_lit) = int_literal(else_)
|
||||
{
|
||||
let inverted = if
|
||||
check_int_literal_equals_val(then_lit, 1)
|
||||
&& check_int_literal_equals_val(else_lit, 0) {
|
||||
let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
|
||||
false
|
||||
} else if
|
||||
check_int_literal_equals_val(then_lit, 0)
|
||||
&& check_int_literal_equals_val(else_lit, 1) {
|
||||
} else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
|
||||
true
|
||||
} else {
|
||||
// Expression isn't boolean, exit
|
||||
|
|
@ -123,14 +119,3 @@ fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hi
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::numeric_literal::NumericLiteral;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
|
|
@ -30,8 +31,10 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
|
||||
if let Some(lit) = get_numeric_literal(cast_expr) {
|
||||
let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
let literal_str = &cast_str;
|
||||
|
||||
if_chain! {
|
||||
if let LitKind::Int(n, _) = lit.node;
|
||||
|
|
@ -49,12 +52,16 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
match lit.node {
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
|
||||
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
|
||||
lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
|
||||
return false;
|
||||
},
|
||||
LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
|
||||
lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
|
||||
lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
|
||||
return false;
|
||||
},
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {
|
||||
return false;
|
||||
},
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
|
||||
LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
|
||||
| LitKind::Float(_, LitFloatType::Suffixed(_))
|
||||
if cast_from.kind() == cast_to.kind() =>
|
||||
|
|
@ -62,45 +69,62 @@ pub(super) fn check<'tcx>(
|
|||
if let Some(src) = snippet_opt(cx, cast_expr.span) {
|
||||
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||
"try",
|
||||
literal_str,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||
"try",
|
||||
cast_str,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
fn lint_unnecessary_cast(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
raw_literal_str: &str,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to: Ty<'_>,
|
||||
) {
|
||||
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
|
||||
let replaced_literal;
|
||||
let matchless = if literal_str.contains(['(', ')']) {
|
||||
replaced_literal = literal_str.replace(['(', ')'], "");
|
||||
&replaced_literal
|
||||
} else {
|
||||
literal_str
|
||||
// first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1`
|
||||
let literal_str = raw_literal_str
|
||||
.replace(['(', ')'], "")
|
||||
.trim_end_matches('.')
|
||||
.to_string();
|
||||
// we know need to check if the parent is a method call, to add parenthesis accordingly (eg:
|
||||
// (-1).foo() instead of -1.foo())
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(..) = parent_expr.kind
|
||||
&& literal_str.starts_with('-')
|
||||
{
|
||||
format!("({literal_str}_{cast_to})")
|
||||
|
||||
} else {
|
||||
format!("{literal_str}_{cast_to}")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"),
|
||||
"try",
|
||||
format!("{}_{cast_to}", matchless.trim_end_matches('.')),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq};
|
||||
use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -223,16 +222,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
|||
|
||||
/// Check for `expr >= 0`
|
||||
fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref lit) = &check.kind;
|
||||
if let LitKind::Int(0, _) = &lit.node;
|
||||
|
||||
then {
|
||||
Some(Conversion::new_any(candidate))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
is_integer_literal(check, 0).then(|| Conversion::new_any(candidate))
|
||||
}
|
||||
|
||||
/// Check for `expr >= (to_type::MIN as from_type)`
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::LimitStack;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Span;
|
||||
|
|
@ -61,11 +63,27 @@ impl CognitiveComplexity {
|
|||
return;
|
||||
}
|
||||
|
||||
let expr = &body.value;
|
||||
let expr = body.value;
|
||||
|
||||
let mut cc = 1u64;
|
||||
let mut returns = 0u64;
|
||||
let _: Option<!> = for_each_expr(expr, |e| {
|
||||
match e.kind {
|
||||
ExprKind::If(_, _, _) => {
|
||||
cc += 1;
|
||||
},
|
||||
ExprKind::Match(_, arms, _) => {
|
||||
if arms.len() > 1 {
|
||||
cc += 1;
|
||||
}
|
||||
cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
|
||||
},
|
||||
ExprKind::Ret(_) => returns += 1,
|
||||
_ => {},
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
|
||||
let mut helper = CcHelper { cc: 1, returns: 0 };
|
||||
helper.visit_expr(expr);
|
||||
let CcHelper { cc, returns } = helper;
|
||||
let ret_ty = cx.typeck_results().node_type(expr.hir_id);
|
||||
let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) {
|
||||
returns
|
||||
|
|
@ -74,13 +92,12 @@ impl CognitiveComplexity {
|
|||
(returns / 2)
|
||||
};
|
||||
|
||||
let mut rust_cc = cc;
|
||||
// prevent degenerate cases where unreachable code contains `return` statements
|
||||
if rust_cc >= ret_adjust {
|
||||
rust_cc -= ret_adjust;
|
||||
if cc >= ret_adjust {
|
||||
cc -= ret_adjust;
|
||||
}
|
||||
|
||||
if rust_cc > self.limit.limit() {
|
||||
if cc > self.limit.limit() {
|
||||
let fn_span = match kind {
|
||||
FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
|
||||
FnKind::Closure => {
|
||||
|
|
@ -107,7 +124,7 @@ impl CognitiveComplexity {
|
|||
COGNITIVE_COMPLEXITY,
|
||||
fn_span,
|
||||
&format!(
|
||||
"the function has a cognitive complexity of ({rust_cc}/{})",
|
||||
"the function has a cognitive complexity of ({cc}/{})",
|
||||
self.limit.limit()
|
||||
),
|
||||
None,
|
||||
|
|
@ -140,27 +157,3 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
|
|||
self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
|
||||
}
|
||||
}
|
||||
|
||||
struct CcHelper {
|
||||
cc: u64,
|
||||
returns: u64,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CcHelper {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
walk_expr(self, e);
|
||||
match e.kind {
|
||||
ExprKind::If(_, _, _) => {
|
||||
self.cc += 1;
|
||||
},
|
||||
ExprKind::Match(_, arms, _) => {
|
||||
if arms.len() > 1 {
|
||||
self.cc += 1;
|
||||
}
|
||||
self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
|
||||
},
|
||||
ExprKind::Ret(_) => self.returns += 1,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_integer_literal;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
|
|
@ -60,8 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
|||
if pathseg.ident.name.as_str() == "from_str_radix";
|
||||
|
||||
// check if the second argument is a primitive `10`
|
||||
if let ExprKind::Lit(lit) = &radix.kind;
|
||||
if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
|
||||
if is_integer_literal(radix, 10);
|
||||
|
||||
then {
|
||||
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::{DefIdSet, LocalDefId};
|
||||
use rustc_hir::{self as hir, def::Res, intravisit, QPath};
|
||||
use rustc_hir::{self as hir, def::Res, QPath};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::{
|
||||
lint::in_external_macro,
|
||||
|
|
@ -13,8 +13,11 @@ use clippy_utils::attrs::is_proc_macro;
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_must_use_ty;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
||||
|
||||
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
|
|
@ -200,63 +203,6 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m
|
|||
}
|
||||
}
|
||||
|
||||
struct StaticMutVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
mutates_static: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
|
||||
|
||||
if self.mutates_static {
|
||||
return;
|
||||
}
|
||||
match expr.kind {
|
||||
Call(_, args) => {
|
||||
let mut tys = DefIdSet::default();
|
||||
for arg in args {
|
||||
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
|
||||
&& is_mutable_ty(
|
||||
self.cx,
|
||||
self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
|
||||
arg.span,
|
||||
&mut tys,
|
||||
)
|
||||
&& is_mutated_static(arg)
|
||||
{
|
||||
self.mutates_static = true;
|
||||
return;
|
||||
}
|
||||
tys.clear();
|
||||
}
|
||||
},
|
||||
MethodCall(_, receiver, args, _) => {
|
||||
let mut tys = DefIdSet::default();
|
||||
for arg in std::iter::once(receiver).chain(args.iter()) {
|
||||
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
|
||||
&& is_mutable_ty(
|
||||
self.cx,
|
||||
self.cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
|
||||
arg.span,
|
||||
&mut tys,
|
||||
)
|
||||
&& is_mutated_static(arg)
|
||||
{
|
||||
self.mutates_static = true;
|
||||
return;
|
||||
}
|
||||
tys.clear();
|
||||
}
|
||||
},
|
||||
Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
|
||||
self.mutates_static |= is_mutated_static(target);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
|
||||
use hir::ExprKind::{Field, Index, Path};
|
||||
|
||||
|
|
@ -269,10 +215,53 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
|
||||
let mut v = StaticMutVisitor {
|
||||
cx,
|
||||
mutates_static: false,
|
||||
};
|
||||
intravisit::walk_expr(&mut v, body.value);
|
||||
v.mutates_static
|
||||
for_each_expr(body.value, |e| {
|
||||
use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
|
||||
|
||||
match e.kind {
|
||||
Call(_, args) => {
|
||||
let mut tys = DefIdSet::default();
|
||||
for arg in args {
|
||||
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
|
||||
&& is_mutable_ty(
|
||||
cx,
|
||||
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
|
||||
arg.span,
|
||||
&mut tys,
|
||||
)
|
||||
&& is_mutated_static(arg)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
tys.clear();
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
MethodCall(_, receiver, args, _) => {
|
||||
let mut tys = DefIdSet::default();
|
||||
for arg in std::iter::once(receiver).chain(args.iter()) {
|
||||
if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
|
||||
&& is_mutable_ty(
|
||||
cx,
|
||||
cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
|
||||
arg.span,
|
||||
&mut tys,
|
||||
)
|
||||
&& is_mutated_static(arg)
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
tys.clear();
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target)
|
||||
if is_mutated_static(target) =>
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
_ => ControlFlow::Continue(()),
|
||||
}
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ use rustc_span::def_id::LocalDefId;
|
|||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::type_is_unsafe_function;
|
||||
use clippy_utils::visitors::for_each_expr_with_closures;
|
||||
use clippy_utils::{iter_input_pats, path_to_local};
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
use super::NOT_UNSAFE_PTR_ARG_DEREF;
|
||||
|
||||
pub(super) fn check_fn<'tcx>(
|
||||
|
|
@ -39,21 +42,34 @@ fn check_raw_ptr<'tcx>(
|
|||
body: &'tcx hir::Body<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
let expr = &body.value;
|
||||
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(def_id) {
|
||||
let raw_ptrs = iter_input_pats(decl, body)
|
||||
.filter_map(|arg| raw_ptr_arg(cx, arg))
|
||||
.collect::<HirIdSet>();
|
||||
|
||||
if !raw_ptrs.is_empty() {
|
||||
let typeck_results = cx.tcx.typeck_body(body.id());
|
||||
let mut v = DerefVisitor {
|
||||
cx,
|
||||
ptrs: raw_ptrs,
|
||||
typeck_results,
|
||||
};
|
||||
|
||||
intravisit::walk_expr(&mut v, expr);
|
||||
let typeck = cx.tcx.typeck_body(body.id());
|
||||
let _: Option<!> = for_each_expr_with_closures(cx, body.value, |e| {
|
||||
match e.kind {
|
||||
hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
|
||||
for arg in args {
|
||||
check_arg(cx, &raw_ptrs, arg);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(_, recv, args, _) => {
|
||||
let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap();
|
||||
if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe {
|
||||
check_arg(cx, &raw_ptrs, recv);
|
||||
for arg in args {
|
||||
check_arg(cx, &raw_ptrs, arg);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => check_arg(cx, &raw_ptrs, ptr),
|
||||
_ => (),
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -70,54 +86,13 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<hir::HirId>
|
|||
}
|
||||
}
|
||||
|
||||
struct DerefVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
ptrs: HirIdSet,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(f, args) => {
|
||||
let ty = self.typeck_results.expr_ty(f);
|
||||
|
||||
if type_is_unsafe_function(self.cx, ty) {
|
||||
for arg in args {
|
||||
self.check_arg(arg);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(_, receiver, args, _) => {
|
||||
let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
|
||||
let base_type = self.cx.tcx.type_of(def_id);
|
||||
|
||||
if type_is_unsafe_function(self.cx, base_type) {
|
||||
self.check_arg(receiver);
|
||||
for arg in args {
|
||||
self.check_arg(arg);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
|
||||
fn check_arg(&self, ptr: &hir::Expr<'_>) {
|
||||
if let Some(id) = path_to_local(ptr) {
|
||||
if self.ptrs.contains(&id) {
|
||||
span_lint(
|
||||
self.cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
ptr.span,
|
||||
"this public function might dereference a raw pointer but is not marked `unsafe`",
|
||||
);
|
||||
}
|
||||
}
|
||||
fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
|
||||
if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
|
||||
span_lint(
|
||||
cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
arg.span,
|
||||
"this public function might dereference a raw pointer but is not marked `unsafe`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
|
||||
use clippy_utils::{
|
||||
contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
|
||||
};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
|
@ -76,10 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind
|
||||
&& is_lang_ctor(cx, then_call_qpath, OptionSome)
|
||||
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
|
||||
&& is_lang_ctor(cx, qpath, OptionNone)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
|
||||
&& !stmts_contains_early_return(then_block.stmts)
|
||||
{
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ use clippy_utils::{
|
|||
diagnostics::span_lint_hir_and_then,
|
||||
get_async_fn_body, is_async_fn,
|
||||
source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
|
||||
visitors::expr_visitor_no_bodies,
|
||||
visitors::for_each_expr,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
|
@ -152,7 +153,7 @@ fn lint_implicit_returns(
|
|||
|
||||
ExprKind::Loop(block, ..) => {
|
||||
let mut add_return = false;
|
||||
expr_visitor_no_bodies(|e| {
|
||||
let _: Option<!> = for_each_expr(block, |e| {
|
||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||
|
|
@ -167,9 +168,8 @@ fn lint_implicit_returns(
|
|||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.visit_block(block);
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
if add_return {
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
if let Some(span) = call_site_span {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq};
|
||||
use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -131,17 +131,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
|
||||
match peel_blocks_with_stmt(expr).kind {
|
||||
ExprKind::AssignOp(ref op1, target, value) => {
|
||||
if_chain! {
|
||||
if BinOpKind::Sub == op1.node;
|
||||
// Check if literal being subtracted is one
|
||||
if let ExprKind::Lit(ref lit1) = value.kind;
|
||||
if let LitKind::Int(1, _) = lit1.node;
|
||||
then {
|
||||
Some(target)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Check if literal being subtracted is one
|
||||
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
|
||||
},
|
||||
ExprKind::Assign(target, value, _) => {
|
||||
if_chain! {
|
||||
|
|
@ -150,8 +141,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
|
|||
|
||||
if SpanlessEq::new(cx).eq_expr(left1, target);
|
||||
|
||||
if let ExprKind::Lit(ref lit1) = right1.kind;
|
||||
if let LitKind::Int(1, _) = lit1.node;
|
||||
if is_integer_literal(right1, 1);
|
||||
then {
|
||||
Some(target)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{higher, match_def_path, path_def_id, paths};
|
||||
use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -168,9 +168,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
},
|
||||
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
|
||||
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
|
||||
ExprKind::Call(path, _) => path_def_id(cx, path)
|
||||
.map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
|
||||
.into(),
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
cx.qpath_res(qpath, path.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
|
||||
.into()
|
||||
} else {
|
||||
Finite
|
||||
}
|
||||
},
|
||||
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
|
||||
_ => Finite,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method};
|
||||
use clippy_utils::{return_ty, trait_ref_of_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -118,7 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
|||
}
|
||||
|
||||
fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
|
||||
let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!");
|
||||
let display_trait_id = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Display)
|
||||
.expect("Failed to get trait ID of `Display`!");
|
||||
|
||||
// Get the real type of 'self'
|
||||
let self_type = cx.tcx.fn_sig(item.def_id).input(0);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
|
|||
LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::INVALID_PATHS),
|
||||
LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
|
||||
LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL),
|
||||
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
|
||||
LintId::of(utils::internal_lints::PRODUCE_ICE),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -24,8 +24,6 @@ store.register_lints(&[
|
|||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
|
||||
|
|
@ -34,6 +32,8 @@ store.register_lints(&[
|
|||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_DEF_PATH,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
|
|
|
|||
|
|
@ -535,7 +535,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths));
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::InterningDefinedSymbol>::default());
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::LintWithoutLintPass>::default());
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use super::utils::make_iterator_snippet;
|
||||
use super::MANUAL_FIND;
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt,
|
||||
diagnostics::span_lint_and_then, higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt,
|
||||
source::snippet_with_applicability, ty::implements_trait,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
|
|
@ -30,8 +30,8 @@ pub(super) fn check<'tcx>(
|
|||
if let [stmt] = block.stmts;
|
||||
if let StmtKind::Semi(semi) = stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret_value)) = semi.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind;
|
||||
if is_lang_ctor(cx, ctor, LangItem::OptionSome);
|
||||
if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind;
|
||||
if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome);
|
||||
if path_res(cx, inner_ret) == Res::Local(binding_id);
|
||||
if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr);
|
||||
then {
|
||||
|
|
@ -143,8 +143,7 @@ fn last_stmt_and_ret<'tcx>(
|
|||
if let Some((_, Node::Block(block))) = parent_iter.next();
|
||||
if let Some((last_stmt, last_ret)) = extract(block);
|
||||
if last_stmt.hir_id == node_hir;
|
||||
if let ExprKind::Path(path) = &last_ret.kind;
|
||||
if is_lang_ctor(cx, path, LangItem::OptionNone);
|
||||
if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone);
|
||||
if let Some((_, Node::Expr(_block))) = parent_iter.next();
|
||||
// This includes the function header
|
||||
if let Some((_, func)) = parent_iter.next();
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ use super::MANUAL_FLATTEN;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt};
|
||||
use clippy_utils::{path_to_local_id, peel_blocks_with_stmt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, DefIdTree};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
|
||||
|
|
@ -30,8 +30,10 @@ pub(super) fn check<'tcx>(
|
|||
if path_to_local_id(let_expr, pat_hir_id);
|
||||
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
|
||||
if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
|
||||
let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
|
||||
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id);
|
||||
if let Some(variant_id) = cx.tcx.opt_parent(ctor_id);
|
||||
let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id);
|
||||
let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id);
|
||||
if some_ctor || ok_ctor;
|
||||
// Ensure expr in `if let` is not used afterwards
|
||||
if !is_local_used(cx, if_then, pat_hir_id);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{
|
||||
get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths,
|
||||
visitors::is_res_used,
|
||||
get_enclosing_loop_or_multi_call_closure, is_refutable, is_res_lang_ctor, is_trait_method, visitors::is_res_used,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter::OnlyBodies;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
|
|
@ -19,9 +18,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
|
||||
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
|
||||
// check for `Some(..)` pattern
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
|
||||
if let Res::Def(_, pat_did) = pat_path.res;
|
||||
if match_def_path(cx, pat_did, &paths::OPTION_SOME);
|
||||
if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind;
|
||||
if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
|
||||
// check for call to `Iterator::next`
|
||||
if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind;
|
||||
if method_name.ident.name == sym::next;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{peel_blocks_with_stmt, sugg};
|
||||
use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -50,20 +51,36 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
|||
let mut applicability = Applicability::MachineApplicable;
|
||||
let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
|
||||
let cond = cond.peel_drop_temps();
|
||||
let mut comments = span_extract_comment(cx.sess().source_map(), expr.span);
|
||||
if !comments.is_empty() {
|
||||
comments += "\n";
|
||||
}
|
||||
let (cond, not) = match cond.kind {
|
||||
ExprKind::Unary(UnOp::Not, e) => (e, ""),
|
||||
_ => (cond, "!"),
|
||||
};
|
||||
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
|
||||
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
|
||||
span_lint_and_sugg(
|
||||
// we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_ASSERT,
|
||||
expr.span,
|
||||
"only a `panic!` in `if`-then statement",
|
||||
"try",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
// comments can be noisy, do not show them to the user
|
||||
diag.tool_only_span_suggestion(
|
||||
expr.span.shrink_to_lo(),
|
||||
"add comments back",
|
||||
comments,
|
||||
applicability);
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"try instead",
|
||||
sugg,
|
||||
applicability);
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,7 +324,7 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
|
|||
outer_arg: &'tcx Expr<'tcx>,
|
||||
span: Span,
|
||||
) -> Option<ClampSuggestion<'tcx>> {
|
||||
if let ExprKind::Call(inner_fn, &[ref first, ref second]) = &inner_call.kind
|
||||
if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind
|
||||
&& let Some(inner_seg) = segment(cx, inner_fn)
|
||||
&& let Some(outer_seg) = segment(cx, outer_fn)
|
||||
{
|
||||
|
|
@ -377,9 +377,7 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
|
|||
/// # ;
|
||||
/// ```
|
||||
fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
|
||||
if let ExprKind::Match(value, &[ref first_arm, ref second_arm, ref last_arm], rustc_hir::MatchSource::Normal) =
|
||||
&expr.kind
|
||||
{
|
||||
if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind {
|
||||
// Find possible min/max branches
|
||||
let minmax_values = |a: &'tcx Arm<'tcx>| {
|
||||
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ fn check_into_iter(
|
|||
&& match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
|
||||
&& let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind
|
||||
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
|
||||
&& match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER)
|
||||
&& cx.tcx.lang_items().require(hir::LangItem::IntoIterIntoIter).ok() == Some(into_iter_def_id)
|
||||
&& match_acceptable_type(cx, left_expr, msrv)
|
||||
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
|
||||
suggest(cx, parent_expr, left_expr, target_expr);
|
||||
|
|
|
|||
|
|
@ -131,12 +131,12 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
|
|||
},
|
||||
hir::ExprKind::Block(block, _) => {
|
||||
match (block.stmts, block.expr.as_ref()) {
|
||||
(&[], Some(inner_expr)) => {
|
||||
([], Some(inner_expr)) => {
|
||||
// If block only contains an expression,
|
||||
// reduce `{ X }` to `X`
|
||||
reduce_unit_expression(cx, inner_expr)
|
||||
},
|
||||
(&[ref inner_stmt], None) => {
|
||||
([inner_stmt], None) => {
|
||||
// If block only contains statements,
|
||||
// reduce `{ X; }` to `X` or `X;`
|
||||
match inner_stmt.kind {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq};
|
||||
use clippy_utils::{
|
||||
is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
|
|
@ -110,7 +112,7 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
|||
}
|
||||
match arm.pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks,
|
||||
peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
|
||||
can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id,
|
||||
peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -251,9 +251,11 @@ fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: Syn
|
|||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
|
||||
PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => {
|
||||
Some(OptionPat::None)
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
|
|
@ -272,16 +274,14 @@ fn get_some_expr<'tcx>(
|
|||
) -> Option<SomeExpr<'tcx>> {
|
||||
// TODO: Allow more complex expressions.
|
||||
match expr.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(ref qpath),
|
||||
..
|
||||
},
|
||||
[arg],
|
||||
) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
|
||||
expr: arg,
|
||||
needs_unsafe_block,
|
||||
}),
|
||||
ExprKind::Call(callee, [arg])
|
||||
if ctxt == expr.span.ctxt() && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) =>
|
||||
{
|
||||
Some(SomeExpr {
|
||||
expr: arg,
|
||||
needs_unsafe_block,
|
||||
})
|
||||
},
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
|
|
@ -302,5 +302,5 @@ fn get_some_expr<'tcx>(
|
|||
|
||||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
|
||||
is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id, sugg};
|
||||
use clippy_utils::{is_res_lang_ctor, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::LangItem::{OptionNone, ResultErr};
|
||||
use rustc_hir::{Arm, Expr, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MANUAL_UNWRAP_OR;
|
||||
|
|
@ -59,15 +61,19 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'
|
|||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) =>
|
||||
matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
|
||||
matches!(pat.kind, PatKind::Wild)
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr),
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
let unwrap_arm = &arms[1 - idx];
|
||||
if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id);
|
||||
if let Some(variant_id) = cx.tcx.opt_parent(ctor_id);
|
||||
if cx.tcx.lang_items().option_some_variant() == Some(variant_id)
|
||||
|| cx.tcx.lang_items().result_ok_variant() == Some(variant_id);
|
||||
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
|
||||
if path_to_local_id(unwrap_arm.body, binding_hir_id);
|
||||
if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_lang_ctor, peel_blocks};
|
||||
use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -59,18 +59,20 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
|
||||
// Checks if arm has the form `None => None`
|
||||
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone))
|
||||
matches!(
|
||||
arm.pat.kind,
|
||||
PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone)
|
||||
)
|
||||
}
|
||||
|
||||
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
|
||||
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
|
||||
if_chain! {
|
||||
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, LangItem::OptionSome);
|
||||
if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome);
|
||||
if let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind;
|
||||
if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind;
|
||||
if let ExprKind::Path(ref some_path) = e.kind;
|
||||
if is_lang_ctor(cx, some_path, LangItem::OptionSome);
|
||||
if is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome);
|
||||
if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind;
|
||||
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
|
||||
use clippy_utils::{
|
||||
eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
|
||||
eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_res_lang_ctor, over, path_res,
|
||||
peel_blocks_with_stmt,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -112,10 +112,7 @@ fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool
|
|||
let ret = strip_return(else_expr);
|
||||
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
|
||||
if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
|
||||
if let ExprKind::Path(ref qpath) = ret.kind {
|
||||
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
|
||||
}
|
||||
return false;
|
||||
return is_res_lang_ctor(cx, path_res(cx, ret), OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
|
||||
}
|
||||
return eq_expr_value(cx, if_let.let_expr, ret);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
|
||||
use clippy_utils::visitors::any_temporaries_need_ordered_drop;
|
||||
use clippy_utils::{higher, is_lang_ctor, is_trait_method};
|
||||
use clippy_utils::{higher, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -87,15 +88,21 @@ fn find_sugg_for_if_let<'tcx>(
|
|||
}
|
||||
},
|
||||
PatKind::Path(ref path) => {
|
||||
let method = if is_lang_ctor(cx, path, OptionNone) {
|
||||
"is_none()"
|
||||
} else if is_lang_ctor(cx, path, PollPending) {
|
||||
"is_pending()"
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id)
|
||||
&& let Some(variant_id) = cx.tcx.opt_parent(ctor_id)
|
||||
{
|
||||
let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) {
|
||||
"is_none()"
|
||||
} else if cx.tcx.lang_items().poll_pending_variant() == Some(variant_id) {
|
||||
"is_pending()"
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
// `None` and `Pending` don't have an inner type.
|
||||
(method, cx.tcx.types.unit)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
// `None` and `Pending` don't have an inner type.
|
||||
(method, cx.tcx.types.unit)
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
|
||||
use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
|
|
@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
|
||||
if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..));
|
||||
if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind;
|
||||
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
|
||||
if is_lang_ctor(cx, err_fun_path, ResultErr);
|
||||
if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr);
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
then {
|
||||
let prefix;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs};
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
|
|
@ -102,40 +102,38 @@ impl_lint_pass!(MemReplace =>
|
|||
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
|
||||
|
||||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
if let ExprKind::Path(ref replacement_qpath) = src.kind {
|
||||
// Check that second argument is `Option::None`
|
||||
if is_lang_ctor(cx, replacement_qpath, OptionNone) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
// argument's type. All that's left is to get
|
||||
// replacee's path.
|
||||
let replaced_path = match dest.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
|
||||
if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
|
||||
replaced_path
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
|
||||
_ => return,
|
||||
};
|
||||
// Check that second argument is `Option::None`
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
// argument's type. All that's left is to get
|
||||
// replacee's path.
|
||||
let replaced_path = match dest.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
|
||||
if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
|
||||
replaced_path
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MEM_REPLACE_OPTION_WITH_NONE,
|
||||
expr_span,
|
||||
"replacing an `Option` with `None`",
|
||||
"consider `Option::take()` instead",
|
||||
format!(
|
||||
"{}.take()",
|
||||
snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MEM_REPLACE_OPTION_WITH_NONE,
|
||||
expr_span,
|
||||
"replacing an `Option` with `None`",
|
||||
"consider `Option::take()` instead",
|
||||
format!(
|
||||
"{}.take()",
|
||||
snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,10 +201,8 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
|||
return;
|
||||
}
|
||||
// disable lint for Option since it is covered in another lint
|
||||
if let ExprKind::Path(q) = &src.kind {
|
||||
if is_lang_ctor(cx, q, OptionNone) {
|
||||
return;
|
||||
}
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
return;
|
||||
}
|
||||
if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{get_parent_expr, paths};
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FILETYPE_IS_FILE;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
|
||||
if !match_type(cx, ty, &paths::FILE_TYPE) {
|
||||
if !is_type_diagnostic_item(cx, ty, sym::FileType) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use rustc_ast::LitKind;
|
||||
use clippy_utils::{is_integer_literal, SpanlessEq};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -26,8 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg:
|
|||
&& lhs_path.ident.name == sym::len
|
||||
|
||||
// RHS of subtraction is 1
|
||||
&& let ExprKind::Lit(rhs_lit) = &rhs.kind
|
||||
&& let LitKind::Int(1, ..) = rhs_lit.node
|
||||
&& is_integer_literal(rhs, 1)
|
||||
|
||||
// check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)`
|
||||
&& SpanlessEq::new(cx).eq_expr(recv, lhs_recv)
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
|||
}
|
||||
|
||||
if let ty::Adt(adt, substs) = ty.kind() {
|
||||
match_def_path(cx, adt.did(), &paths::COW) && substs.type_at(1).is_str()
|
||||
cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res};
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
|
@ -26,26 +26,11 @@ impl IterType {
|
|||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
|
||||
let item = match &recv.kind {
|
||||
ExprKind::Array(v) if v.len() <= 1 => v.first(),
|
||||
ExprKind::Path(p) => {
|
||||
if is_lang_ctor(cx, p, OptionNone) {
|
||||
None
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Call(f, some_args) if some_args.len() == 1 => {
|
||||
if let ExprKind::Path(p) = &f.kind {
|
||||
if is_lang_ctor(cx, p, OptionSome) {
|
||||
Some(&some_args[0])
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
let item = match recv.kind {
|
||||
ExprKind::Array([]) => None,
|
||||
ExprKind::Array([e]) => Some(e),
|
||||
ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None,
|
||||
ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg),
|
||||
_ => return,
|
||||
};
|
||||
let iter_type = match method_name {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_hir::{Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
|
@ -22,8 +22,8 @@ pub(super) fn check<'tcx>(
|
|||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if let ExprKind::Call(err_path, [err_arg]) = or_expr.kind;
|
||||
if is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
|
|
@ -46,17 +46,19 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
match map_expr.kind {
|
||||
ExprKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, map_expr.hir_id), ResultOk) => true,
|
||||
ExprKind::Closure(closure) => {
|
||||
let body = cx.tcx.hir().body(closure.body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind
|
||||
&& let ExprKind::Call(callee, [ok_arg]) = body.value.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, callee), ResultOk)
|
||||
{
|
||||
path_to_local_id(ok_arg, param_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_path_diagnostic_item;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
|
||||
use clippy_utils::{is_expr_path_def_path, paths};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -38,7 +38,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
|
|||
let ty = cx.typeck_results().expr_ty(e);
|
||||
if is_type_diagnostic_item(cx, ty, sym::String)
|
||||
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
|
||||
|| (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str))
|
||||
|| (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
|
||||
{
|
||||
Some(RepeatKind::String)
|
||||
} else {
|
||||
|
|
@ -57,7 +57,7 @@ pub(super) fn check(
|
|||
) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
|
||||
if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT);
|
||||
if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::String);
|
||||
if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
|
||||
if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_def_id};
|
||||
use clippy_utils::{is_res_lang_ctor, path_def_id, path_res};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
|
|
@ -51,22 +51,12 @@ pub(super) fn check<'tcx>(
|
|||
return;
|
||||
}
|
||||
|
||||
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
|
||||
is_lang_ctor(cx, qpath, OptionNone)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !default_arg_is_none {
|
||||
if !is_res_lang_ctor(cx, path_res(cx, def_arg), OptionNone) {
|
||||
// nothing to lint!
|
||||
return;
|
||||
}
|
||||
|
||||
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
|
||||
is_lang_ctor(cx, qpath, OptionSome)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let f_arg_is_some = is_res_lang_ctor(cx, path_res(cx, map_arg), OptionSome);
|
||||
|
||||
if is_option {
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, is_lang_ctor};
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{lang_items::LangItem, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -58,8 +58,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: LangItem) -> Option<Span> {
|
||||
if let ExprKind::Call(some_expr, [arg]) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = &some_expr.kind
|
||||
&& is_lang_ctor(cx, qpath, item)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, some_expr), item)
|
||||
{
|
||||
Some(arg.span)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use clippy_utils::consts::{constant, Constant};
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::visitors::expr_visitor;
|
||||
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{
|
||||
BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
||||
};
|
||||
|
|
@ -211,7 +211,7 @@ fn indirect_usage<'tcx>(
|
|||
binding: HirId,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<IndirectUsage<'tcx>> {
|
||||
if let StmtKind::Local(Local {
|
||||
if let StmtKind::Local(&Local {
|
||||
pat: Pat {
|
||||
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
|
||||
..
|
||||
|
|
@ -222,14 +222,12 @@ fn indirect_usage<'tcx>(
|
|||
}) = stmt.kind
|
||||
{
|
||||
let mut path_to_binding = None;
|
||||
expr_visitor(cx, |expr| {
|
||||
if path_to_local_id(expr, binding) {
|
||||
path_to_binding = Some(expr);
|
||||
let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
|
||||
if path_to_local_id(e, binding) {
|
||||
path_to_binding = Some(e);
|
||||
}
|
||||
|
||||
path_to_binding.is_none()
|
||||
})
|
||||
.visit_expr(init_expr);
|
||||
ControlFlow::Continue(Descend::from(path_to_binding.is_none()))
|
||||
});
|
||||
|
||||
let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
|
||||
let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
|
||||
|
|
@ -250,7 +248,7 @@ fn indirect_usage<'tcx>(
|
|||
..
|
||||
} = iter_usage
|
||||
{
|
||||
if parent_id == *local_hir_id {
|
||||
if parent_id == local_hir_id {
|
||||
return Some(IndirectUsage {
|
||||
name: ident.name,
|
||||
span: stmt.span,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ use super::utils::clone_or_copy_needed;
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -13,7 +14,7 @@ use rustc_span::sym;
|
|||
use super::UNNECESSARY_FILTER_MAP;
|
||||
use super::UNNECESSARY_FIND_MAP;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>, name: &str) {
|
||||
if !is_trait_method(cx, expr, sym::Iterator) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -26,10 +27,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
|
|||
|
||||
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);
|
||||
found_mapping |= return_visitor.found_mapping;
|
||||
found_filtering |= return_visitor.found_filtering;
|
||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
||||
if let hir::ExprKind::Ret(Some(e)) = &e.kind {
|
||||
let (found_mapping_res, found_filtering_res) = check_expression(cx, arg_id, e);
|
||||
found_mapping |= found_mapping_res;
|
||||
found_filtering |= found_filtering_res;
|
||||
ControlFlow::Continue(Descend::No)
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
}
|
||||
});
|
||||
|
||||
let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
|
||||
let sugg = if !found_filtering {
|
||||
|
|
@ -61,15 +68,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
|
|||
|
||||
// returns (found_mapping, found_filtering)
|
||||
fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
|
||||
match &expr.kind {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
if let hir::ExprKind::Path(ref path) = func.kind {
|
||||
if is_lang_ctor(cx, path, OptionSome) {
|
||||
if path_to_local_id(&args[0], arg_id) {
|
||||
return (false, false);
|
||||
}
|
||||
return (true, false);
|
||||
if is_res_lang_ctor(cx, path_res(cx, func), OptionSome) {
|
||||
if path_to_local_id(&args[0], arg_id) {
|
||||
return (false, false);
|
||||
}
|
||||
return (true, false);
|
||||
}
|
||||
(true, true)
|
||||
},
|
||||
|
|
@ -80,7 +85,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
|||
hir::ExprKind::Match(_, arms, _) => {
|
||||
let mut found_mapping = false;
|
||||
let mut found_filtering = false;
|
||||
for arm in *arms {
|
||||
for arm in arms {
|
||||
let (m, f) = check_expression(cx, arg_id, arm.body);
|
||||
found_mapping |= m;
|
||||
found_filtering |= f;
|
||||
|
|
@ -93,39 +98,9 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
|||
let else_check = check_expression(cx, arg_id, else_arm);
|
||||
(if_check.0 | else_check.0, if_check.1 | else_check.1)
|
||||
},
|
||||
hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
|
||||
hir::ExprKind::Path(ref path) if is_res_lang_ctor(cx, cx.qpath_res(path, expr.hir_id), OptionNone) => {
|
||||
(false, true)
|
||||
},
|
||||
_ => (true, true),
|
||||
}
|
||||
}
|
||||
|
||||
struct ReturnVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
arg_id: hir::HirId,
|
||||
// Found a non-None return that isn't Some(input)
|
||||
found_mapping: bool,
|
||||
// Found a return that isn't Some
|
||||
found_filtering: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ReturnVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
|
||||
ReturnVisitor {
|
||||
cx,
|
||||
arg_id,
|
||||
found_mapping: false,
|
||||
found_filtering: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ReturnVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if let hir::ExprKind::Ret(Some(expr)) = &expr.kind {
|
||||
let (found_mapping, found_filtering) = check_expression(self.cx, self.arg_id, expr);
|
||||
self.found_mapping |= found_mapping;
|
||||
self.found_filtering |= found_filtering;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::walk_ptrs_ty_depth;
|
||||
use clippy_utils::{get_parent_expr, match_trait_method, paths};
|
||||
use clippy_utils::{get_parent_expr, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::USELESS_ASREF;
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ use super::USELESS_ASREF;
|
|||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) {
|
||||
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
|
||||
// check if the call is to the actual `AsRef` or `AsMut` trait
|
||||
if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
|
||||
if is_trait_method(cx, expr, sym::AsRef) || is_trait_method(cx, expr, sym::AsMut) {
|
||||
// check if the type after `as_ref` or `as_mut` is the same as before
|
||||
let rcv_ty = cx.typeck_results().expr_ty(recvr);
|
||||
let res_ty = cx.typeck_results().expr_ty(expr);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{match_trait_method, paths};
|
||||
use clippy_utils::is_trait_method;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -83,7 +83,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
|
|||
}
|
||||
},
|
||||
ExprKind::MethodCall(path, receiver, args @ [_], _) => {
|
||||
if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) {
|
||||
if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) {
|
||||
if path.ident.name == sym!(max) {
|
||||
fetch_const(cx, Some(receiver), args, MinMax::Max)
|
||||
} else if path.ident.name == sym!(min) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
|
|
@ -15,7 +14,7 @@ use rustc_span::hygiene::DesugaringKind;
|
|||
use rustc_span::source_map::{ExpnKind, Span};
|
||||
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
|
||||
use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -314,8 +313,7 @@ fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
|
|||
fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
|
||||
if_chain! {
|
||||
if let TyKind::Ptr(ref mut_ty) = ty.kind;
|
||||
if let ExprKind::Lit(ref lit) = e.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
if is_integer_literal(e, 0);
|
||||
if !in_constant(cx, e.hir_id);
|
||||
then {
|
||||
let (msg, sugg_fn) = match mut_ty.mutbl {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -8,36 +6,26 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings that destructure a reference and borrow the inner
|
||||
/// Checks for bindings that needlessly destructure a reference and borrow the inner
|
||||
/// value with `&ref`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This pattern has no effect in almost all cases.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// In some cases, `&ref` is needed to avoid a lifetime mismatch error.
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// fn foo(a: &Option<String>, b: &Option<String>) {
|
||||
/// match (a, b) {
|
||||
/// (None, &ref c) | (&ref c, None) => (),
|
||||
/// (&Some(ref c), _) => (),
|
||||
/// };
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// # #[allow(unused)]
|
||||
/// v.iter_mut().filter(|&ref a| a.is_empty());
|
||||
///
|
||||
/// if let &[ref first, ref second] = v.as_slice() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// # #[allow(unused)]
|
||||
/// v.iter_mut().filter(|a| a.is_empty());
|
||||
///
|
||||
/// if let [first, second] = v.as_slice() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_BORROWED_REFERENCE,
|
||||
|
|
@ -54,34 +42,83 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
// Only lint immutable refs, because `&mut ref T` may be useful.
|
||||
if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind;
|
||||
// Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms
|
||||
for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) {
|
||||
let Node::Pat(pat) = node else { break };
|
||||
|
||||
// Check sub_pat got a `ref` keyword (excluding `ref mut`).
|
||||
if let PatKind::Binding(BindingAnnotation::REF, .., spanned_name, _) = sub_pat.kind;
|
||||
let parent_id = cx.tcx.hir().get_parent_node(pat.hir_id);
|
||||
if let Some(parent_node) = cx.tcx.hir().find(parent_id);
|
||||
then {
|
||||
// do not recurse within patterns, as they may have other references
|
||||
// XXXManishearth we can relax this constraint if we only check patterns
|
||||
// with a single ref pattern inside them
|
||||
if let Node::Pat(_) = parent_node {
|
||||
return;
|
||||
}
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
|
||||
"this pattern takes a reference on something that is being de-referenced",
|
||||
|diag| {
|
||||
let hint = snippet_with_applicability(cx, spanned_name.span, "..", &mut applicability).into_owned();
|
||||
diag.span_suggestion(
|
||||
pat.span,
|
||||
"try removing the `&ref` part and just keep",
|
||||
hint,
|
||||
applicability,
|
||||
);
|
||||
});
|
||||
if matches!(pat.kind, PatKind::Or(_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only lint immutable refs, because `&mut ref T` may be useful.
|
||||
let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return };
|
||||
|
||||
match sub_pat.kind {
|
||||
// Check sub_pat got a `ref` keyword (excluding `ref mut`).
|
||||
PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_BORROWED_REFERENCE,
|
||||
pat.span,
|
||||
"this pattern takes a reference on something that is being dereferenced",
|
||||
|diag| {
|
||||
// `&ref ident`
|
||||
// ^^^^^
|
||||
let span = pat.span.until(ident.span);
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"try removing the `&ref` part",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
// Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]`
|
||||
PatKind::Slice(
|
||||
before,
|
||||
None
|
||||
| Some(Pat {
|
||||
kind: PatKind::Wild, ..
|
||||
}),
|
||||
after,
|
||||
) => {
|
||||
let mut suggestions = Vec::new();
|
||||
|
||||
for element_pat in itertools::chain(before, after) {
|
||||
if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind {
|
||||
// `&[..., ref ident, ...]`
|
||||
// ^^^^
|
||||
let span = element_pat.span.until(ident.span);
|
||||
suggestions.push((span, String::new()));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !suggestions.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_BORROWED_REFERENCE,
|
||||
pat.span,
|
||||
"dereferencing a slice pattern where every element takes a reference",
|
||||
|diag| {
|
||||
// `&[...]`
|
||||
// ^
|
||||
let span = pat.span.until(sub_pat.span);
|
||||
suggestions.push((span, String::new()));
|
||||
|
||||
diag.multipart_suggestion(
|
||||
"try removing the `&` and `ref` parts",
|
||||
suggestions,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::path_to_local;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::needs_ordered_drop;
|
||||
use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
|
||||
use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{
|
||||
BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
|
||||
StmtKind,
|
||||
|
|
@ -64,31 +64,25 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
|
||||
|
||||
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
|
||||
let mut seen = false;
|
||||
expr_visitor(cx, |expr| {
|
||||
if let ExprKind::Assign(..) = expr.kind {
|
||||
seen = true;
|
||||
for_each_expr_with_closures(cx, stmt, |e| {
|
||||
if matches!(e.kind, ExprKind::Assign(..)) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
!seen
|
||||
})
|
||||
.visit_stmt(stmt);
|
||||
|
||||
seen
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn contains_let(cond: &Expr<'_>) -> bool {
|
||||
let mut seen = false;
|
||||
expr_visitor_no_bodies(|expr| {
|
||||
if let ExprKind::Let(_) = expr.kind {
|
||||
seen = true;
|
||||
for_each_expr(cond, |e| {
|
||||
if matches!(e.kind, ExprKind::Let(_)) {
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
!seen
|
||||
})
|
||||
.visit_expr(cond);
|
||||
|
||||
seen
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lang_ctor;
|
||||
use clippy_utils::path_res;
|
||||
use clippy_utils::source::snippet;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{AsyncGeneratorKind, Block, Body, Expr, ExprKind, GeneratorKind, LangItem, MatchSource, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -112,11 +113,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
|
|||
|
||||
fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(path, [arg]) = &expr.kind;
|
||||
if let ExprKind::Path(ref qpath) = &path.kind;
|
||||
let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
|
||||
if let ExprKind::Call(path, [arg]) = expr.kind;
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = path_res(cx, path);
|
||||
if let Some(variant_id) = cx.tcx.opt_parent(ctor_id);
|
||||
let sugg_remove = if cx.tcx.lang_items().option_some_variant() == Some(variant_id) {
|
||||
"Some()"
|
||||
} else if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
} else if cx.tcx.lang_items().result_ok_variant() == Some(variant_id) {
|
||||
"Ok()"
|
||||
} else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -49,7 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
|||
if_chain! {
|
||||
if (path.ident.name == sym!(mode)
|
||||
&& (match_type(cx, obj_ty, &paths::OPEN_OPTIONS)
|
||||
|| match_type(cx, obj_ty, &paths::DIR_BUILDER)))
|
||||
|| is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
|
||||
|| (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
|
||||
if let ExprKind::Lit(_) = param.kind;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,25 +44,19 @@ impl ArithmeticSideEffects {
|
|||
/// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
|
||||
/// non-constant environment that won't overflow.
|
||||
fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool {
|
||||
if let hir::BinOpKind::Add | hir::BinOpKind::Sub = op.node
|
||||
&& let hir::ExprKind::Lit(ref lit) = expr.kind
|
||||
&& let ast::LitKind::Int(0, _) = lit.node
|
||||
if let hir::ExprKind::Lit(ref lit) = expr.kind &&
|
||||
let ast::LitKind::Int(value, _) = lit.node
|
||||
{
|
||||
return true;
|
||||
match (&op.node, value) {
|
||||
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) |
|
||||
(hir::BinOpKind::Mul, 0 | 1) => true,
|
||||
(hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
|
||||
(hir::BinOpKind::Div | hir::BinOpKind::Rem, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node
|
||||
&& let hir::ExprKind::Lit(ref lit) = expr.kind
|
||||
&& !matches!(lit.node, ast::LitKind::Int(0, _))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if let hir::BinOpKind::Mul = op.node
|
||||
&& let hir::ExprKind::Lit(ref lit) = expr.kind
|
||||
&& let ast::LitKind::Int(0 | 1, _) = lit.node
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if the given `expr` has any of the inner `allowed` elements.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ use clippy_utils::binop_traits;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{eq_expr_value, trait_ref_of_method};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir_analysis::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
|
|
@ -65,15 +66,19 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
};
|
||||
|
||||
let mut visitor = ExprVisitor {
|
||||
assignee,
|
||||
counter: 0,
|
||||
cx,
|
||||
};
|
||||
let mut found = false;
|
||||
let found_multiple = for_each_expr(e, |e| {
|
||||
if eq_expr_value(cx, assignee, e) {
|
||||
if found {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.is_some();
|
||||
|
||||
walk_expr(&mut visitor, e);
|
||||
|
||||
if visitor.counter == 1 {
|
||||
if found && !found_multiple {
|
||||
// a = a op b
|
||||
if eq_expr_value(cx, assignee, l) {
|
||||
lint(assignee, r);
|
||||
|
|
@ -98,22 +103,6 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
struct ExprVisitor<'a, 'tcx> {
|
||||
assignee: &'a hir::Expr<'a>,
|
||||
counter: u8,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if eq_expr_value(self.cx, self.assignee, expr) {
|
||||
self.counter += 1;
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet {
|
||||
struct S(hir::HirIdSet);
|
||||
impl Delegate<'_> for S {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::consts::constant_simple;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_integer_literal;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
|
|
@ -50,11 +51,9 @@ impl Context {
|
|||
hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
|
||||
hir::ExprKind::Lit(_lit) => (),
|
||||
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
|
||||
if let hir::ExprKind::Lit(lit) = &expr.kind {
|
||||
if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
}
|
||||
if is_integer_literal(expr, 1) {
|
||||
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
|
||||
self.expr_id = Some(expr.hir_id);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
|
||||
can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks,
|
||||
peel_hir_expr_while, CaptureKind,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
|
|
@ -174,7 +174,8 @@ fn try_get_option_occurrence<'tcx>(
|
|||
|
||||
fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
|
||||
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
|
||||
let res = cx.qpath_res(qpath, pat.hir_id);
|
||||
if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) {
|
||||
return Some(inner_pat);
|
||||
}
|
||||
}
|
||||
|
|
@ -226,9 +227,10 @@ fn try_convert_match<'tcx>(
|
|||
|
||||
fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [first_pat], _) => {
|
||||
is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
|
||||
is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr)
|
||||
&& matches!(first_pat.kind, PatKind::Wild)
|
||||
},
|
||||
PatKind::Wild => true,
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::return_ty;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::expr_visitor_no_bodies;
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Span};
|
||||
|
|
@ -58,18 +59,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
|||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||
let mut panics = Vec::new();
|
||||
expr_visitor_no_bodies(|expr| {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
|
||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, e) else {
|
||||
return ControlFlow::Continue(Descend::Yes);
|
||||
};
|
||||
if matches!(
|
||||
cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
|
||||
) {
|
||||
panics.push(macro_call.span);
|
||||
return false;
|
||||
ControlFlow::Continue(Descend::No)
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
}
|
||||
true
|
||||
})
|
||||
.visit_expr(body.value);
|
||||
});
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg,
|
||||
diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg,
|
||||
ty::is_type_diagnostic_item,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -54,8 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
|
|||
// If the expression is a literal `Option::None`
|
||||
let is_none_ctor = |expr: &Expr<'_>| {
|
||||
!expr.span.from_expansion()
|
||||
&& matches!(&peel_hir_expr_refs(expr).0.kind,
|
||||
ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
|
||||
&& is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone)
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ use clippy_utils::higher;
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{
|
||||
eq_expr_value, get_parent_node, in_constant, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id,
|
||||
eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id,
|
||||
peel_blocks, peel_blocks_with_stmt,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -58,7 +59,7 @@ enum IfBlockType<'hir> {
|
|||
/// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
|
||||
/// if_else (d)
|
||||
IfLet(
|
||||
&'hir QPath<'hir>,
|
||||
Res,
|
||||
Ty<'hir>,
|
||||
Symbol,
|
||||
&'hir Expr<'hir>,
|
||||
|
|
@ -126,7 +127,14 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr:
|
|||
if ddpos.as_opt_usize().is_none();
|
||||
if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(let_expr);
|
||||
let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
|
||||
let if_block = IfBlockType::IfLet(
|
||||
cx.qpath_res(path1, let_pat.hir_id),
|
||||
caller_ty,
|
||||
ident.name,
|
||||
let_expr,
|
||||
if_then,
|
||||
if_else
|
||||
);
|
||||
if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
|
||||
|| is_early_return(sym::Result, cx, &if_block);
|
||||
if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
|
||||
|
|
@ -165,21 +173,21 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_
|
|||
_ => false,
|
||||
}
|
||||
},
|
||||
IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
|
||||
IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
|
||||
is_type_diagnostic_item(cx, let_expr_ty, smbl)
|
||||
&& match smbl {
|
||||
sym::Option => {
|
||||
// We only need to check `if let Some(x) = option` not `if let None = option`,
|
||||
// because the later one will be suggested as `if option.is_none()` thus causing conflict.
|
||||
is_lang_ctor(cx, qpath, OptionSome)
|
||||
is_res_lang_ctor(cx, res, OptionSome)
|
||||
&& if_else.is_some()
|
||||
&& expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
|
||||
},
|
||||
sym::Result => {
|
||||
(is_lang_ctor(cx, qpath, ResultOk)
|
||||
(is_res_lang_ctor(cx, res, ResultOk)
|
||||
&& if_else.is_some()
|
||||
&& expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
|
||||
|| is_lang_ctor(cx, qpath, ResultErr)
|
||||
|| is_res_lang_ctor(cx, res, ResultErr)
|
||||
&& expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
|
||||
},
|
||||
_ => false,
|
||||
|
|
@ -198,7 +206,7 @@ fn expr_return_none_or_err(
|
|||
match peel_blocks_with_stmt(expr).kind {
|
||||
ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
|
||||
ExprKind::Path(ref qpath) => match smbl {
|
||||
sym::Option => is_lang_ctor(cx, qpath, OptionNone),
|
||||
sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone),
|
||||
sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
|
||||
_ => false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ use clippy_utils::{
|
|||
diagnostics::{span_lint, span_lint_and_sugg},
|
||||
higher::{get_vec_init_kind, VecInitKind},
|
||||
source::snippet,
|
||||
visitors::expr_visitor_no_bodies,
|
||||
visitors::for_each_expr,
|
||||
};
|
||||
use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
|
||||
use core::ops::ControlFlow;
|
||||
use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -58,10 +59,8 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
|
||||
{
|
||||
// finds use of `_.read(&mut v)`
|
||||
let mut read_found = false;
|
||||
let mut visitor = expr_visitor_no_bodies(|expr| {
|
||||
if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind
|
||||
let visitor = |expr: &Expr<'_>| {
|
||||
if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind
|
||||
&& let PathSegment { ident: read_or_read_exact, .. } = *path
|
||||
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
|
||||
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
|
||||
|
|
@ -69,27 +68,22 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
&& let [inner_seg] = inner_path.segments
|
||||
&& ident.name == inner_seg.ident.name
|
||||
{
|
||||
read_found = true;
|
||||
}
|
||||
!read_found
|
||||
});
|
||||
|
||||
let next_stmt_span;
|
||||
if idx == block.stmts.len() - 1 {
|
||||
// case { .. stmt; expr }
|
||||
if let Some(e) = block.expr {
|
||||
visitor.visit_expr(e);
|
||||
next_stmt_span = e.span;
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
return;
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
} else {
|
||||
};
|
||||
|
||||
let (read_found, next_stmt_span) =
|
||||
if let Some(next_stmt) = block.stmts.get(idx + 1) {
|
||||
// case { .. stmt; stmt; .. }
|
||||
let next_stmt = &block.stmts[idx + 1];
|
||||
visitor.visit_stmt(next_stmt);
|
||||
next_stmt_span = next_stmt.span;
|
||||
}
|
||||
drop(visitor);
|
||||
(for_each_expr(next_stmt, visitor).is_some(), next_stmt.span)
|
||||
} else if let Some(e) = block.expr {
|
||||
// case { .. stmt; expr }
|
||||
(for_each_expr(e, visitor).is_some(), e.span)
|
||||
} else {
|
||||
return
|
||||
};
|
||||
|
||||
if read_found && !next_stmt_span.from_expansion() {
|
||||
let applicability = Applicability::MaybeIncorrect;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_context};
|
||||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::{fn_def_id, path_to_local_id};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
|
@ -270,33 +272,20 @@ fn emit_return_lint(
|
|||
}
|
||||
|
||||
fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
let mut visitor = BorrowVisitor { cx, borrows: false };
|
||||
walk_expr(&mut visitor, expr);
|
||||
visitor.borrows
|
||||
}
|
||||
|
||||
struct BorrowVisitor<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
borrows: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.borrows || expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(def_id) = fn_def_id(self.cx, expr) {
|
||||
self.borrows = self
|
||||
.cx
|
||||
for_each_expr(expr, |e| {
|
||||
if let Some(def_id) = fn_def_id(cx, e)
|
||||
&& cx
|
||||
.tcx
|
||||
.fn_sig(def_id)
|
||||
.output()
|
||||
.skip_binder()
|
||||
.output()
|
||||
.walk()
|
||||
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
.any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
ControlFlow::Continue(Descend::from(!expr.span.from_expansion()))
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
|
||||
use clippy_utils::{
|
||||
get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
|
||||
|
|
@ -219,8 +220,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
&& path_to_local_id(self_arg, self.vec_alloc.local_id)
|
||||
&& path.ident.name == sym!(resize)
|
||||
// Check that is filled with 0
|
||||
&& let ExprKind::Lit(ref lit) = fill_arg.kind
|
||||
&& let LitKind::Int(0, _) = lit.node {
|
||||
&& is_integer_literal(fill_arg, 0) {
|
||||
// Check that len expression is equals to `with_capacity` expression
|
||||
if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
|
||||
self.slow_expression = Some(InitializationType::Resize(expr));
|
||||
|
|
@ -254,10 +254,8 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
|
||||
if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
|
||||
if let ExprKind::Lit(ref lit) = repeat_arg.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
|
||||
if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat);
|
||||
if is_integer_literal(repeat_arg, 0);
|
||||
then {
|
||||
true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
|
|
@ -92,25 +93,17 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||
}
|
||||
|
||||
fn count_binops(expr: &hir::Expr<'_>) -> u32 {
|
||||
let mut visitor = BinaryExprVisitor::default();
|
||||
visitor.visit_expr(expr);
|
||||
visitor.nb_binops
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct BinaryExprVisitor {
|
||||
nb_binops: u32,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for BinaryExprVisitor {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
match expr.kind {
|
||||
let mut count = 0u32;
|
||||
let _: Option<!> = for_each_expr(expr, |e| {
|
||||
if matches!(
|
||||
e.kind,
|
||||
hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _)
|
||||
| hir::ExprKind::AssignOp(..) => self.nb_binops += 1,
|
||||
_ => {},
|
||||
| hir::ExprKind::Unary(hir::UnOp::Not | hir::UnOp::Neg, _)
|
||||
| hir::ExprKind::AssignOp(..)
|
||||
) {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
count
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_path_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
|
@ -19,37 +17,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
|
||||
if x == 0;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind &&
|
||||
let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) &&
|
||||
x == 0
|
||||
{
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_path_diagnostic_item(cx, func1, sym::ptr_null);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
if let ExprKind::Call(func1, []) = arg.kind &&
|
||||
is_path_diagnostic_item(cx, func1, sym::ptr_null)
|
||||
{
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty};
|
||||
use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq};
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
|
@ -216,7 +215,7 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt
|
|||
let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs();
|
||||
if is_type_diagnostic_item(cx, self_type, sym::Vec)
|
||||
&& path.ident.name.as_str() == "set_len"
|
||||
&& !is_literal_zero(arg)
|
||||
&& !is_integer_literal(arg, 0)
|
||||
{
|
||||
Some((self_expr, expr.span))
|
||||
} else {
|
||||
|
|
@ -226,13 +225,3 @@ fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Opt
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_literal_zero(arg: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(lit) = &arg.kind
|
||||
&& let LitKind::Int(0, _) = lit.node
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::{match_def_path, paths};
|
|||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
|
@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
|
|||
);
|
||||
} else {
|
||||
if_chain! {
|
||||
if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
|
||||
if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(fun_def_id);
|
||||
if let [.., last_arg] = args;
|
||||
if let ExprKind::Lit(spanned) = &last_arg.kind;
|
||||
if let LitKind::Str(symbol, _) = spanned.node;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
|
||||
use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty, visitors::find_all_ret_expressions};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
|
|
@ -120,9 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
if !ret_expr.span.from_expansion();
|
||||
// Check if a function call.
|
||||
if let ExprKind::Call(func, [arg]) = ret_expr.kind;
|
||||
// Check if OPTION_SOME or RESULT_OK, depending on return type.
|
||||
if let ExprKind::Path(qpath) = &func.kind;
|
||||
if is_lang_ctor(cx, qpath, lang_item);
|
||||
if is_res_lang_ctor(cx, path_res(cx, func), lang_item);
|
||||
// Make sure the function argument does not contain a return expression.
|
||||
if !contains_return(arg);
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::{is_try, match_trait_method, paths};
|
||||
use clippy_utils::{is_trait_method, is_try, match_trait_method, paths};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -116,13 +117,13 @@ fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Exp
|
|||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT)
|
||||
} else {
|
||||
match_trait_method(cx, call, &paths::IO_READ)
|
||||
is_trait_method(cx, call, sym::IoRead)
|
||||
};
|
||||
let write_trait = if is_await {
|
||||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|
||||
} else {
|
||||
match_trait_method(cx, call, &paths::IO_WRITE)
|
||||
is_trait_method(cx, call, sym::IoWrite)
|
||||
};
|
||||
|
||||
match (read_trait, write_trait, symbol, is_await) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{method_chain_args, return_ty};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{Expr, ImplItemKind};
|
||||
use rustc_hir::ImplItemKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
|
|
@ -73,51 +73,37 @@ impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
|
|||
}
|
||||
}
|
||||
|
||||
struct FindExpectUnwrap<'a, 'tcx> {
|
||||
lcx: &'a LateContext<'tcx>,
|
||||
typeck_results: &'tcx ty::TypeckResults<'tcx>,
|
||||
result: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
||||
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();
|
||||
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
|
||||
{
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
}
|
||||
|
||||
// and check sub-expressions
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let mut fpu = FindExpectUnwrap {
|
||||
lcx: cx,
|
||||
typeck_results: cx.tcx.typeck(impl_item.def_id.def_id),
|
||||
result: Vec::new(),
|
||||
};
|
||||
fpu.visit_expr(body.value);
|
||||
let typeck = cx.tcx.typeck(impl_item.def_id.def_id);
|
||||
let mut result = Vec::new();
|
||||
let _: Option<!> = for_each_expr(body.value, |e| {
|
||||
// check for `expect`
|
||||
if let Some(arglists) = method_chain_args(e, &["expect"]) {
|
||||
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(cx, receiver_ty, sym::Result)
|
||||
{
|
||||
result.push(e.span);
|
||||
}
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(e, &["unwrap"]) {
|
||||
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(cx, receiver_ty, sym::Result)
|
||||
{
|
||||
result.push(e.span);
|
||||
}
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
|
||||
// if we've found one, lint
|
||||
if !fpu.result.is_empty() {
|
||||
if !result.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNWRAP_IN_RESULT,
|
||||
|
|
@ -125,7 +111,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc
|
|||
"used unwrap or expect in a function that returns result or option",
|
||||
move |diag| {
|
||||
diag.help("unwrap and expect should not be used in a function that returns result or option");
|
||||
diag.span_note(fpu.result, "potential non-recoverable error(s)");
|
||||
diag.span_note(result, "potential non-recoverable error(s)");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
|
|||
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
|
@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
}
|
||||
|
||||
if_chain! {
|
||||
if match_def_path(cx, def_id, &paths::FROM_FROM);
|
||||
if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id);
|
||||
if same_type_and_consts(a, b);
|
||||
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
|
|||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{
|
||||
def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
|
||||
method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
|
||||
def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_any_def_paths,
|
||||
match_def_path, method_calls, paths, peel_blocks_with_stmt, peel_hir_expr_refs, SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast as ast;
|
||||
|
|
@ -20,21 +20,24 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{
|
||||
BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
|
||||
BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind,
|
||||
TyKind, UnOp,
|
||||
};
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
|
||||
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
|
||||
use rustc_middle::ty::{
|
||||
self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty,
|
||||
};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::str;
|
||||
|
||||
#[cfg(feature = "internal")]
|
||||
pub mod metadata_collector;
|
||||
|
|
@ -226,11 +229,11 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `utils::match_type()` on a type diagnostic item
|
||||
/// and suggests to use `utils::is_type_diagnostic_item()` instead.
|
||||
/// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
|
||||
/// The path for an item is subject to change and is less efficient to look up than a
|
||||
/// diagnostic item or a `LangItem`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
|
|
@ -241,9 +244,9 @@ declare_clippy_lint! {
|
|||
/// ```rust,ignore
|
||||
/// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
/// ```
|
||||
pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
pub UNNECESSARY_DEF_PATH,
|
||||
internal,
|
||||
"using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
|
||||
"using a def path when a diagnostic item or a `LangItem` is available"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -537,7 +540,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
|
||||
fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let TyKind::Rptr(
|
||||
_,
|
||||
MutTy {
|
||||
|
|
@ -888,89 +891,225 @@ fn suggest_note(
|
|||
);
|
||||
}
|
||||
|
||||
declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
|
||||
declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
|
||||
enum Item {
|
||||
LangItem(Symbol),
|
||||
DiagnosticItem(Symbol),
|
||||
}
|
||||
static PATHS: &[&[&str]] = &[
|
||||
&["clippy_utils", "match_def_path"],
|
||||
&["clippy_utils", "match_trait_method"],
|
||||
&["clippy_utils", "ty", "match_type"],
|
||||
&["clippy_utils", "is_expr_path_def_path"],
|
||||
];
|
||||
|
||||
if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
// Check if this is a call to utils::match_type()
|
||||
if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
|
||||
if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
|
||||
if let ExprKind::Call(func, [cx_arg, def_arg, args@..]) = expr.kind;
|
||||
if let ExprKind::Path(path) = &func.kind;
|
||||
if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id();
|
||||
if let Some(which_path) = match_any_def_paths(cx, id, PATHS);
|
||||
let item_arg = if which_path == 4 { &args[1] } else { &args[0] };
|
||||
// Extract the path to the matched type
|
||||
if let Some(segments) = path_to_matched_type(cx, ty_path);
|
||||
let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
|
||||
if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
|
||||
// Check if the matched type is a diagnostic item
|
||||
if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
|
||||
if let Some(segments) = path_to_matched_type(cx, item_arg);
|
||||
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
|
||||
if let Some(def_id) = def_path_res(cx, &segments[..]).opt_def_id();
|
||||
then {
|
||||
// TODO: check paths constants from external crates.
|
||||
let cx_snippet = snippet(cx, context.span, "_");
|
||||
let ty_snippet = snippet(cx, ty.span, "_");
|
||||
// def_path_res will match field names before anything else, but for this we want to match
|
||||
// inherent functions first.
|
||||
let def_id = if cx.tcx.def_kind(def_id) == DefKind::Field {
|
||||
let method_name = *segments.last().unwrap();
|
||||
cx.tcx.def_key(def_id).parent
|
||||
.and_then(|parent_idx|
|
||||
cx.tcx.inherent_impls(DefId { index: parent_idx, krate: def_id.krate }).iter()
|
||||
.find_map(|impl_id| cx.tcx.associated_items(*impl_id)
|
||||
.find_by_name_and_kind(
|
||||
cx.tcx,
|
||||
Ident::from_str(method_name),
|
||||
AssocKind::Fn,
|
||||
*impl_id,
|
||||
)
|
||||
)
|
||||
)
|
||||
.map_or(def_id, |item| item.def_id)
|
||||
} else {
|
||||
def_id
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
// Check if the target item is a diagnostic item or LangItem.
|
||||
let (msg, item) = if let Some(item_name)
|
||||
= cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
|
||||
{
|
||||
(
|
||||
"use of a def path to a diagnostic item",
|
||||
Item::DiagnosticItem(*item_name),
|
||||
)
|
||||
} else if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) {
|
||||
let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"]).def_id();
|
||||
let item_name = cx.tcx.adt_def(lang_items).variants().iter().nth(lang_item).unwrap().name;
|
||||
(
|
||||
"use of a def path to a `LangItem`",
|
||||
Item::LangItem(item_name),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let has_ctor = match cx.tcx.def_kind(def_id) {
|
||||
DefKind::Struct => {
|
||||
let variant = cx.tcx.adt_def(def_id).non_enum_variant();
|
||||
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
|
||||
}
|
||||
DefKind::Variant => {
|
||||
let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
|
||||
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
|
||||
let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
|
||||
let (sugg, with_note) = match (which_path, item) {
|
||||
// match_def_path
|
||||
(0, Item::DiagnosticItem(item)) =>
|
||||
(format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"), has_ctor),
|
||||
(0, Item::LangItem(item)) => (
|
||||
format!("{cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some({def_snip})"),
|
||||
has_ctor
|
||||
),
|
||||
// match_trait_method
|
||||
(1, Item::DiagnosticItem(item)) =>
|
||||
(format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false),
|
||||
// match_type
|
||||
(2, Item::DiagnosticItem(item)) =>
|
||||
(format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false),
|
||||
(2, Item::LangItem(item)) =>
|
||||
(format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"), false),
|
||||
// is_expr_path_def_path
|
||||
(3, Item::DiagnosticItem(item)) if has_ctor => (
|
||||
format!(
|
||||
"is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",
|
||||
),
|
||||
false,
|
||||
),
|
||||
(3, Item::LangItem(item)) if has_ctor => (
|
||||
format!(
|
||||
"is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",
|
||||
),
|
||||
false,
|
||||
),
|
||||
(3, Item::DiagnosticItem(item)) =>
|
||||
(format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"), false),
|
||||
(3, Item::LangItem(item)) => (
|
||||
format!(
|
||||
"path_res({cx_snip}, {def_snip}).opt_def_id()\
|
||||
.map_or(false, |id| {cx_snip}.tcx.lang_items().require(LangItem::{item}).ok() == Some(id))",
|
||||
),
|
||||
false,
|
||||
),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
UNNECESSARY_DEF_PATH,
|
||||
expr.span,
|
||||
"usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
|
||||
"try",
|
||||
format!("clippy_utils::ty::is_type_diagnostic_item({cx_snippet}, {ty_snippet}, sym::{item_name})"),
|
||||
Applicability::MaybeIncorrect,
|
||||
msg,
|
||||
|diag| {
|
||||
diag.span_suggestion(expr.span, "try", sugg, app);
|
||||
if with_note {
|
||||
diag.help(
|
||||
"if this `DefId` came from a constructor expression or pattern then the \
|
||||
parent `DefId` should be used instead"
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
|
||||
use rustc_hir::ItemKind;
|
||||
|
||||
match &expr.kind {
|
||||
ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
|
||||
ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
|
||||
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<String>> {
|
||||
match peel_hir_expr_refs(expr).0.kind {
|
||||
ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) {
|
||||
Res::Local(hir_id) => {
|
||||
let parent_id = cx.tcx.hir().get_parent_node(hir_id);
|
||||
if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
|
||||
if let Some(init) = local.init {
|
||||
return path_to_matched_type(cx, init);
|
||||
}
|
||||
}
|
||||
},
|
||||
Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
ExprKind::Array(exprs) => {
|
||||
let segments: Vec<Symbol> = exprs
|
||||
.iter()
|
||||
.filter_map(|expr| {
|
||||
if let ExprKind::Lit(lit) = &expr.kind {
|
||||
if let LitKind::Str(sym, _) = lit.node {
|
||||
return Some(sym);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) {
|
||||
path_to_matched_type(cx, init)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if segments.len() == exprs.len() {
|
||||
return Some(segments);
|
||||
}
|
||||
}
|
||||
},
|
||||
Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
|
||||
cx,
|
||||
cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
|
||||
cx.tcx.type_of(def_id),
|
||||
),
|
||||
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
|
||||
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
|
||||
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id))
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
ExprKind::Array(exprs) => exprs
|
||||
.iter()
|
||||
.map(|expr| {
|
||||
if let ExprKind::Lit(lit) = &expr.kind {
|
||||
if let LitKind::Str(sym, _) = lit.node {
|
||||
return Some((*sym.as_str()).to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
})
|
||||
.collect(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
|
||||
let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
|
||||
let &alloc = alloc.provenance().values().next()?;
|
||||
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
|
||||
(alloc.inner(), ty)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
(alloc, ty)
|
||||
};
|
||||
|
||||
if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
|
||||
&& let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
|
||||
&& ty.is_str()
|
||||
{
|
||||
alloc
|
||||
.provenance()
|
||||
.values()
|
||||
.map(|&alloc| {
|
||||
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
|
||||
let alloc = alloc.inner();
|
||||
str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
|
||||
.ok().map(ToOwned::to_owned)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a complete resolver for paths. It works on all the paths currently used in the paths
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue