mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Auto merge of #11750 - Alexendoo:let-chains, r=flip1995
Replace if_chain with let chains Closes #9353 Let chains are now supported by rustfmt 🎉 The PR is split into two commits 1. The result of running [`if-to-let-chain clippy*/**/*.rs`](https://github.com/Alexendoo/if-to-let-chain) 2. The manual clean up: fixing some errors/formatting, dogfood lints, removing the if_chain internal lint r? `@flip1995` changelog: none
This commit is contained in:
commit
9a4dd106d9
245 changed files with 8185 additions and 9444 deletions
|
@ -14,7 +14,6 @@ cargo_metadata = "0.15.3"
|
|||
clippy_config = { path = "../clippy_config" }
|
||||
clippy_utils = { path = "../clippy_utils" }
|
||||
declare_clippy_lint = { path = "../declare_clippy_lint" }
|
||||
if_chain = "1.0"
|
||||
itertools = "0.10.1"
|
||||
quine-mc_cluskey = "0.2"
|
||||
regex-syntax = "0.7"
|
||||
|
|
|
@ -52,14 +52,13 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
|
|||
impl LateLintPass<'_> for AllowAttribute {
|
||||
// Separate each crate's features.
|
||||
fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), attr.span);
|
||||
if cx.tcx.features().lint_reasons;
|
||||
if let AttrStyle::Outer = attr.style;
|
||||
if let Some(ident) = attr.ident();
|
||||
if ident.name == rustc_span::symbol::sym::allow;
|
||||
if !is_from_proc_macro(cx, &attr);
|
||||
then {
|
||||
if !in_external_macro(cx.sess(), attr.span)
|
||||
&& cx.tcx.features().lint_reasons
|
||||
&& let AttrStyle::Outer = attr.style
|
||||
&& let Some(ident) = attr.ident()
|
||||
&& ident.name == rustc_span::symbol::sym::allow
|
||||
&& !is_from_proc_macro(cx, &attr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ALLOW_ATTRIBUTES,
|
||||
|
@ -71,5 +70,4 @@ impl LateLintPass<'_> for AllowAttribute {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
|
|||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::macros::{is_panic, macro_backtrace};
|
||||
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::token::{Token, TokenKind};
|
||||
use rustc_ast::tokenstream::TokenTree;
|
||||
use rustc_ast::{
|
||||
|
@ -471,17 +470,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
return;
|
||||
}
|
||||
for item in items {
|
||||
if_chain! {
|
||||
if let NestedMetaItem::MetaItem(mi) = &item;
|
||||
if let MetaItemKind::NameValue(lit) = &mi.kind;
|
||||
if mi.has_name(sym::since);
|
||||
then {
|
||||
if let NestedMetaItem::MetaItem(mi) = &item
|
||||
&& let MetaItemKind::NameValue(lit) = &mi.kind
|
||||
&& mi.has_name(sym::since)
|
||||
{
|
||||
check_semver(cx, item.span(), lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if attr.has_name(sym::should_panic) {
|
||||
check_should_panic_reason(cx, attr);
|
||||
}
|
||||
|
@ -580,16 +577,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
|||
|
||||
/// Returns the lint name if it is clippy lint.
|
||||
fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
|
||||
if_chain! {
|
||||
if let Some(meta_item) = lint.meta_item();
|
||||
if meta_item.path.segments.len() > 1;
|
||||
if let tool_name = meta_item.path.segments[0].ident;
|
||||
if tool_name.name == sym::clippy;
|
||||
then {
|
||||
if let Some(meta_item) = lint.meta_item()
|
||||
&& meta_item.path.segments.len() > 1
|
||||
&& let tool_name = meta_item.path.segments[0].ident
|
||||
&& tool_name.name == sym::clippy
|
||||
{
|
||||
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
|
||||
return Some(lint_name);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -857,18 +852,17 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
|
|||
}
|
||||
|
||||
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
|
||||
if_chain! {
|
||||
if msrv.meets(msrvs::TOOL_ATTRIBUTES);
|
||||
if msrv.meets(msrvs::TOOL_ATTRIBUTES)
|
||||
// check cfg_attr
|
||||
if attr.has_name(sym::cfg_attr);
|
||||
if let Some(items) = attr.meta_item_list();
|
||||
if items.len() == 2;
|
||||
&& attr.has_name(sym::cfg_attr)
|
||||
&& let Some(items) = attr.meta_item_list()
|
||||
&& items.len() == 2
|
||||
// check for `rustfmt`
|
||||
if let Some(feature_item) = items[0].meta_item();
|
||||
if feature_item.has_name(sym::rustfmt);
|
||||
&& let Some(feature_item) = items[0].meta_item()
|
||||
&& feature_item.has_name(sym::rustfmt)
|
||||
// check for `rustfmt_skip` and `rustfmt::skip`
|
||||
if let Some(skip_item) = &items[1].meta_item();
|
||||
if skip_item.has_name(sym!(rustfmt_skip))
|
||||
&& let Some(skip_item) = &items[1].meta_item()
|
||||
&& (skip_item.has_name(sym!(rustfmt_skip))
|
||||
|| skip_item
|
||||
.path
|
||||
.segments
|
||||
|
@ -876,11 +870,11 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
|
|||
.expect("empty path in attribute")
|
||||
.ident
|
||||
.name
|
||||
== sym::skip;
|
||||
== sym::skip)
|
||||
// Only lint outer attributes, because custom inner attributes are unstable
|
||||
// Tracking issue: https://github.com/rust-lang/rust/issues/54726
|
||||
if attr.style == AttrStyle::Outer;
|
||||
then {
|
||||
&& attr.style == AttrStyle::Outer
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DEPRECATED_CFG_ATTR,
|
||||
|
@ -891,7 +885,6 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||
|
@ -990,13 +983,11 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
mismatched.extend(find_mismatched_target_os(list));
|
||||
},
|
||||
MetaItemKind::Word => {
|
||||
if_chain! {
|
||||
if let Some(ident) = meta.ident();
|
||||
if let Some(os) = find_os(ident.name.as_str());
|
||||
then {
|
||||
if let Some(ident) = meta.ident()
|
||||
&& let Some(os) = find_os(ident.name.as_str())
|
||||
{
|
||||
mismatched.push((os, ident.span));
|
||||
}
|
||||
}
|
||||
},
|
||||
MetaItemKind::NameValue(..) => {},
|
||||
}
|
||||
|
@ -1006,12 +997,11 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
mismatched
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if attr.has_name(sym::cfg);
|
||||
if let Some(list) = attr.meta_item_list();
|
||||
let mismatched = find_mismatched_target_os(&list);
|
||||
if !mismatched.is_empty();
|
||||
then {
|
||||
if attr.has_name(sym::cfg)
|
||||
&& let Some(list) = attr.meta_item_list()
|
||||
&& let mismatched = find_mismatched_target_os(&list)
|
||||
&& !mismatched.is_empty()
|
||||
{
|
||||
let mess = "operating system used in target family position";
|
||||
|
||||
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
|
||||
|
@ -1030,7 +1020,6 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_lint_level(symbol: Symbol) -> bool {
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait;
|
|||
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||
use clippy_utils::{get_parent_expr, higher};
|
||||
use core::ops::ControlFlow;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BlockCheckMode, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -114,16 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
|
|||
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 {
|
||||
if let Some(parent) = get_parent_expr(cx, e)
|
||||
&& let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
|
||||
&& let caller = cx.typeck_results().expr_ty(self_arg)
|
||||
&& let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||
&& implements_trait(cx, caller, iter_id, &[])
|
||||
{
|
||||
return ControlFlow::Continue(Descend::No);
|
||||
}
|
||||
}
|
||||
|
||||
let body = cx.tcx.hir().body(closure.body);
|
||||
let ex = &body.value;
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
|||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
|
@ -152,19 +151,17 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
|||
return Ok(Bool::Term(n as u8));
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
|
||||
if implements_ord(self.cx, e_lhs);
|
||||
if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
|
||||
if negate(e_binop.node) == Some(expr_binop.node);
|
||||
if eq_expr_value(self.cx, e_lhs, expr_lhs);
|
||||
if eq_expr_value(self.cx, e_rhs, expr_rhs);
|
||||
then {
|
||||
if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind
|
||||
&& implements_ord(self.cx, e_lhs)
|
||||
&& let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind
|
||||
&& negate(e_binop.node) == Some(expr_binop.node)
|
||||
&& eq_expr_value(self.cx, e_lhs, expr_lhs)
|
||||
&& eq_expr_value(self.cx, e_rhs, expr_rhs)
|
||||
{
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
|
||||
}
|
||||
}
|
||||
}
|
||||
let n = self.terminals.len();
|
||||
self.terminals.push(e);
|
||||
if n < 32 {
|
||||
|
|
|
@ -49,30 +49,29 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if !e.span.from_expansion();
|
||||
if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
|
||||
if !addrof_target.span.from_expansion();
|
||||
if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
|
||||
if !deref_target.span.from_expansion();
|
||||
if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
|
||||
let ref_ty = cx.typeck_results().expr_ty(deref_target);
|
||||
if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
|
||||
if !is_from_proc_macro(cx, e);
|
||||
then{
|
||||
|
||||
if let Some(parent_expr) = get_parent_expr(cx, e){
|
||||
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
|
||||
!is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
|
||||
if !e.span.from_expansion()
|
||||
&& let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
|
||||
&& !addrof_target.span.from_expansion()
|
||||
&& let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
|
||||
&& !deref_target.span.from_expansion()
|
||||
&& !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& let ref_ty = cx.typeck_results().expr_ty(deref_target)
|
||||
&& let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
|
||||
&& !is_from_proc_macro(cx, e)
|
||||
{
|
||||
if let Some(parent_expr) = get_parent_expr(cx, e) {
|
||||
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// modification to `&mut &*x` is different from `&mut x`
|
||||
if matches!(deref_target.kind, ExprKind::Path(..)
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Index(..)
|
||||
| ExprKind::Unary(UnOp::Deref, ..))
|
||||
&& matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
|
||||
if matches!(
|
||||
deref_target.kind,
|
||||
ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..)
|
||||
) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -87,12 +86,12 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
|||
e.span,
|
||||
"if you would like to reborrow, try removing `&*`",
|
||||
snippet_opt(cx, deref_target.span).unwrap(),
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
// has deref trait -> give 2 help
|
||||
// doesn't have deref trait -> give 1 help
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
|
||||
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
|
||||
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
|
||||
return;
|
||||
}
|
||||
|
@ -101,17 +100,11 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
|
|||
diag.span_suggestion(
|
||||
e.span,
|
||||
"if you would like to deref, try using `&**`",
|
||||
format!(
|
||||
"&**{}",
|
||||
&snippet_opt(cx, deref_target.span).unwrap(),
|
||||
),
|
||||
Applicability::MaybeIncorrect
|
||||
format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -15,12 +14,15 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
|||
let mut packages = metadata.packages.clone();
|
||||
packages.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
if_chain! {
|
||||
if let Some(resolve) = &metadata.resolve;
|
||||
if let Some(local_id) = packages
|
||||
.iter()
|
||||
.find_map(|p| if p.name == local_name.as_str() { Some(&p.id) } else { None });
|
||||
then {
|
||||
if let Some(resolve) = &metadata.resolve
|
||||
&& let Some(local_id) = packages.iter().find_map(|p| {
|
||||
if p.name == local_name.as_str() {
|
||||
Some(&p.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
|
||||
let group: Vec<&Package> = group.collect();
|
||||
|
||||
|
@ -42,7 +44,6 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use cargo_metadata::Metadata;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use if_chain::if_chain;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::DUMMY_SP;
|
||||
|
||||
|
@ -9,12 +8,11 @@ use super::WILDCARD_DEPENDENCIES;
|
|||
pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
||||
for dep in &metadata.packages[0].dependencies {
|
||||
// VersionReq::any() does not work
|
||||
if_chain! {
|
||||
if let Ok(wildcard_ver) = semver::VersionReq::parse("*");
|
||||
if let Some(ref source) = dep.source;
|
||||
if !source.starts_with("git");
|
||||
if dep.req == wildcard_ver;
|
||||
then {
|
||||
if let Ok(wildcard_ver) = semver::VersionReq::parse("*")
|
||||
&& let Some(ref source) = dep.source
|
||||
&& !source.starts_with("git")
|
||||
&& dep.req == wildcard_ver
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
WILDCARD_DEPENDENCIES,
|
||||
|
@ -23,5 +21,4 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{method_chain_args, sext};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
@ -28,28 +27,24 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
|
|||
|
||||
// Don't lint for positive constants.
|
||||
let const_val = constant(cx, cx.typeck_results(), cast_op);
|
||||
if_chain! {
|
||||
if let Some(Constant::Int(n)) = const_val;
|
||||
if let ty::Int(ity) = *cast_from.kind();
|
||||
if sext(cx.tcx, n, ity) >= 0;
|
||||
then {
|
||||
if let Some(Constant::Int(n)) = const_val
|
||||
&& let ty::Int(ity) = *cast_from.kind()
|
||||
&& sext(cx.tcx, n, ity) >= 0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't lint for the result of methods that always return non-negative values.
|
||||
if let ExprKind::MethodCall(path, ..) = cast_op.kind {
|
||||
let mut method_name = path.ident.name.as_str();
|
||||
let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
|
||||
|
||||
if_chain! {
|
||||
if method_name == "unwrap";
|
||||
if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]);
|
||||
if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind;
|
||||
then {
|
||||
if method_name == "unwrap"
|
||||
&& let Some(arglist) = method_chain_args(cast_op, &["unwrap"])
|
||||
&& let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
|
||||
{
|
||||
method_name = inner_path.ident.name.as_str();
|
||||
}
|
||||
}
|
||||
|
||||
if allowed_methods.iter().any(|&name| method_name == name) {
|
||||
return false;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -69,10 +68,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv
|
|||
|
||||
fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let map = cx.tcx.hir();
|
||||
if_chain! {
|
||||
if let Some(parent_id) = map.opt_parent_id(expr.hir_id);
|
||||
if let Some(parent) = map.find(parent_id);
|
||||
then {
|
||||
if let Some(parent_id) = map.opt_parent_id(expr.hir_id)
|
||||
&& let Some(parent) = map.find(parent_id)
|
||||
{
|
||||
let expr = match parent {
|
||||
Node::Block(block) => {
|
||||
if let Some(parent_expr) = block.expr {
|
||||
|
@ -89,7 +87,6 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -25,20 +24,19 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
|
|||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
|
||||
if_chain! {
|
||||
if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS);
|
||||
if let ty::RawPtr(ptrty) = cast_to.kind();
|
||||
if let ty::Slice(_) = ptrty.ty.kind();
|
||||
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
|
||||
if let ExprKind::Path(ref qpath) = fun.kind;
|
||||
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
|
||||
if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
|
||||
let ctxt = expr.span.ctxt();
|
||||
if cast_expr.span.ctxt() == ctxt;
|
||||
then {
|
||||
if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS)
|
||||
&& let ty::RawPtr(ptrty) = cast_to.kind()
|
||||
&& let ty::Slice(_) = ptrty.ty.kind()
|
||||
&& let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind
|
||||
&& let ExprKind::Path(ref qpath) = fun.kind
|
||||
&& let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
|
||||
&& let Some(rpk) = raw_parts_kind(cx, fun_def_id)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& cast_expr.span.ctxt() == ctxt
|
||||
{
|
||||
let func = match rpk {
|
||||
RawPartsKind::Immutable => "from_raw_parts",
|
||||
RawPartsKind::Mutable => "from_raw_parts_mut"
|
||||
RawPartsKind::Mutable => "from_raw_parts_mut",
|
||||
};
|
||||
let span = expr.span;
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
@ -51,8 +49,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
&format!("casting the result of `{func}` to {cast_to}"),
|
||||
"replace with",
|
||||
format!("core::ptr::slice_{func}({ptr}, {len})"),
|
||||
applicability
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -10,12 +9,11 @@ use rustc_middle::ty::{self, UintTy};
|
|||
use super::CHAR_LIT_AS_U8;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(e, _) = &expr.kind;
|
||||
if let ExprKind::Lit(l) = &e.kind;
|
||||
if let LitKind::Char(c) = l.node;
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind();
|
||||
then {
|
||||
if let ExprKind::Cast(e, _) = &expr.kind
|
||||
&& let ExprKind::Lit(l) = &e.kind
|
||||
&& let LitKind::Char(c) = l.node
|
||||
&& ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind()
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
|
||||
|
||||
|
@ -35,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -17,14 +16,21 @@ pub(super) fn check<'tcx>(
|
|||
cast_to: Ty<'tcx>,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if_chain! {
|
||||
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS);
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind();
|
||||
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind();
|
||||
if matches!((from_mutbl, to_mutbl),
|
||||
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not));
|
||||
if from_ty == to_ty;
|
||||
then {
|
||||
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
|
||||
&& let ty::RawPtr(TypeAndMut {
|
||||
mutbl: from_mutbl,
|
||||
ty: from_ty,
|
||||
}) = cast_from.kind()
|
||||
&& let ty::RawPtr(TypeAndMut {
|
||||
mutbl: to_mutbl,
|
||||
ty: to_ty,
|
||||
}) = cast_to.kind()
|
||||
&& matches!(
|
||||
(from_mutbl, to_mutbl),
|
||||
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)
|
||||
)
|
||||
&& from_ty == to_ty
|
||||
{
|
||||
let sugg = Sugg::hir(cx, cast_expr, "_");
|
||||
let constness = match *to_mutbl {
|
||||
Mutability::Not => "const",
|
||||
|
@ -41,5 +47,4 @@ pub(super) fn check<'tcx>(
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use clippy_utils::numeric_literal::NumericLiteral;
|
|||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::visitors::{for_each_expr, Visitable};
|
||||
use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -25,16 +24,15 @@ pub(super) fn check<'tcx>(
|
|||
) -> bool {
|
||||
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
|
||||
|
||||
if_chain! {
|
||||
if let ty::RawPtr(..) = cast_from.kind();
|
||||
if let ty::RawPtr(..) = cast_from.kind()
|
||||
// check both mutability and type are the same
|
||||
if cast_from.kind() == cast_to.kind();
|
||||
if let ExprKind::Cast(_, cast_to_hir) = expr.kind;
|
||||
&& cast_from.kind() == cast_to.kind()
|
||||
&& let ExprKind::Cast(_, cast_to_hir) = expr.kind
|
||||
// Ignore casts to e.g. type aliases and infer types
|
||||
// - p as pointer_alias
|
||||
// - p as _
|
||||
if let TyKind::Ptr(to_pointee) = cast_to_hir.kind;
|
||||
then {
|
||||
&& let TyKind::Ptr(to_pointee) = cast_to_hir.kind
|
||||
{
|
||||
match to_pointee.ty.kind {
|
||||
// Ignore casts to pointers that are aliases or cfg dependant, e.g.
|
||||
// - p as *const std::ffi::c_char (alias)
|
||||
|
@ -53,13 +51,14 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"),
|
||||
&format!(
|
||||
"casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"
|
||||
),
|
||||
"try",
|
||||
cast_str.clone(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// skip cast of local that is a type alias
|
||||
if let ExprKind::Cast(inner, ..) = expr.kind
|
||||
|
@ -86,15 +85,13 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
// skip cast to non-primitive type
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(_, cast_to) = expr.kind;
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
|
||||
if let Res::PrimTy(_) = path.res;
|
||||
then {}
|
||||
else {
|
||||
if let ExprKind::Cast(_, cast_to) = expr.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind
|
||||
&& let Res::PrimTy(_) = path.res
|
||||
{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// skip cast of fn call that returns type alias
|
||||
if let ExprKind::Cast(inner, ..) = expr.kind
|
||||
|
@ -106,18 +103,19 @@ pub(super) fn check<'tcx>(
|
|||
if let Some(lit) = get_numeric_literal(cast_expr) {
|
||||
let literal_str = &cast_str;
|
||||
|
||||
if_chain! {
|
||||
if let LitKind::Int(n, _) = lit.node;
|
||||
if let Some(src) = snippet_opt(cx, cast_expr.span);
|
||||
if cast_to.is_floating_point();
|
||||
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
|
||||
let from_nbits = 128 - n.leading_zeros();
|
||||
let to_nbits = fp_ty_mantissa_nbits(cast_to);
|
||||
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
|
||||
then {
|
||||
if let LitKind::Int(n, _) = lit.node
|
||||
&& let Some(src) = snippet_opt(cx, cast_expr.span)
|
||||
&& cast_to.is_floating_point()
|
||||
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
|
||||
&& let from_nbits = 128 - n.leading_zeros()
|
||||
&& let to_nbits = fp_ty_mantissa_nbits(cast_to)
|
||||
&& from_nbits != 0
|
||||
&& to_nbits != 0
|
||||
&& from_nbits <= to_nbits
|
||||
&& num_lit.is_decimal()
|
||||
{
|
||||
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
|
||||
return true
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
match lit.node {
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_config::msrvs::{self, Msrv};
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
|
@ -55,12 +54,10 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
|||
return;
|
||||
}
|
||||
|
||||
let result = if_chain! {
|
||||
if !in_constant(cx, item.hir_id);
|
||||
if !in_external_macro(cx.sess(), item.span);
|
||||
if let ExprKind::Binary(op, left, right) = &item.kind;
|
||||
|
||||
then {
|
||||
let result = if !in_constant(cx, item.hir_id)
|
||||
&& !in_external_macro(cx.sess(), item.span)
|
||||
&& let ExprKind::Binary(op, left, right) = &item.kind
|
||||
{
|
||||
match op.node {
|
||||
BinOpKind::Ge | BinOpKind::Le => single_check(item),
|
||||
BinOpKind::And => double_check(cx, left, right),
|
||||
|
@ -68,7 +65,6 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
|
|||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(cv) = result {
|
||||
|
@ -193,17 +189,14 @@ impl ConversionType {
|
|||
|
||||
/// Check for `expr <= (to_type::MAX as from_type)`
|
||||
fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref op, left, right) = &expr.kind;
|
||||
if let Some((candidate, check)) = normalize_le_ge(op, left, right);
|
||||
if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX");
|
||||
|
||||
then {
|
||||
if let ExprKind::Binary(ref op, left, right) = &expr.kind
|
||||
&& let Some((candidate, check)) = normalize_le_ge(op, left, right)
|
||||
&& let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX")
|
||||
{
|
||||
Conversion::try_new(candidate, from, to)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for `expr >= 0|(to_type::MIN as from_type)`
|
||||
|
@ -243,34 +236,28 @@ fn get_types_from_cast<'a>(
|
|||
) -> Option<(&'a str, &'a str)> {
|
||||
// `to_type::max_value() as from_type`
|
||||
// or `to_type::MAX as from_type`
|
||||
let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
|
||||
let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
|
||||
// to_type::max_value(), from_type
|
||||
if let ExprKind::Cast(limit, from_type) = &expr.kind;
|
||||
if let TyKind::Path(ref from_type_path) = &from_type.kind;
|
||||
if let Some(from_sym) = int_ty_to_sym(from_type_path);
|
||||
|
||||
then {
|
||||
&& let TyKind::Path(ref from_type_path) = &from_type.kind
|
||||
&& let Some(from_sym) = int_ty_to_sym(from_type_path)
|
||||
{
|
||||
Some((limit, from_sym))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// `from_type::from(to_type::max_value())`
|
||||
let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(from_func, [limit]) = &expr.kind
|
||||
// `from_type::from, to_type::max_value()`
|
||||
if let ExprKind::Call(from_func, [limit]) = &expr.kind;
|
||||
// `from_type::from`
|
||||
if let ExprKind::Path(ref path) = &from_func.kind;
|
||||
if let Some(from_sym) = get_implementing_type(path, INTS, "from");
|
||||
|
||||
then {
|
||||
&& let ExprKind::Path(ref path) = &from_func.kind
|
||||
&& let Some(from_sym) = get_implementing_type(path, INTS, "from")
|
||||
{
|
||||
Some((limit, from_sym))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Some((limit, from_type)) = limit_from {
|
||||
|
@ -298,32 +285,28 @@ fn get_types_from_cast<'a>(
|
|||
|
||||
/// Gets the type which implements the called function
|
||||
fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
|
||||
if_chain! {
|
||||
if let QPath::TypeRelative(ty, path) = &path;
|
||||
if path.ident.name.as_str() == function;
|
||||
if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind;
|
||||
if let [int] = tp.segments;
|
||||
then {
|
||||
if let QPath::TypeRelative(ty, path) = &path
|
||||
&& path.ident.name.as_str() == function
|
||||
&& let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind
|
||||
&& let [int] = tp.segments
|
||||
{
|
||||
let name = int.ident.name.as_str();
|
||||
candidates.iter().find(|c| &name == *c).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the type as a string, if it is a supported integer
|
||||
fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
|
||||
if_chain! {
|
||||
if let QPath::Resolved(_, path) = *path;
|
||||
if let [ty] = path.segments;
|
||||
then {
|
||||
if let QPath::Resolved(_, path) = *path
|
||||
&& let [ty] = path.segments
|
||||
{
|
||||
let name = ty.ident.name.as_str();
|
||||
INTS.iter().find(|c| &name == *c).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Will return the expressions as if they were expr1 <= expr2
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
|
@ -121,18 +120,21 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
|
|||
}
|
||||
|
||||
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
|
||||
if_chain! {
|
||||
if let ast::ExprKind::Block(ref block, _) = else_.kind;
|
||||
if !block_starts_with_comment(cx, block);
|
||||
if let Some(else_) = expr_block(block);
|
||||
if else_.attrs.is_empty();
|
||||
if !else_.span.from_expansion();
|
||||
if let ast::ExprKind::If(..) = else_.kind;
|
||||
then {
|
||||
if let ast::ExprKind::Block(ref block, _) = else_.kind
|
||||
&& !block_starts_with_comment(cx, block)
|
||||
&& let Some(else_) = expr_block(block)
|
||||
&& else_.attrs.is_empty()
|
||||
&& !else_.span.from_expansion()
|
||||
&& let ast::ExprKind::If(..) = else_.kind
|
||||
{
|
||||
// Prevent "elseif"
|
||||
// Check that the "else" is followed by whitespace
|
||||
let up_to_else = then_span.between(block.span);
|
||||
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
|
||||
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
|
||||
!c.is_whitespace()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
|
@ -149,21 +151,24 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_:
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
|
||||
if_chain! {
|
||||
if !block_starts_with_comment(cx, then);
|
||||
if let Some(inner) = expr_block(then);
|
||||
if inner.attrs.is_empty();
|
||||
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
|
||||
if !block_starts_with_comment(cx, then)
|
||||
&& let Some(inner) = expr_block(then)
|
||||
&& inner.attrs.is_empty()
|
||||
&& let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind
|
||||
// Prevent triggering on `if c { if let a = b { .. } }`.
|
||||
if !matches!(check_inner.kind, ast::ExprKind::Let(..));
|
||||
let ctxt = expr.span.ctxt();
|
||||
if inner.span.ctxt() == ctxt;
|
||||
then {
|
||||
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
|
||||
&& !matches!(check_inner.kind, ast::ExprKind::Let(..))
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& inner.span.ctxt() == ctxt
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_IF,
|
||||
expr.span,
|
||||
"this `if` statement can be collapsed",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
|
||||
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
|
||||
|
@ -177,8 +182,8 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
|
|||
),
|
||||
app, // snippet
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,6 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for types that implement `Copy` as well as
|
||||
|
@ -38,16 +36,15 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyIterator {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
..
|
||||
}) = item.kind;
|
||||
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
|
||||
if is_copy(cx, ty);
|
||||
if let Some(trait_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id);
|
||||
then {
|
||||
}) = item.kind
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& is_copy(cx, ty)
|
||||
&& let Some(trait_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Iterator, trait_id)
|
||||
{
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
COPY_ITERATOR,
|
||||
|
@ -58,5 +55,4 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,12 +53,11 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
|
|||
|
||||
impl EarlyLintPass for CrateInMacroDef {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if_chain! {
|
||||
if item.attrs.iter().any(is_macro_export);
|
||||
if let ItemKind::MacroDef(macro_def) = &item.kind;
|
||||
let tts = macro_def.body.tokens.clone();
|
||||
if let Some(span) = contains_unhygienic_crate_reference(&tts);
|
||||
then {
|
||||
if item.attrs.iter().any(is_macro_export)
|
||||
&& let ItemKind::MacroDef(macro_def) = &item.kind
|
||||
&& let tts = macro_def.body.tokens.clone()
|
||||
&& let Some(span) = contains_unhygienic_crate_reference(&tts)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CRATE_IN_MACRO_DEF,
|
||||
|
@ -70,34 +69,29 @@ impl EarlyLintPass for CrateInMacroDef {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_macro_export(attr: &Attribute) -> bool {
|
||||
if_chain! {
|
||||
if let AttrKind::Normal(normal) = &attr.kind;
|
||||
if let [segment] = normal.item.path.segments.as_slice();
|
||||
then {
|
||||
if let AttrKind::Normal(normal) = &attr.kind
|
||||
&& let [segment] = normal.item.path.segments.as_slice()
|
||||
{
|
||||
segment.ident.name == sym::macro_export
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
|
||||
let mut prev_is_dollar = false;
|
||||
let mut cursor = tts.trees();
|
||||
while let Some(curr) = cursor.next() {
|
||||
if_chain! {
|
||||
if !prev_is_dollar;
|
||||
if let Some(span) = is_crate_keyword(curr);
|
||||
if let Some(next) = cursor.look_ahead(0);
|
||||
if is_token(next, &TokenKind::ModSep);
|
||||
then {
|
||||
if !prev_is_dollar
|
||||
&& let Some(span) = is_crate_keyword(curr)
|
||||
&& let Some(next) = cursor.look_ahead(0)
|
||||
&& is_token(next, &TokenKind::ModSep)
|
||||
{
|
||||
return Some(span);
|
||||
}
|
||||
}
|
||||
if let TokenTree::Delimited(_, _, tts) = &curr {
|
||||
let span = contains_unhygienic_crate_reference(tts);
|
||||
if span.is_some() {
|
||||
|
@ -110,10 +104,18 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
|
|||
}
|
||||
|
||||
fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
|
||||
if_chain! {
|
||||
if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }, _) = tt;
|
||||
if symbol.as_str() == "crate";
|
||||
then { Some(*span) } else { None }
|
||||
if let TokenTree::Token(
|
||||
Token {
|
||||
kind: TokenKind::Ident(symbol, _),
|
||||
span,
|
||||
},
|
||||
_,
|
||||
) = tt
|
||||
&& symbol.as_str() == "crate"
|
||||
{
|
||||
Some(*span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -33,12 +32,11 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
|
|||
|
||||
impl LateLintPass<'_> for CreateDir {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [arg, ..]) = expr.kind;
|
||||
if let ExprKind::Path(ref path) = func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id);
|
||||
then {
|
||||
if let ExprKind::Call(func, [arg, ..]) = expr.kind
|
||||
&& let ExprKind::Path(ref path) = func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CREATE_DIR,
|
||||
|
@ -47,8 +45,7 @@ impl LateLintPass<'_> for CreateDir {
|
|||
"consider calling `std::fs::create_dir_all` instead",
|
||||
format!("create_dir_all({})", snippet(cx, arg.span, "..")),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO,
|
||||
#[cfg(feature = "internal")]
|
||||
crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO,
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{has_drop, is_copy};
|
||||
use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -81,22 +80,21 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if !expr.span.from_expansion()
|
||||
// Avoid cases already linted by `field_reassign_with_default`
|
||||
if !self.reassigned_linted.contains(&expr.span);
|
||||
if let ExprKind::Call(path, ..) = expr.kind;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if let ExprKind::Path(ref qpath) = path.kind;
|
||||
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::default_fn, def_id);
|
||||
if !is_update_syntax_base(cx, expr);
|
||||
&& !self.reassigned_linted.contains(&expr.span)
|
||||
&& let ExprKind::Call(path, ..) = expr.kind
|
||||
&& !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
|
||||
&& let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
|
||||
&& !is_update_syntax_base(cx, expr)
|
||||
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
|
||||
if let QPath::Resolved(None, _path) = qpath;
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, ..) = expr_ty.kind();
|
||||
if !is_from_proc_macro(cx, expr);
|
||||
then {
|
||||
&& let QPath::Resolved(None, _path) = qpath
|
||||
&& let expr_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let ty::Adt(def, ..) = expr_ty.kind()
|
||||
&& !is_from_proc_macro(cx, expr)
|
||||
{
|
||||
let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did())));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -109,7 +107,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
|
@ -124,38 +121,36 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
|
||||
// `default` method of the `Default` trait, and store statement index in current block being
|
||||
// checked and the name of the bound variable
|
||||
let (local, variant, binding_name, binding_type, span) = if_chain! {
|
||||
let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind
|
||||
// only take `let ...` statements
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let Some(expr) = local.init;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if !expr.span.from_expansion();
|
||||
&& let Some(expr) = local.init
|
||||
&& !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
|
||||
&& !expr.span.from_expansion()
|
||||
// only take bindings to identifiers
|
||||
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
|
||||
&& let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind
|
||||
// only when assigning `... = Default::default()`
|
||||
if is_expr_default(expr, cx);
|
||||
let binding_type = cx.typeck_results().node_type(binding_id);
|
||||
if let Some(adt) = binding_type.ty_adt_def();
|
||||
if adt.is_struct();
|
||||
let variant = adt.non_enum_variant();
|
||||
if adt.did().is_local() || !variant.is_field_list_non_exhaustive();
|
||||
let module_did = cx.tcx.parent_module(stmt.hir_id);
|
||||
if variant
|
||||
&& is_expr_default(expr, cx)
|
||||
&& let binding_type = cx.typeck_results().node_type(binding_id)
|
||||
&& let Some(adt) = binding_type.ty_adt_def()
|
||||
&& adt.is_struct()
|
||||
&& let variant = adt.non_enum_variant()
|
||||
&& (adt.did().is_local() || !variant.is_field_list_non_exhaustive())
|
||||
&& let module_did = cx.tcx.parent_module(stmt.hir_id)
|
||||
&& variant
|
||||
.fields
|
||||
.iter()
|
||||
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
|
||||
let all_fields_are_copy = variant
|
||||
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx))
|
||||
&& let all_fields_are_copy = variant
|
||||
.fields
|
||||
.iter()
|
||||
.all(|field| {
|
||||
is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity())
|
||||
});
|
||||
if !has_drop(cx, binding_type) || all_fields_are_copy;
|
||||
then {
|
||||
})
|
||||
&& (!has_drop(cx, binding_type) || all_fields_are_copy)
|
||||
{
|
||||
(local, variant, ident.name, binding_type, expr.span)
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let init_ctxt = local.span.ctxt();
|
||||
|
@ -216,10 +211,9 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
.join(", ");
|
||||
|
||||
// give correct suggestion if generics are involved (see #6944)
|
||||
let binding_type = if_chain! {
|
||||
if let ty::Adt(adt_def, args) = binding_type.kind();
|
||||
if !args.is_empty();
|
||||
then {
|
||||
let binding_type = if let ty::Adt(adt_def, args) = binding_type.kind()
|
||||
&& !args.is_empty()
|
||||
{
|
||||
let adt_def_ty_name = cx.tcx.item_name(adt_def.did());
|
||||
let generic_args = args.iter().collect::<Vec<_>>();
|
||||
let tys_str = generic_args
|
||||
|
@ -230,7 +224,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
format!("{adt_def_ty_name}::<{}>", &tys_str)
|
||||
} else {
|
||||
binding_type.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
let sugg = if ext_with_default {
|
||||
|
@ -260,48 +253,42 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
|
||||
/// Checks if the given expression is the `default` method belonging to the `Default` trait.
|
||||
fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fn_expr, _) = &expr.kind;
|
||||
if let ExprKind::Path(qpath) = &fn_expr.kind;
|
||||
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
|
||||
then {
|
||||
if let ExprKind::Call(fn_expr, _) = &expr.kind
|
||||
&& let ExprKind::Path(qpath) = &fn_expr.kind
|
||||
&& let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
|
||||
{
|
||||
// right hand side of assignment is `Default::default`
|
||||
cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
|
||||
fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(later_expr) = this.kind
|
||||
// only take assignments
|
||||
if let StmtKind::Semi(later_expr) = this.kind;
|
||||
if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
|
||||
&& let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind
|
||||
// only take assignments to fields where the left-hand side field is a field of
|
||||
// the same binding as the previous statement
|
||||
if let ExprKind::Field(binding, field_ident) = assign_lhs.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
|
||||
if let Some(second_binding_name) = path.segments.last();
|
||||
if second_binding_name.ident.name == binding_name;
|
||||
then {
|
||||
&& let ExprKind::Field(binding, field_ident) = assign_lhs.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind
|
||||
&& let Some(second_binding_name) = path.segments.last()
|
||||
&& second_binding_name.ident.name == binding_name
|
||||
{
|
||||
Some((field_ident, assign_rhs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
|
||||
fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
if let ExprKind::Struct(_, _, Some(base)) = parent.kind;
|
||||
then {
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Struct(_, _, Some(base)) = parent.kind
|
||||
{
|
||||
base.hir_id == expr.hir_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,22 +56,21 @@ fn is_alias(ty: hir::Ty<'_>) -> bool {
|
|||
|
||||
impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
||||
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if_chain!(
|
||||
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind
|
||||
// make sure we have a call to `Default::default`
|
||||
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind;
|
||||
&& let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind
|
||||
// make sure this isn't a type alias:
|
||||
// `<Foo as Bar>::Assoc` cannot be used as a constructor
|
||||
if !is_alias(*base);
|
||||
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::default_fn, def_id);
|
||||
&& !is_alias(*base)
|
||||
&& let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
|
||||
// make sure we have a struct with no fields (unit struct)
|
||||
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if def.is_struct();
|
||||
if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant();
|
||||
if !var.is_field_list_non_exhaustive();
|
||||
if !expr.span.from_expansion() && !qpath.span().from_expansion();
|
||||
then {
|
||||
&& let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& def.is_struct()
|
||||
&& let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant()
|
||||
&& !var.is_field_list_non_exhaustive()
|
||||
&& !expr.span.from_expansion() && !qpath.span().from_expansion()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
|
@ -80,8 +79,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
|||
"remove this call to `default`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{get_parent_node, numeric_literal};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
|
@ -82,12 +81,13 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
|
||||
/// Check whether a passed literal has potential to cause fallback or not.
|
||||
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
|
||||
if_chain! {
|
||||
if !in_external_macro(self.cx.sess(), lit.span);
|
||||
if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false)));
|
||||
if matches!(lit.node,
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));
|
||||
then {
|
||||
if !in_external_macro(self.cx.sess(), lit.span)
|
||||
&& matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false)))
|
||||
&& matches!(
|
||||
lit.node,
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)
|
||||
)
|
||||
{
|
||||
let (suffix, is_float) = match lit_ty.kind() {
|
||||
ty::Int(IntTy::I32) => ("i32", false),
|
||||
ty::Float(FloatTy::F64) => ("f64", true),
|
||||
|
@ -113,11 +113,10 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
|
|||
"default numeric fallback might occur",
|
||||
|diag| {
|
||||
diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
||||
|
@ -149,22 +148,20 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
|
||||
ExprKind::Struct(_, fields, base) => {
|
||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||
if_chain! {
|
||||
if let Some(adt_def) = ty.ty_adt_def();
|
||||
if adt_def.is_struct();
|
||||
if let Some(variant) = adt_def.variants().iter().next();
|
||||
then {
|
||||
if let Some(adt_def) = ty.ty_adt_def()
|
||||
&& adt_def.is_struct()
|
||||
&& let Some(variant) = adt_def.variants().iter().next()
|
||||
{
|
||||
let fields_def = &variant.fields;
|
||||
|
||||
// Push field type then visit each field expr.
|
||||
for field in *fields {
|
||||
let bound =
|
||||
fields_def
|
||||
.iter()
|
||||
.find_map(|f_def| {
|
||||
if f_def.ident(self.cx.tcx) == field.ident
|
||||
{ Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) }
|
||||
else { None }
|
||||
let bound = fields_def.iter().find_map(|f_def| {
|
||||
if f_def.ident(self.cx.tcx) == field.ident {
|
||||
Some(self.cx.tcx.type_of(f_def.did).instantiate_identity())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
self.ty_bounds.push(bound.into());
|
||||
self.visit_expr(field.expr);
|
||||
|
@ -179,7 +176,6 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::Lit(lit) => {
|
||||
|
|
|
@ -597,12 +597,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if !pat.span.from_expansion();
|
||||
if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
|
||||
if !pat.span.from_expansion()
|
||||
&& let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind()
|
||||
// only lint immutable refs, because borrowed `&mut T` cannot be moved out
|
||||
if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
|
||||
then {
|
||||
&& let ty::Ref(_, _, Mutability::Not) = *tam.kind()
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
|
||||
self.current_body = self.current_body.or(cx.enclosing_body);
|
||||
|
@ -619,7 +618,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
if Some(body.id()) == self.current_body {
|
||||
|
|
|
@ -148,78 +148,61 @@ fn check_struct<'tcx>(
|
|||
}
|
||||
|
||||
fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind;
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res;
|
||||
if let variant_id = cx.tcx.parent(id);
|
||||
if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id);
|
||||
if variant_def.fields.is_empty();
|
||||
if !variant_def.is_field_list_non_exhaustive();
|
||||
|
||||
then {
|
||||
if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
|
||||
&& let variant_id = cx.tcx.parent(id)
|
||||
&& let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id)
|
||||
&& variant_def.fields.is_empty()
|
||||
&& !variant_def.is_field_list_non_exhaustive()
|
||||
{
|
||||
let enum_span = cx.tcx.def_span(adt_def.did());
|
||||
let indent_enum = indent_of(cx, enum_span).unwrap_or(0);
|
||||
let variant_span = cx.tcx.def_span(variant_def.def_id);
|
||||
let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DERIVABLE_IMPLS,
|
||||
item.span,
|
||||
"this `impl` can be derived",
|
||||
|diag| {
|
||||
span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
|
||||
diag.span_suggestion_hidden(
|
||||
item.span,
|
||||
"remove the manual implementation...",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
enum_span.shrink_to_lo(),
|
||||
"...and instead derive it...",
|
||||
format!(
|
||||
"#[derive(Default)]\n{indent}",
|
||||
indent = " ".repeat(indent_enum),
|
||||
),
|
||||
Applicability::MachineApplicable
|
||||
format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
variant_span.shrink_to_lo(),
|
||||
"...and mark the default variant",
|
||||
format!(
|
||||
"#[default]\n{indent}",
|
||||
indent = " ".repeat(indent_variant),
|
||||
),
|
||||
Applicability::MachineApplicable
|
||||
format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
items: [child],
|
||||
self_ty,
|
||||
..
|
||||
}) = item.kind;
|
||||
if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived);
|
||||
if !item.span.from_expansion();
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
|
||||
if let impl_item_hir = child.id.hir_id();
|
||||
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
|
||||
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
|
||||
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
|
||||
if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind();
|
||||
if let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
if !attrs.iter().any(|attr| attr.doc_str().is_some());
|
||||
if cx.tcx.hir().attrs(impl_item_hir).is_empty();
|
||||
|
||||
then {
|
||||
}) = item.kind
|
||||
&& !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
|
||||
&& !item.span.from_expansion()
|
||||
&& let Some(def_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Default, def_id)
|
||||
&& let impl_item_hir = child.id.hir_id()
|
||||
&& let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir)
|
||||
&& let ImplItemKind::Fn(_, b) = &impl_item.kind
|
||||
&& let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
|
||||
&& let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
|
||||
&& let attrs = cx.tcx.hir().attrs(item.hir_id())
|
||||
&& !attrs.iter().any(|attr| attr.doc_str().is_some())
|
||||
&& cx.tcx.hir().attrs(impl_item_hir).is_empty()
|
||||
{
|
||||
if adt_def.is_struct() {
|
||||
check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
|
||||
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
|
||||
|
@ -227,7 +210,6 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
||||
use clippy_utils::{is_lint_allowed, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||
|
@ -233,11 +232,10 @@ fn check_hash_peq<'tcx>(
|
|||
ty: Ty<'tcx>,
|
||||
hash_is_automatically_derived: bool,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
|
||||
then {
|
||||
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
|
||||
&& let Some(def_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Hash, def_id)
|
||||
{
|
||||
// Look for the PartialEq implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
|
||||
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||
|
@ -259,17 +257,13 @@ fn check_hash_peq<'tcx>(
|
|||
|diag| {
|
||||
if let Some(local_def_id) = impl_id.as_local() {
|
||||
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
|
||||
diag.span_note(
|
||||
cx.tcx.hir().span(hir_id),
|
||||
"`PartialEq` implemented here"
|
||||
);
|
||||
}
|
||||
diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
|
||||
|
@ -280,12 +274,11 @@ fn check_ord_partial_ord<'tcx>(
|
|||
ty: Ty<'tcx>,
|
||||
ord_is_automatically_derived: bool,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
|
||||
if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
|
||||
if let Some(def_id) = &trait_ref.trait_def_id();
|
||||
if *def_id == ord_trait_def_id;
|
||||
then {
|
||||
if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
|
||||
&& let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait()
|
||||
&& let Some(def_id) = &trait_ref.trait_def_id()
|
||||
&& *def_id == ord_trait_def_id
|
||||
{
|
||||
// Look for the PartialOrd implementations for `ty`
|
||||
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
|
||||
let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||
|
@ -305,24 +298,14 @@ fn check_ord_partial_ord<'tcx>(
|
|||
"you are deriving `Ord` but have implemented `PartialOrd` explicitly"
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
span,
|
||||
mess,
|
||||
|diag| {
|
||||
span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| {
|
||||
if let Some(local_def_id) = impl_id.as_local() {
|
||||
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
|
||||
diag.span_note(
|
||||
cx.tcx.hir().span(hir_id),
|
||||
"`PartialOrd` implemented here"
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,28 +378,28 @@ fn check_unsafe_derive_deserialize<'tcx>(
|
|||
visitor.has_unsafe
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(trait_def_id) = trait_ref.trait_def_id();
|
||||
if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
if let Some(local_def_id) = def.did().as_local();
|
||||
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
|
||||
if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
|
||||
if cx.tcx.inherent_impls(def.did())
|
||||
if let Some(trait_def_id) = trait_ref.trait_def_id()
|
||||
&& match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE)
|
||||
&& let ty::Adt(def, _) = ty.kind()
|
||||
&& let Some(local_def_id) = def.did().as_local()
|
||||
&& let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id)
|
||||
&& !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id)
|
||||
&& cx
|
||||
.tcx
|
||||
.inherent_impls(def.did())
|
||||
.iter()
|
||||
.map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
|
||||
.any(|imp| has_unsafe(cx, imp));
|
||||
then {
|
||||
.any(|imp| has_unsafe(cx, imp))
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNSAFE_DERIVE_DESERIALIZE,
|
||||
item.span,
|
||||
"you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
|
||||
None,
|
||||
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
|
||||
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct UnsafeVisitor<'a, 'tcx> {
|
||||
|
@ -432,13 +415,11 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(header) = kind.header();
|
||||
if header.unsafety == Unsafety::Unsafe;
|
||||
then {
|
||||
if let Some(header) = kind.header()
|
||||
&& header.unsafety == Unsafety::Unsafe
|
||||
{
|
||||
self.has_unsafe = true;
|
||||
}
|
||||
}
|
||||
|
||||
walk_fn(self, kind, decl, body_id, id);
|
||||
}
|
||||
|
@ -464,20 +445,19 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
|
||||
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
|
||||
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
|
||||
if_chain! {
|
||||
if let ty::Adt(adt, args) = ty.kind();
|
||||
if cx.tcx.visibility(adt.did()).is_public();
|
||||
if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
|
||||
if let Some(def_id) = trait_ref.trait_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
|
||||
let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
|
||||
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
|
||||
if let ty::Adt(adt, args) = ty.kind()
|
||||
&& cx.tcx.visibility(adt.did()).is_public()
|
||||
&& let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
|
||||
&& let Some(def_id) = trait_ref.trait_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
|
||||
&& let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
|
||||
&& !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])
|
||||
// If all of our fields implement `Eq`, we can implement `Eq` too
|
||||
if adt
|
||||
&& adt
|
||||
.all_fields()
|
||||
.map(|f| f.ty(cx.tcx, args))
|
||||
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
|
||||
then {
|
||||
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]))
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
|
@ -486,8 +466,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
|
|||
"consider deriving `Eq` as well",
|
||||
"PartialEq, Eq".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use pulldown_cmark::Event::{
|
||||
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
|
||||
};
|
||||
|
@ -428,17 +427,15 @@ fn lint_for_missing_headers(
|
|||
span,
|
||||
"docs for function returning `Result` missing `# Errors` section",
|
||||
);
|
||||
} else {
|
||||
if_chain! {
|
||||
if let Some(body_id) = body_id;
|
||||
if let Some(future) = cx.tcx.lang_items().future_trait();
|
||||
let typeck = cx.tcx.typeck_body(body_id);
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let ret_ty = typeck.expr_ty(body.value);
|
||||
if implements_trait(cx, ret_ty, future, &[]);
|
||||
if let ty::Coroutine(_, subs, _) = ret_ty.kind();
|
||||
if is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result);
|
||||
then {
|
||||
} else if let Some(body_id) = body_id
|
||||
&& let Some(future) = cx.tcx.lang_items().future_trait()
|
||||
&& let typeck = cx.tcx.typeck_body(body_id)
|
||||
&& let body = cx.tcx.hir().body(body_id)
|
||||
&& let ret_ty = typeck.expr_ty(body.value)
|
||||
&& implements_trait(cx, ret_ty, future, &[])
|
||||
&& let ty::Coroutine(_, subs, _) = ret_ty.kind()
|
||||
&& is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_ERRORS_DOC,
|
||||
|
@ -447,8 +444,6 @@ fn lint_for_missing_headers(
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::peel_blocks;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -36,21 +35,21 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
|
|||
|
||||
impl LateLintPass<'_> for EmptyDrop {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
items: [child],
|
||||
..
|
||||
}) = item.kind;
|
||||
if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
|
||||
if let impl_item_hir = child.id.hir_id();
|
||||
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
|
||||
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
|
||||
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
|
||||
let func_expr = peel_blocks(func_expr);
|
||||
if let ExprKind::Block(block, _) = func_expr.kind;
|
||||
if block.stmts.is_empty() && block.expr.is_none();
|
||||
then {
|
||||
}) = item.kind
|
||||
&& trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
|
||||
&& let impl_item_hir = child.id.hir_id()
|
||||
&& let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir)
|
||||
&& let ImplItemKind::Fn(_, b) = &impl_item.kind
|
||||
&& let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
|
||||
&& let func_expr = peel_blocks(func_expr)
|
||||
&& let ExprKind::Block(block, _) = func_expr.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& block.expr.is_none()
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EMPTY_DROP,
|
||||
|
@ -58,9 +57,8 @@ impl LateLintPass<'_> for EmptyDrop {
|
|||
"empty drop implementation",
|
||||
"try removing this impl",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,29 +114,25 @@ impl LateLintPass<'_> for EndianBytes {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind;
|
||||
if args.is_empty();
|
||||
let ty = cx.typeck_results().expr_ty(receiver);
|
||||
if ty.is_primitive_ty();
|
||||
if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty);
|
||||
then {
|
||||
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind
|
||||
&& args.is_empty()
|
||||
&& let ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& ty.is_primitive_ty()
|
||||
&& maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(function, ..) = expr.kind;
|
||||
if let ExprKind::Path(qpath) = function.kind;
|
||||
if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id();
|
||||
if let Some(function_name) = cx.get_def_path(def_id).last();
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if ty.is_primitive_ty();
|
||||
then {
|
||||
if let ExprKind::Call(function, ..) = expr.kind
|
||||
&& let ExprKind::Path(qpath) = function.kind
|
||||
&& let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id()
|
||||
&& let Some(function_name) = cx.get_def_path(def_id).last()
|
||||
&& let ty = cx.typeck_results().expr_ty(expr)
|
||||
&& ty.is_primitive_ty()
|
||||
{
|
||||
maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::indent_of;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -71,16 +70,13 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
|
|||
|
||||
impl LateLintPass<'_> for ExhaustiveItems {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind;
|
||||
if cx.effective_visibilities.is_exported(item.owner_id.def_id);
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
|
||||
then {
|
||||
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind
|
||||
&& cx.effective_visibilities.is_exported(item.owner_id.def_id)
|
||||
&& let attrs = cx.tcx.hir().attrs(item.hir_id())
|
||||
&& !attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
|
||||
{
|
||||
let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
|
||||
if v.fields().iter().any(|f| {
|
||||
!cx.tcx.visibility(f.def_id).is_public()
|
||||
}) {
|
||||
if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) {
|
||||
// skip structs with private fields
|
||||
return;
|
||||
}
|
||||
|
@ -90,21 +86,15 @@ impl LateLintPass<'_> for ExhaustiveItems {
|
|||
};
|
||||
let suggestion_span = item.span.shrink_to_lo();
|
||||
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
lint,
|
||||
item.span,
|
||||
msg,
|
||||
|diag| {
|
||||
span_lint_and_then(cx, lint, item.span, msg, |diag| {
|
||||
let sugg = format!("#[non_exhaustive]\n{indent}");
|
||||
diag.span_suggestion(suggestion_span,
|
||||
diag.span_suggestion(
|
||||
suggestion_span,
|
||||
"try adding #[non_exhaustive]",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect);
|
||||
}
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_entrypoint_fn;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -42,19 +41,17 @@ declare_lint_pass!(Exit => [EXIT]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Exit {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(path_expr, _args) = e.kind;
|
||||
if let ExprKind::Path(ref path) = path_expr.kind;
|
||||
if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::process_exit, def_id);
|
||||
let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id;
|
||||
if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent);
|
||||
if let ExprKind::Call(path_expr, _args) = e.kind
|
||||
&& let ExprKind::Path(ref path) = path_expr.kind
|
||||
&& let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
|
||||
&& let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id
|
||||
&& let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent)
|
||||
// If the next item up is a function we check if it is an entry point
|
||||
// and only then emit a linter warning
|
||||
if !is_entrypoint_fn(cx, parent.to_def_id());
|
||||
then {
|
||||
&& !is_entrypoint_fn(cx, parent.to_def_id())
|
||||
{
|
||||
span_lint(cx, EXIT, e.span, "usage of `process::exit`");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::macros::{find_format_args, format_args_inputs_span};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_expn_of, path_def_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
|
||||
|
@ -101,30 +100,28 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
/// If `kind` is a block that looks like `{ let result = $expr; result }` then
|
||||
/// returns $expr. Otherwise returns `kind`.
|
||||
fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
|
||||
if_chain! {
|
||||
if let ExprKind::Block(block, _label @ None) = kind;
|
||||
if let Block {
|
||||
if let ExprKind::Block(block, _label @ None) = kind
|
||||
&& let Block {
|
||||
stmts: [Stmt { kind: StmtKind::Local(local), .. }],
|
||||
expr: Some(expr_end_of_block),
|
||||
rules: BlockCheckMode::DefaultBlock,
|
||||
..
|
||||
} = block;
|
||||
} = block
|
||||
|
||||
// Find id of the local that expr_end_of_block resolves to
|
||||
if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind;
|
||||
if let Res::Local(expr_res) = expr_path.res;
|
||||
if let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res);
|
||||
&& let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind
|
||||
&& let Res::Local(expr_res) = expr_path.res
|
||||
&& let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res)
|
||||
|
||||
// Find id of the local we found in the block
|
||||
if let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind;
|
||||
&& let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind
|
||||
|
||||
// If those two are the same hir id
|
||||
if res_pat.hir_id == local_hir_id;
|
||||
&& res_pat.hir_id == local_hir_id
|
||||
|
||||
if let Some(init) = local.init;
|
||||
then {
|
||||
&& let Some(init) = local.init
|
||||
{
|
||||
return &init.kind;
|
||||
}
|
||||
}
|
||||
kind
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::method_chain_args;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
|
@ -53,15 +52,15 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
|
|||
impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
// check for `impl From<???> for ..`
|
||||
if_chain! {
|
||||
if let hir::ItemKind::Impl(impl_) = &item.kind;
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id);
|
||||
then {
|
||||
if let hir::ItemKind::Impl(impl_) = &item.kind
|
||||
&& let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
|
||||
&& cx
|
||||
.tcx
|
||||
.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id)
|
||||
{
|
||||
lint_impl_body(cx, item.span, impl_.items);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
|
||||
|
@ -98,11 +97,9 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
|
|||
}
|
||||
|
||||
for impl_item in impl_items {
|
||||
if_chain! {
|
||||
if impl_item.ident.name == sym::from;
|
||||
if let ImplItemKind::Fn(_, body_id) =
|
||||
cx.tcx.hir().impl_item(impl_item.id).kind;
|
||||
then {
|
||||
if impl_item.ident.name == sym::from
|
||||
&& let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind
|
||||
{
|
||||
// check the body for `begin_panic` or `unwrap`
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let mut fpu = FindPanicUnwrap {
|
||||
|
@ -122,10 +119,11 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
|
|||
move |diag| {
|
||||
diag.help(
|
||||
"`From` is intended for infallible conversions only. \
|
||||
Use `TryFrom` if there's a possibility for the conversion to fail");
|
||||
Use `TryFrom` if there's a possibility for the conversion to fail",
|
||||
);
|
||||
diag.span_note(fpu.result, "potential failure(s)");
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::numeric_literal;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -64,11 +63,10 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
|
|||
impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if_chain! {
|
||||
if let ty::Float(fty) = *ty.kind();
|
||||
if let hir::ExprKind::Lit(lit) = expr.kind;
|
||||
if let LitKind::Float(sym, lit_float_ty) = lit.node;
|
||||
then {
|
||||
if let ty::Float(fty) = *ty.kind()
|
||||
&& let hir::ExprKind::Lit(lit) = expr.kind
|
||||
&& let LitKind::Float(sym, lit_float_ty) = lit.node
|
||||
{
|
||||
let sym_str = sym.as_str();
|
||||
let formatter = FloatFormat::new(sym_str);
|
||||
// Try to bail out if the float is for sure fine.
|
||||
|
@ -80,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
let type_suffix = match lit_float_ty {
|
||||
LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"),
|
||||
LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
|
||||
LitFloatType::Unsuffixed => None
|
||||
LitFloatType::Unsuffixed => None,
|
||||
};
|
||||
let (is_whole, is_inf, mut float_str) = match fty {
|
||||
FloatTy::F32 => {
|
||||
|
@ -91,7 +89,6 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
FloatTy::F64 => {
|
||||
let value = sym_str.parse::<f64>().unwrap();
|
||||
|
||||
|
||||
(value.fract() == 0.0, value.is_infinite(), formatter.format(value))
|
||||
},
|
||||
};
|
||||
|
@ -133,7 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::{
|
||||
eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -133,31 +132,26 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
|
|||
expr = inner_expr;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind()
|
||||
// if the expression is a float literal and it is unsuffixed then
|
||||
// add a suffix so the suggestion is valid and unambiguous
|
||||
if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if let ExprKind::Lit(lit) = &expr.kind;
|
||||
if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
|
||||
then {
|
||||
&& let ExprKind::Lit(lit) = &expr.kind
|
||||
&& let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node
|
||||
{
|
||||
let op = format!(
|
||||
"{suggestion}{}{}",
|
||||
// Check for float literals without numbers following the decimal
|
||||
// separator such as `2.` and adds a trailing zero
|
||||
if sym.as_str().ends_with('.') {
|
||||
"0"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if sym.as_str().ends_with('.') { "0" } else { "" },
|
||||
float_ty.name_str()
|
||||
).into();
|
||||
)
|
||||
.into();
|
||||
|
||||
suggestion = match suggestion {
|
||||
Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
|
||||
_ => Sugg::NonParen(op)
|
||||
_ => Sugg::NonParen(op),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
suggestion.maybe_par()
|
||||
}
|
||||
|
@ -359,35 +353,59 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
|
|||
) = receiver.kind
|
||||
{
|
||||
// check if expression of the form x * x + y * y
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
|
||||
if eq_expr_value(cx, lmul_lhs, lmul_rhs);
|
||||
if eq_expr_value(cx, rmul_lhs, rmul_rhs);
|
||||
then {
|
||||
return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, "..")));
|
||||
}
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lmul_lhs,
|
||||
lmul_rhs,
|
||||
) = add_lhs.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
rmul_lhs,
|
||||
rmul_rhs,
|
||||
) = add_rhs.kind
|
||||
&& eq_expr_value(cx, lmul_lhs, lmul_rhs)
|
||||
&& eq_expr_value(cx, rmul_lhs, rmul_rhs)
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, lmul_lhs, "..").maybe_par(),
|
||||
Sugg::hir(cx, rmul_lhs, "..")
|
||||
));
|
||||
}
|
||||
|
||||
// check if expression of the form x.powi(2) + y.powi(2)
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(
|
||||
PathSegment { ident: lmethod_name, .. },
|
||||
largs_0, [largs_1, ..],
|
||||
_
|
||||
) = &add_lhs.kind;
|
||||
if let ExprKind::MethodCall(
|
||||
PathSegment { ident: rmethod_name, .. },
|
||||
rargs_0, [rargs_1, ..],
|
||||
_
|
||||
) = &add_rhs.kind;
|
||||
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
|
||||
if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1);
|
||||
if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1);
|
||||
if Int(2) == lvalue && Int(2) == rvalue;
|
||||
then {
|
||||
return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
|
||||
}
|
||||
PathSegment {
|
||||
ident: lmethod_name, ..
|
||||
},
|
||||
largs_0,
|
||||
[largs_1, ..],
|
||||
_,
|
||||
) = &add_lhs.kind
|
||||
&& let ExprKind::MethodCall(
|
||||
PathSegment {
|
||||
ident: rmethod_name, ..
|
||||
},
|
||||
rargs_0,
|
||||
[rargs_1, ..],
|
||||
_,
|
||||
) = &add_rhs.kind
|
||||
&& lmethod_name.as_str() == "powi"
|
||||
&& rmethod_name.as_str() == "powi"
|
||||
&& let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1)
|
||||
&& let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1)
|
||||
&& Int(2) == lvalue
|
||||
&& Int(2) == rvalue
|
||||
{
|
||||
return Some(format!(
|
||||
"{}.hypot({})",
|
||||
Sugg::hir(cx, largs_0, "..").maybe_par(),
|
||||
Sugg::hir(cx, rargs_0, "..")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -411,40 +429,45 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
|
|||
// TODO: Lint expressions of the form `x.exp() - y` where y > 1
|
||||
// and suggest usage of `x.exp_m1() - (y - 1)` instead
|
||||
fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
|
||||
if cx.typeck_results().expr_ty(lhs).is_floating_point();
|
||||
if let Some(value) = constant(cx, cx.typeck_results(), rhs);
|
||||
if F32(1.0) == value || F64(1.0) == value;
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind;
|
||||
if cx.typeck_results().expr_ty(self_arg).is_floating_point();
|
||||
if path.ident.name.as_str() == "exp";
|
||||
then {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = expr.kind
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& let Some(value) = constant(cx, cx.typeck_results(), rhs)
|
||||
&& (F32(1.0) == value || F64(1.0) == value)
|
||||
&& let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
|
||||
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
|
||||
&& path.ident.name.as_str() == "exp"
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPRECISE_FLOPS,
|
||||
expr.span,
|
||||
"(e.pow(x) - 1) can be computed more accurately",
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.exp_m1()",
|
||||
Sugg::hir(cx, self_arg, "..").maybe_par()
|
||||
),
|
||||
format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
|
||||
if cx.typeck_results().expr_ty(lhs).is_floating_point();
|
||||
if cx.typeck_results().expr_ty(rhs).is_floating_point();
|
||||
then {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind
|
||||
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
|
||||
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
|
||||
{
|
||||
return Some((lhs, rhs));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -553,12 +576,15 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
|
|||
}
|
||||
|
||||
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
|
||||
let if_body_expr = peel_blocks(then);
|
||||
let else_body_expr = peel_blocks(r#else);
|
||||
if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
|
||||
then {
|
||||
if let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: Some(r#else),
|
||||
}) = higher::If::hir(expr)
|
||||
&& let if_body_expr = peel_blocks(then)
|
||||
&& let else_body_expr = peel_blocks(r#else)
|
||||
&& let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr)
|
||||
{
|
||||
let positive_abs_sugg = (
|
||||
"manual implementation of `abs` method",
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
|
||||
|
@ -592,21 +618,30 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind;
|
||||
if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind;
|
||||
then {
|
||||
return method_name_a.as_str() == method_name_b.as_str() &&
|
||||
args_a.len() == args_b.len() &&
|
||||
(
|
||||
["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
|
||||
method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])
|
||||
);
|
||||
}
|
||||
if let ExprKind::MethodCall(
|
||||
PathSegment {
|
||||
ident: method_name_a, ..
|
||||
},
|
||||
_,
|
||||
args_a,
|
||||
_,
|
||||
) = expr_a.kind
|
||||
&& let ExprKind::MethodCall(
|
||||
PathSegment {
|
||||
ident: method_name_b, ..
|
||||
},
|
||||
_,
|
||||
args_b,
|
||||
_,
|
||||
) = expr_b.kind
|
||||
{
|
||||
return method_name_a.as_str() == method_name_b.as_str()
|
||||
&& args_a.len() == args_b.len()
|
||||
&& (["ln", "log2", "log10"].contains(&method_name_a.as_str())
|
||||
|| method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]));
|
||||
}
|
||||
|
||||
false
|
||||
|
@ -614,67 +649,66 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>
|
|||
|
||||
fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
// check if expression of the form x.logN() / y.logN()
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
lhs,
|
||||
rhs,
|
||||
) = &expr.kind;
|
||||
if are_same_base_logs(cx, lhs, rhs);
|
||||
if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind;
|
||||
if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind;
|
||||
then {
|
||||
) = &expr.kind
|
||||
&& are_same_base_logs(cx, lhs, rhs)
|
||||
&& let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind
|
||||
&& let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
expr.span,
|
||||
"log base can be expressed more clearly",
|
||||
"consider using",
|
||||
format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
|
||||
format!(
|
||||
"{}.log({})",
|
||||
Sugg::hir(cx, largs_self, "..").maybe_par(),
|
||||
Sugg::hir(cx, rargs_self, ".."),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Div, ..
|
||||
},
|
||||
div_lhs,
|
||||
div_rhs,
|
||||
) = &expr.kind;
|
||||
if let ExprKind::Binary(
|
||||
) = &expr.kind
|
||||
&& let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Mul, ..
|
||||
},
|
||||
mul_lhs,
|
||||
mul_rhs,
|
||||
) = &div_lhs.kind;
|
||||
if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs);
|
||||
if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs);
|
||||
then {
|
||||
) = &div_lhs.kind
|
||||
&& let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs)
|
||||
&& let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs)
|
||||
{
|
||||
// TODO: also check for constant values near PI/180 or 180/PI
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
|
||||
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
|
||||
&& (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
if float_type == ast::LitFloatType::Unsuffixed;
|
||||
then {
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
} else {
|
||||
proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
}
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
|
@ -684,23 +718,20 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
proposal,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if
|
||||
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
|
||||
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
} else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue)
|
||||
&& (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
if float_type == ast::LitFloatType::Unsuffixed;
|
||||
then {
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind
|
||||
&& let ast::LitKind::Float(ref value, float_type) = literal.node
|
||||
&& float_type == ast::LitFloatType::Unsuffixed
|
||||
{
|
||||
if value.as_str().ends_with('.') {
|
||||
proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
} else {
|
||||
proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
}
|
||||
}
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUBOPTIMAL_FLOPS,
|
||||
|
@ -712,7 +743,6 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
|
|
|
@ -8,7 +8,6 @@ use clippy_utils::macros::{
|
|||
};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::{
|
||||
FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
|
||||
|
@ -404,28 +403,25 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb
|
|||
}
|
||||
|
||||
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if !value.span.from_expansion();
|
||||
if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
|
||||
if is_diag_trait_item(cx, method_def_id, sym::ToString);
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
|
||||
let (n_needed_derefs, target) =
|
||||
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
|
||||
if implements_trait(cx, target, display_trait_id, &[]);
|
||||
if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
if !value.span.from_expansion()
|
||||
&& let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
|
||||
&& is_diag_trait_item(cx, method_def_id, sym::ToString)
|
||||
&& let receiver_ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display)
|
||||
&& let (n_needed_derefs, target) =
|
||||
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
|
||||
&& implements_trait(cx, target, display_trait_id, &[])
|
||||
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
|
||||
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
|
||||
{
|
||||
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
|
||||
if n_needed_derefs == 0 && !needs_ref {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
to_string_span.with_lo(receiver.span.hi()),
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{name}!` args"
|
||||
),
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
|
@ -435,9 +431,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{name}!` args"
|
||||
),
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
|
||||
"use this",
|
||||
format!(
|
||||
"{}{:*>n_needed_derefs$}{receiver_snippet}",
|
||||
|
@ -448,7 +442,6 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_arg_positions(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{FormatArgsPiece, FormatTrait};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
|
||||
|
@ -141,20 +140,19 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
|||
}
|
||||
|
||||
fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind
|
||||
// Get the hir_id of the object we are calling the method on
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind;
|
||||
// Is the method to_string() ?
|
||||
if path.ident.name == sym::to_string;
|
||||
&& path.ident.name == sym::to_string
|
||||
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
|
||||
// separately)
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if is_diag_trait_item(cx, expr_def_id, sym::ToString);
|
||||
&& let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& is_diag_trait_item(cx, expr_def_id, sym::ToString)
|
||||
// Is the method is called on self
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind;
|
||||
if let [segment] = path.segments;
|
||||
if segment.ident.name == kw::SelfLower;
|
||||
then {
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
|
||||
&& let [segment] = path.segments
|
||||
&& segment.ident.name == kw::SelfLower
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
RECURSIVE_FORMAT_IMPL,
|
||||
|
@ -162,7 +160,6 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
|
||||
|
@ -215,10 +212,9 @@ fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_
|
|||
}
|
||||
|
||||
fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
|
||||
if_chain! {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
|
||||
if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
|
||||
then {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||
{
|
||||
let replacement = match name {
|
||||
sym::print_macro | sym::eprint_macro => "write",
|
||||
sym::println_macro | sym::eprintln_macro => "writeln",
|
||||
|
@ -241,29 +237,28 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
|
|||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> {
|
||||
if_chain! {
|
||||
if impl_item.ident.name == sym::fmt;
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
||||
if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id());
|
||||
if let Some(did) = trait_ref.trait_def_id();
|
||||
if let Some(name) = cx.tcx.get_diagnostic_name(did);
|
||||
if matches!(name, sym::Debug | sym::Display);
|
||||
then {
|
||||
if impl_item.ident.name == sym::fmt
|
||||
&& let ImplItemKind::Fn(_, body_id) = impl_item.kind
|
||||
&& let Some(Impl {
|
||||
of_trait: Some(trait_ref),
|
||||
..
|
||||
}) = get_parent_as_impl(cx.tcx, impl_item.hir_id())
|
||||
&& let Some(did) = trait_ref.trait_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(did)
|
||||
&& matches!(name, sym::Debug | sym::Display)
|
||||
{
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let formatter_name = body.params.get(1)
|
||||
let formatter_name = body
|
||||
.params
|
||||
.get(1)
|
||||
.and_then(|param| param.pat.simple_ident())
|
||||
.map(|ident| ident.name);
|
||||
|
||||
Some(FormatTraitNames {
|
||||
name,
|
||||
formatter_name,
|
||||
})
|
||||
Some(FormatTraitNames { name, formatter_name })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
|
||||
use clippy_utils::is_span_if;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -168,21 +167,20 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
|
||||
/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
|
||||
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
|
||||
if !lhs.span.from_expansion() && !rhs.span.from_expansion();
|
||||
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind
|
||||
&& !lhs.span.from_expansion() && !rhs.span.from_expansion()
|
||||
// span between BinOp LHS and RHS
|
||||
let binop_span = lhs.span.between(rhs.span);
|
||||
&& let binop_span = lhs.span.between(rhs.span)
|
||||
// if RHS is an UnOp
|
||||
if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
|
||||
&& let ExprKind::Unary(op, ref un_rhs) = rhs.kind
|
||||
// from UnOp operator to UnOp operand
|
||||
let unop_operand_span = rhs.span.until(un_rhs.span);
|
||||
if let Some(binop_snippet) = snippet_opt(cx, binop_span);
|
||||
if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
|
||||
let binop_str = BinOpKind::to_string(&binop.node);
|
||||
&& let unop_operand_span = rhs.span.until(un_rhs.span)
|
||||
&& let Some(binop_snippet) = snippet_opt(cx, binop_span)
|
||||
&& let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span)
|
||||
&& let binop_str = BinOpKind::to_string(&binop.node)
|
||||
// no space after BinOp operator and space after UnOp operator
|
||||
if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
|
||||
then {
|
||||
&& binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ')
|
||||
{
|
||||
let unop_str = UnOp::to_string(op);
|
||||
let eqop_span = lhs.span.between(un_rhs.span);
|
||||
span_lint_and_help(
|
||||
|
@ -194,52 +192,45 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
`{binop_str}{unop_str}` is a single operator"
|
||||
),
|
||||
None,
|
||||
&format!(
|
||||
"put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"
|
||||
),
|
||||
&format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
|
||||
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if_chain! {
|
||||
if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
|
||||
if is_block(else_) || is_if(else_);
|
||||
if !then.span.from_expansion() && !else_.span.from_expansion();
|
||||
if !in_external_macro(cx.sess(), expr.span);
|
||||
if let ExprKind::If(_, then, Some(else_)) = &expr.kind
|
||||
&& (is_block(else_) || is_if(else_))
|
||||
&& !then.span.from_expansion() && !else_.span.from_expansion()
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
|
||||
// workaround for rust-lang/rust#43081
|
||||
if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
|
||||
&& expr.span.lo().0 != 0 && expr.span.hi().0 != 0
|
||||
|
||||
// this will be a span from the closing ‘}’ of the “then” block (excluding) to
|
||||
// the “if” of the “else if” block (excluding)
|
||||
let else_span = then.span.between(else_.span);
|
||||
&& let else_span = then.span.between(else_.span)
|
||||
|
||||
// the snippet should look like " else \n " with maybe comments anywhere
|
||||
// it’s bad when there is a ‘\n’ after the “else”
|
||||
if let Some(else_snippet) = snippet_opt(cx, else_span);
|
||||
if let Some((pre_else, post_else)) = else_snippet.split_once("else");
|
||||
if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
|
||||
|
||||
then {
|
||||
&& let Some(else_snippet) = snippet_opt(cx, else_span)
|
||||
&& let Some((pre_else, post_else)) = else_snippet.split_once("else")
|
||||
&& let Some((_, post_else_post_eol)) = post_else.split_once('\n')
|
||||
{
|
||||
// Allow allman style braces `} \n else \n {`
|
||||
if_chain! {
|
||||
if is_block(else_);
|
||||
if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
|
||||
if is_block(else_)
|
||||
&& let Some((_, pre_else_post_eol)) = pre_else.split_once('\n')
|
||||
// Exactly one eol before and after the else
|
||||
if !pre_else_post_eol.contains('\n');
|
||||
if !post_else_post_eol.contains('\n');
|
||||
then {
|
||||
&& !pre_else_post_eol.contains('\n')
|
||||
&& !post_else_post_eol.contains('\n')
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't warn if the only thing inside post_else_post_eol is a comment block.
|
||||
let trimmed_post_else_post_eol = post_else_post_eol.trim();
|
||||
if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
let else_desc = if is_if(else_) { "if" } else { "{..}" };
|
||||
|
@ -255,7 +246,6 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
@ -272,15 +262,15 @@ fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
|
|||
fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Array(ref array) = expr.kind {
|
||||
for element in array {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
|
||||
if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span);
|
||||
let space_span = lhs.span.between(op.span);
|
||||
if let Some(space_snippet) = snippet_opt(cx, space_span);
|
||||
let lint_span = lhs.span.with_lo(lhs.span.hi());
|
||||
if space_snippet.contains('\n');
|
||||
if indentation(cx, op.span) <= indentation(cx, lhs.span);
|
||||
then {
|
||||
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind
|
||||
&& has_unary_equivalent(op.node)
|
||||
&& lhs.span.eq_ctxt(op.span)
|
||||
&& let space_span = lhs.span.between(op.span)
|
||||
&& let Some(space_snippet) = snippet_opt(cx, space_span)
|
||||
&& let lint_span = lhs.span.with_lo(lhs.span.hi())
|
||||
&& space_snippet.contains('\n')
|
||||
&& indentation(cx, op.span) <= indentation(cx, lhs.span)
|
||||
{
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
POSSIBLE_MISSING_COMMA,
|
||||
|
@ -292,24 +282,22 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
|
||||
if_chain! {
|
||||
if !first.span.from_expansion() && !second.span.from_expansion();
|
||||
if matches!(first.kind, ExprKind::If(..));
|
||||
if is_block(second) || is_if(second);
|
||||
if !first.span.from_expansion() && !second.span.from_expansion()
|
||||
&& matches!(first.kind, ExprKind::If(..))
|
||||
&& (is_block(second) || is_if(second))
|
||||
|
||||
// Proc-macros can give weird spans. Make sure this is actually an `if`.
|
||||
if is_span_if(cx, first.span);
|
||||
&& is_span_if(cx, first.span)
|
||||
|
||||
// If there is a line break between the two expressions, don't lint.
|
||||
// If there is a non-whitespace character, this span came from a proc-macro.
|
||||
let else_span = first.span.between(second.span);
|
||||
if let Some(else_snippet) = snippet_opt(cx, else_span);
|
||||
if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
|
||||
then {
|
||||
&& let else_span = first.span.between(second.span)
|
||||
&& let Some(else_snippet) = snippet_opt(cx, else_span)
|
||||
&& !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace())
|
||||
{
|
||||
let (looks_like, next_thing) = if is_if(second) {
|
||||
("an `else if`", "the second `if`")
|
||||
} else {
|
||||
|
@ -322,12 +310,9 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
|
|||
else_span,
|
||||
&format!("this looks like {looks_like} but the `else` is missing"),
|
||||
None,
|
||||
&format!(
|
||||
"to remove this lint, add the missing `else` or add a new line before {next_thing}",
|
||||
),
|
||||
&format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_block(expr: &Expr) -> bool {
|
||||
|
|
|
@ -2,7 +2,6 @@ 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, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -46,41 +45,31 @@ declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
|
||||
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind
|
||||
&& let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind
|
||||
|
||||
// check if the first part of the path is some integer primitive
|
||||
if let TyKind::Path(ty_qpath) = &ty.kind;
|
||||
let ty_res = cx.qpath_res(ty_qpath, ty.hir_id);
|
||||
if let def::Res::PrimTy(prim_ty) = ty_res;
|
||||
if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_));
|
||||
&& let TyKind::Path(ty_qpath) = &ty.kind
|
||||
&& let ty_res = cx.qpath_res(ty_qpath, ty.hir_id)
|
||||
&& let def::Res::PrimTy(prim_ty) = ty_res
|
||||
&& matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_))
|
||||
|
||||
// check if the second part of the path indeed calls the associated
|
||||
// function `from_str_radix`
|
||||
if pathseg.ident.name.as_str() == "from_str_radix";
|
||||
&& pathseg.ident.name.as_str() == "from_str_radix"
|
||||
|
||||
// check if the second argument is a primitive `10`
|
||||
if is_integer_literal(radix, 10);
|
||||
|
||||
then {
|
||||
&& is_integer_literal(radix, 10)
|
||||
{
|
||||
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if is_ty_stringish(cx, ty) {
|
||||
expr
|
||||
} else {
|
||||
&src
|
||||
}
|
||||
if is_ty_stringish(cx, ty) { expr } else { &src }
|
||||
} else {
|
||||
&src
|
||||
};
|
||||
|
||||
let sugg = Sugg::hir_with_applicability(
|
||||
cx,
|
||||
expr,
|
||||
"<string>",
|
||||
&mut Applicability::MachineApplicable
|
||||
).maybe_par();
|
||||
let sugg =
|
||||
Sugg::hir_with_applicability(cx, expr, "<string>", &mut Applicability::MachineApplicable).maybe_par();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -89,11 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
|||
"this call to `from_str_radix` can be replaced with a call to `str::parse`",
|
||||
"try",
|
||||
format!("{sugg}.parse::<{}>()", prim_ty.name_str()),
|
||||
Applicability::MaybeIncorrect
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a Ty is `String` or `&str`
|
||||
|
|
|
@ -52,49 +52,44 @@ fn report(
|
|||
}
|
||||
|
||||
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
|
||||
if_chain! {
|
||||
if let FnKind::ItemFn(ident, generics, _) = kind;
|
||||
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
|
||||
if !is_in_test_function(cx.tcx, hir_id);
|
||||
then {
|
||||
if let FnKind::ItemFn(ident, generics, _) = kind
|
||||
&& cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
|
||||
&& !is_in_test_function(cx.tcx, hir_id)
|
||||
{
|
||||
for param in generics.params {
|
||||
if param.is_impl_trait() {
|
||||
report(cx, param, ident, generics, body.params[0].span);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
||||
if_chain! {
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id());
|
||||
if let hir::ItemKind::Impl(impl_) = item.kind;
|
||||
if let hir::Impl { of_trait, .. } = *impl_;
|
||||
if of_trait.is_none();
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public();
|
||||
if !is_in_test_function(cx.tcx, impl_item.hir_id());
|
||||
then {
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind
|
||||
&& let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id())
|
||||
&& let hir::ItemKind::Impl(impl_) = item.kind
|
||||
&& let hir::Impl { of_trait, .. } = *impl_
|
||||
&& of_trait.is_none()
|
||||
&& let body = cx.tcx.hir().body(body_id)
|
||||
&& cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
|
||||
&& !is_in_test_function(cx.tcx, impl_item.hir_id())
|
||||
{
|
||||
for param in impl_item.generics.params {
|
||||
if param.is_impl_trait() {
|
||||
report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) {
|
||||
if_chain! {
|
||||
if !avoid_breaking_exported_api;
|
||||
if let TraitItemKind::Fn(_, _) = trait_item.kind;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id());
|
||||
if !avoid_breaking_exported_api
|
||||
&& let TraitItemKind::Fn(_, _) = trait_item.kind
|
||||
&& let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id())
|
||||
// ^^ (Will always be a trait)
|
||||
if !item.vis_span.is_empty(); // Is public
|
||||
if !is_in_test_function(cx.tcx, trait_item.hir_id());
|
||||
then {
|
||||
&& !item.vis_span.is_empty() // Is public
|
||||
&& !is_in_test_function(cx.tcx, trait_item.hir_id())
|
||||
{
|
||||
for param in trait_item.generics.params {
|
||||
if param.is_impl_trait() {
|
||||
let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1));
|
||||
|
@ -102,5 +97,4 @@ pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
|
|||
// Body must be &(mut) <self_data>.name
|
||||
// self_data is not necessarily self, to also lint sub-getters, etc…
|
||||
|
||||
let block_expr = if_chain! {
|
||||
if let ExprKind::Block(block,_) = body.value.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(block_expr) = block.expr;
|
||||
then {
|
||||
let block_expr = if let ExprKind::Block(block, _) = body.value.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& let Some(block_expr) = block.expr
|
||||
{
|
||||
block_expr
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let expr_span = block_expr.span;
|
||||
|
||||
|
@ -61,14 +59,12 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
|
|||
} else {
|
||||
block_expr
|
||||
};
|
||||
let (self_data, used_ident) = if_chain! {
|
||||
if let ExprKind::Field(self_data, ident) = expr.kind;
|
||||
if ident.name.as_str() != name;
|
||||
then {
|
||||
let (self_data, used_ident) = if let ExprKind::Field(self_data, ident) = expr.kind
|
||||
&& ident.name.as_str() != name
|
||||
{
|
||||
(self_data, ident)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut used_field = None;
|
||||
|
|
|
@ -86,15 +86,15 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S
|
|||
}
|
||||
|
||||
fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
|
||||
if_chain! {
|
||||
if let Adt(adt, subst) = err_ty.kind();
|
||||
if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local();
|
||||
if let Some(hir::Node::Item(item)) = cx
|
||||
.tcx
|
||||
.hir()
|
||||
.find_by_def_id(local_def_id);
|
||||
if let hir::ItemKind::Enum(ref def, _) = item.kind;
|
||||
then {
|
||||
if let Adt(adt, subst) = err_ty.kind()
|
||||
&& let Some(local_def_id) = err_ty
|
||||
.ty_adt_def()
|
||||
.expect("already checked this is adt")
|
||||
.did()
|
||||
.as_local()
|
||||
&& let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(local_def_id)
|
||||
&& let hir::ItemKind::Enum(ref def, _) = item.kind
|
||||
{
|
||||
let variants_size = AdtVariantInfo::new(cx, *adt, subst);
|
||||
if let Some((first_variant, variants)) = variants_size.split_first()
|
||||
&& first_variant.size >= large_err_threshold
|
||||
|
@ -115,17 +115,19 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
|
|||
let variant_def = &def.variants[variant.ind];
|
||||
diag.span_label(
|
||||
variant_def.span,
|
||||
format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size),
|
||||
format!(
|
||||
"the variant `{}` contains at least {} bytes",
|
||||
variant_def.ident, variant.size
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let ty_size = approx_ty_size(cx, err_ty);
|
||||
if ty_size >= large_err_threshold {
|
||||
span_lint_and_then(
|
||||
|
@ -140,5 +142,4 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::intravisit::{self as visit, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -127,15 +126,13 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
|
|||
}
|
||||
|
||||
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind;
|
||||
if path.ident.as_str() == "lock";
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if is_type_diagnostic_item(cx, ty, sym::Mutex);
|
||||
then {
|
||||
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
|
||||
&& path.ident.as_str() == "lock"
|
||||
&& let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Mutex)
|
||||
{
|
||||
Some(self_arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
|
@ -337,20 +335,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
|||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fun, args) = e.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
|
||||
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
|
||||
if let Some(ty_did) = ty_path.res.opt_def_id();
|
||||
then {
|
||||
if let ExprKind::Call(fun, args) = e.kind
|
||||
&& let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind
|
||||
&& let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind
|
||||
&& let Some(ty_did) = ty_path.res.opt_def_id()
|
||||
{
|
||||
if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashMap::default()".to_string());
|
||||
self.suggestions.insert(e.span, "HashMap::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
self.suggestions.insert(
|
||||
e.span,
|
||||
|
@ -362,8 +358,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
|||
}
|
||||
} else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashSet::default()".to_string());
|
||||
self.suggestions.insert(e.span, "HashSet::default()".to_string());
|
||||
} else if method.ident.name == sym!(with_capacity) {
|
||||
self.suggestions.insert(
|
||||
e.span,
|
||||
|
@ -375,7 +370,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk_expr(self, e);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::consts::{constant, Constant};
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
|
@ -40,30 +39,39 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::If(cond, then, None) = expr.kind;
|
||||
if let ExprKind::DropTemps(expr1) = cond.kind;
|
||||
if let Some((c, op_node, l)) = get_const(cx, expr1);
|
||||
if let BinOpKind::Ne | BinOpKind::Lt = op_node;
|
||||
if let ExprKind::Block(block, None) = then.kind;
|
||||
if let Block {
|
||||
if let ExprKind::If(cond, then, None) = expr.kind
|
||||
&& let ExprKind::DropTemps(expr1) = cond.kind
|
||||
&& let Some((c, op_node, l)) = get_const(cx, expr1)
|
||||
&& let BinOpKind::Ne | BinOpKind::Lt = op_node
|
||||
&& let ExprKind::Block(block, None) = then.kind
|
||||
&& let Block {
|
||||
stmts:
|
||||
[Stmt
|
||||
{ kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }],
|
||||
expr: None, ..} |
|
||||
Block { stmts: [], expr: Some(ex), ..} = block;
|
||||
if let ExprKind::AssignOp(op1, target, value) = ex.kind;
|
||||
let ty = cx.typeck_results().expr_ty(target);
|
||||
if Some(c) == get_int_max(ty);
|
||||
let ctxt = expr.span.ctxt();
|
||||
if ex.span.ctxt() == ctxt;
|
||||
if expr1.span.ctxt() == ctxt;
|
||||
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
|
||||
if BinOpKind::Add == op1.node;
|
||||
if let ExprKind::Lit(lit) = value.kind;
|
||||
if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
|
||||
if block.expr.is_none();
|
||||
then {
|
||||
[
|
||||
Stmt {
|
||||
kind: StmtKind::Expr(ex) | StmtKind::Semi(ex),
|
||||
..
|
||||
},
|
||||
],
|
||||
expr: None,
|
||||
..
|
||||
}
|
||||
| Block {
|
||||
stmts: [],
|
||||
expr: Some(ex),
|
||||
..
|
||||
} = block
|
||||
&& let ExprKind::AssignOp(op1, target, value) = ex.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(target)
|
||||
&& Some(c) == get_int_max(ty)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& ex.span.ctxt() == ctxt
|
||||
&& expr1.span.ctxt() == ctxt
|
||||
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
|
||||
&& BinOpKind::Add == op1.node
|
||||
&& let ExprKind::Lit(lit) = value.kind
|
||||
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
|
||||
&& block.expr.is_none()
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
|
||||
let sugg = if let Some(parent) = get_parent_expr(cx, expr)
|
||||
|
@ -74,8 +82,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
|||
} else {
|
||||
format!("{code} = {code}.saturating_add(1);")
|
||||
};
|
||||
span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app);
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IMPLICIT_SATURATING_ADD,
|
||||
expr.span,
|
||||
"manual saturating add detected",
|
||||
"use instead",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
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;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
|
||||
|
@ -46,22 +45,20 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr);
|
||||
if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr)
|
||||
|
||||
// Check if the conditional expression is a binary operation
|
||||
if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
|
||||
&& let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
|
||||
|
||||
// Ensure that the binary operator is >, !=, or <
|
||||
if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
|
||||
&& (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
|
||||
|
||||
// Check if assign operation is done
|
||||
if let Some(target) = subtracts_one(cx, then);
|
||||
&& let Some(target) = subtracts_one(cx, then)
|
||||
|
||||
// Extracting out the variable name
|
||||
if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind;
|
||||
|
||||
then {
|
||||
&& let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
|
||||
{
|
||||
// Handle symmetric conditions in the if statement
|
||||
let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
|
||||
if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
|
||||
|
@ -97,35 +94,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
}
|
||||
},
|
||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||
if_chain! {
|
||||
if name.ident.as_str() == "MIN";
|
||||
if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
|
||||
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
|
||||
if cx.tcx.type_of(impl_id).instantiate_identity().is_integral();
|
||||
then {
|
||||
print_lint_and_sugg(cx, var_name, expr)
|
||||
}
|
||||
if name.ident.as_str() == "MIN"
|
||||
&& let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(const_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
ExprKind::Call(func, []) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
|
||||
if name.ident.as_str() == "min_value";
|
||||
if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
|
||||
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl
|
||||
if cx.tcx.type_of(impl_id).instantiate_identity().is_integral();
|
||||
then {
|
||||
print_lint_and_sugg(cx, var_name, expr)
|
||||
}
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
|
||||
&& name.ident.as_str() == "min_value"
|
||||
&& let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(func_id)
|
||||
&& let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
|
||||
{
|
||||
print_lint_and_sugg(cx, var_name, expr);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
|
||||
|
@ -135,19 +127,15 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
|
|||
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
|
||||
},
|
||||
ExprKind::Assign(target, value, _) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(ref op1, left1, right1) = value.kind;
|
||||
if BinOpKind::Sub == op1.node;
|
||||
|
||||
if SpanlessEq::new(cx).eq_expr(left1, target);
|
||||
|
||||
if is_integer_literal(right1, 1);
|
||||
then {
|
||||
if let ExprKind::Binary(ref op1, left1, right1) = value.kind
|
||||
&& BinOpKind::Sub == op1.node
|
||||
&& SpanlessEq::new(cx).eq_expr(left1, target)
|
||||
&& is_integer_literal(right1, 1)
|
||||
{
|
||||
Some(target)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
|
@ -66,15 +65,14 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if let ExprKind::Struct(qpath, fields, base) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let Some(adt_def) = ty.ty_adt_def();
|
||||
if adt_def.is_struct();
|
||||
if let Some(variant) = adt_def.variants().iter().next();
|
||||
if fields.iter().all(|f| f.is_shorthand);
|
||||
then {
|
||||
if !expr.span.from_expansion()
|
||||
&& let ExprKind::Struct(qpath, fields, base) = expr.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(expr)
|
||||
&& let Some(adt_def) = ty.ty_adt_def()
|
||||
&& adt_def.is_struct()
|
||||
&& let Some(variant) = adt_def.variants().iter().next()
|
||||
&& fields.iter().all(|f| f.is_shorthand)
|
||||
{
|
||||
let mut def_order_map = FxHashMap::default();
|
||||
for (idx, field) in variant.fields.iter().enumerate() {
|
||||
def_order_map.insert(field.name, idx);
|
||||
|
@ -100,7 +98,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
String::new()
|
||||
};
|
||||
|
||||
let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}",
|
||||
let sugg = format!(
|
||||
"{} {{ {fields_snippet}{base_snippet} }}",
|
||||
snippet(cx, qpath.span(), ".."),
|
||||
);
|
||||
|
||||
|
@ -112,8 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
|||
"try",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::higher::IfLet;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -70,23 +69,20 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
|
||||
if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
|
||||
if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
|
||||
if self.msrv.meets(msrvs::SLICE_PATTERNS);
|
||||
|
||||
let found_slices = find_slice_values(cx, let_pat);
|
||||
if !found_slices.is_empty();
|
||||
let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
|
||||
if !filtered_slices.is_empty();
|
||||
then {
|
||||
if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some())
|
||||
&& let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr)
|
||||
&& !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id)
|
||||
&& self.msrv.meets(msrvs::SLICE_PATTERNS)
|
||||
&& let found_slices = find_slice_values(cx, let_pat)
|
||||
&& !found_slices.is_empty()
|
||||
&& let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then)
|
||||
&& !filtered_slices.is_empty()
|
||||
{
|
||||
for slice in filtered_slices.values() {
|
||||
lint_slice(cx, slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -245,29 +241,27 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
|
|||
max_suggested_slice,
|
||||
} = *self;
|
||||
|
||||
if_chain! {
|
||||
if let Some(use_info) = slice_lint_info.get_mut(&local_id)
|
||||
// Check if this is even a local we're interested in
|
||||
if let Some(use_info) = slice_lint_info.get_mut(&local_id);
|
||||
|
||||
let map = cx.tcx.hir();
|
||||
&& let map = cx.tcx.hir()
|
||||
|
||||
// Checking for slice indexing
|
||||
let parent_id = map.parent_id(expr.hir_id);
|
||||
if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
|
||||
if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind;
|
||||
if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr);
|
||||
if let Ok(index_value) = index_value.try_into();
|
||||
if index_value < max_suggested_slice;
|
||||
&& let parent_id = map.parent_id(expr.hir_id)
|
||||
&& let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id)
|
||||
&& let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind
|
||||
&& let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr)
|
||||
&& let Ok(index_value) = index_value.try_into()
|
||||
&& index_value < max_suggested_slice
|
||||
|
||||
// Make sure that this slice index is read only
|
||||
let maybe_addrof_id = map.parent_id(parent_id);
|
||||
if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
|
||||
if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
|
||||
then {
|
||||
&& let maybe_addrof_id = map.parent_id(parent_id)
|
||||
&& let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id)
|
||||
&& let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind
|
||||
{
|
||||
use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The slice was used for something other than indexing
|
||||
self.slice_lint_info.remove(&local_id);
|
||||
|
|
|
@ -89,27 +89,17 @@ impl LateLintPass<'_> for InstantSubtraction {
|
|||
rhs,
|
||||
) = expr.kind
|
||||
{
|
||||
if_chain! {
|
||||
if is_instant_now_call(cx, lhs);
|
||||
|
||||
if is_an_instant(cx, rhs);
|
||||
if let Some(sugg) = Sugg::hir_opt(cx, rhs);
|
||||
|
||||
then {
|
||||
print_manual_instant_elapsed_sugg(cx, expr, sugg)
|
||||
} else {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
if self.msrv.meets(msrvs::TRY_FROM);
|
||||
|
||||
if is_an_instant(cx, lhs);
|
||||
if is_a_duration(cx, rhs);
|
||||
|
||||
then {
|
||||
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_instant_now_call(cx, lhs)
|
||||
&& is_an_instant(cx, rhs)
|
||||
&& let Some(sugg) = Sugg::hir_opt(cx, rhs)
|
||||
{
|
||||
print_manual_instant_elapsed_sugg(cx, expr, sugg);
|
||||
} else if !expr.span.from_expansion()
|
||||
&& self.msrv.meets(msrvs::TRY_FROM)
|
||||
&& is_an_instant(cx, lhs)
|
||||
&& is_a_duration(cx, rhs)
|
||||
{
|
||||
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -47,21 +46,19 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if_chain! {
|
||||
if !item.span.from_expansion();
|
||||
if let ItemKind::Const(_, generics, _) = &item.kind;
|
||||
if !item.span.from_expansion()
|
||||
&& let ItemKind::Const(_, generics, _) = &item.kind
|
||||
// Since static items may not have generics, skip generic const items.
|
||||
// FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
|
||||
// doesn't account for empty where-clauses that only consist of keyword `where` IINM.
|
||||
if generics.params.is_empty() && !generics.has_where_clause_predicates;
|
||||
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
|
||||
if let ty::Array(element_type, cst) = ty.kind();
|
||||
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
|
||||
if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx);
|
||||
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
|
||||
if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size);
|
||||
|
||||
then {
|
||||
&& generics.params.is_empty() && !generics.has_where_clause_predicates
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& let ty::Array(element_type, cst) = ty.kind()
|
||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
||||
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
|
||||
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
|
||||
{
|
||||
let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
|
||||
let sugg_span = Span::new(
|
||||
hi_pos - BytePos::from_usize("const".len()),
|
||||
|
@ -81,9 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
|||
"static",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,13 +50,12 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
|||
|
||||
impl LateLintPass<'_> for LargeIncludeFile {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
|
||||
if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id);
|
||||
if let ExprKind::Lit(lit) = &expr.kind;
|
||||
then {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id)
|
||||
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
|
||||
&& let ExprKind::Lit(lit) = &expr.kind
|
||||
{
|
||||
let len = match &lit.node {
|
||||
// include_bytes
|
||||
LitKind::ByteStr(bstr, _) => bstr.len(),
|
||||
|
@ -82,5 +81,4 @@ impl LateLintPass<'_> for LargeIncludeFile {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
@ -131,25 +130,22 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
|||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||
if_chain! {
|
||||
if item.ident.name == sym::len;
|
||||
if let ImplItemKind::Fn(sig, _) = &item.kind;
|
||||
if sig.decl.implicit_self.has_implicit_self();
|
||||
if sig.decl.inputs.len() == 1;
|
||||
if cx.effective_visibilities.is_exported(item.owner_id.def_id);
|
||||
if matches!(sig.decl.output, FnRetTy::Return(_));
|
||||
if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
|
||||
if imp.of_trait.is_none();
|
||||
if let TyKind::Path(ty_path) = &imp.self_ty.kind;
|
||||
if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
|
||||
if let Some(local_id) = ty_id.as_local();
|
||||
let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
|
||||
if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
|
||||
if let Some(output) = parse_len_output(
|
||||
cx,
|
||||
cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()
|
||||
);
|
||||
then {
|
||||
if item.ident.name == sym::len
|
||||
&& let ImplItemKind::Fn(sig, _) = &item.kind
|
||||
&& sig.decl.implicit_self.has_implicit_self()
|
||||
&& sig.decl.inputs.len() == 1
|
||||
&& cx.effective_visibilities.is_exported(item.owner_id.def_id)
|
||||
&& matches!(sig.decl.output, FnRetTy::Return(_))
|
||||
&& let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
|
||||
&& imp.of_trait.is_none()
|
||||
&& let TyKind::Path(ty_path) = &imp.self_ty.kind
|
||||
&& let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id()
|
||||
&& let Some(local_id) = ty_id.as_local()
|
||||
&& let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id)
|
||||
&& !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id)
|
||||
&& let Some(output) =
|
||||
parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder())
|
||||
{
|
||||
let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
|
||||
Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
|
||||
Some(Node::Item(x)) => match x.kind {
|
||||
|
@ -157,11 +153,10 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
|||
ItemKind::Enum(..) => (x.ident.name, "enum"),
|
||||
ItemKind::Union(..) => (x.ident.name, "union"),
|
||||
_ => (x.ident.name, "type"),
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind)
|
||||
}
|
||||
check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::path_to_local_id;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{BindingAnnotation, Mutability};
|
||||
|
@ -61,24 +60,32 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
|
||||
let mut it = block.stmts.iter().peekable();
|
||||
while let Some(stmt) = it.next() {
|
||||
if_chain! {
|
||||
if let Some(expr) = it.peek();
|
||||
if let hir::StmtKind::Local(local) = stmt.kind;
|
||||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
|
||||
if let hir::StmtKind::Expr(if_) = expr.kind;
|
||||
if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
|
||||
if !is_local_used(cx, *cond, canonical_id);
|
||||
if let hir::ExprKind::Block(then, _) = then.kind;
|
||||
if let Some(value) = check_assign(cx, canonical_id, then);
|
||||
if !is_local_used(cx, value, canonical_id);
|
||||
then {
|
||||
if let Some(expr) = it.peek()
|
||||
&& let hir::StmtKind::Local(local) = stmt.kind
|
||||
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
|
||||
&& let hir::StmtKind::Expr(if_) = expr.kind
|
||||
&& let hir::ExprKind::If(
|
||||
hir::Expr {
|
||||
kind: hir::ExprKind::DropTemps(cond),
|
||||
..
|
||||
},
|
||||
then,
|
||||
else_,
|
||||
) = if_.kind
|
||||
&& !is_local_used(cx, *cond, canonical_id)
|
||||
&& let hir::ExprKind::Block(then, _) = then.kind
|
||||
&& let Some(value) = check_assign(cx, canonical_id, then)
|
||||
&& !is_local_used(cx, value, canonical_id)
|
||||
{
|
||||
let span = stmt.span.to(if_.span);
|
||||
|
||||
let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze(
|
||||
cx.tcx,
|
||||
cx.param_env,
|
||||
);
|
||||
if has_interior_mutability { return; }
|
||||
let has_interior_mutability = !cx
|
||||
.typeck_results()
|
||||
.node_type(canonical_id)
|
||||
.is_freeze(cx.tcx, cx.param_env);
|
||||
if has_interior_mutability {
|
||||
return;
|
||||
}
|
||||
|
||||
let (default_multi_stmts, default) = if let Some(else_) = else_ {
|
||||
if let hir::ExprKind::Block(else_, _) = else_.kind {
|
||||
|
@ -115,7 +122,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
value=snippet(cx, value.span, "<value>"),
|
||||
default=snippet(cx, default.span, "<default>"),
|
||||
);
|
||||
span_lint_and_then(cx,
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USELESS_LET_IF_SEQ,
|
||||
span,
|
||||
"`if _ { .. } else { .. }` is an expression",
|
||||
|
@ -129,8 +137,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
if !mutability.is_empty() {
|
||||
diag.note("you might not need `mut` at all");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,14 +149,18 @@ fn check_assign<'tcx>(
|
|||
decl: hir::HirId,
|
||||
block: &'tcx hir::Block<'_>,
|
||||
) -> Option<&'tcx hir::Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if block.expr.is_none();
|
||||
if let Some(expr) = block.stmts.iter().last();
|
||||
if let hir::StmtKind::Semi(expr) = expr.kind;
|
||||
if let hir::ExprKind::Assign(var, value, _) = expr.kind;
|
||||
if path_to_local_id(var, decl);
|
||||
then {
|
||||
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
|
||||
if block.expr.is_none()
|
||||
&& let Some(expr) = block.stmts.iter().last()
|
||||
&& let hir::StmtKind::Semi(expr) = expr.kind
|
||||
&& let hir::ExprKind::Assign(var, value, _) = expr.kind
|
||||
&& path_to_local_id(var, decl)
|
||||
{
|
||||
if block
|
||||
.stmts
|
||||
.iter()
|
||||
.take(block.stmts.len() - 1)
|
||||
.any(|stmt| is_local_used(cx, stmt, decl))
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(value)
|
||||
|
@ -156,5 +168,4 @@ fn check_assign<'tcx>(
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,11 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
|
|||
|
||||
impl LateLintPass<'_> for UnderscoreTyped {
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, local.span);
|
||||
if let Some(ty) = local.ty; // Ensure that it has a type defined
|
||||
if let TyKind::Infer = &ty.kind; // that type is '_'
|
||||
if local.span.eq_ctxt(ty.span);
|
||||
then {
|
||||
if !in_external_macro(cx.tcx.sess, local.span)
|
||||
&& let Some(ty) = local.ty // Ensure that it has a type defined
|
||||
&& let TyKind::Infer = &ty.kind // that type is '_'
|
||||
&& local.span.eq_ctxt(ty.span)
|
||||
{
|
||||
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
|
||||
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
|
||||
if snippet(cx, ty.span, "_").trim() != "_" {
|
||||
|
@ -45,9 +44,8 @@ impl LateLintPass<'_> for UnderscoreTyped {
|
|||
local.span,
|
||||
"variable declared with type underscore",
|
||||
Some(ty.span.with_lo(local.pat.span.hi())),
|
||||
"remove the explicit type `_` declaration"
|
||||
)
|
||||
}
|
||||
"remove the explicit type `_` declaration",
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -521,7 +521,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| {
|
||||
Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
|
||||
store.register_late_pass(|_| {
|
||||
Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
|
||||
|
|
|
@ -310,13 +310,11 @@ fn elision_suggestions(
|
|||
|
||||
// elision doesn't work for explicit self types, see rust-lang/rust#69064
|
||||
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(ident) = ident;
|
||||
if ident.name == kw::SelfLower;
|
||||
if !func.implicit_self.has_implicit_self();
|
||||
|
||||
if let Some(self_ty) = func.inputs.first();
|
||||
then {
|
||||
if let Some(ident) = ident
|
||||
&& ident.name == kw::SelfLower
|
||||
&& !func.implicit_self.has_implicit_self()
|
||||
&& let Some(self_ty) = func.inputs.first()
|
||||
{
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
visitor.visit_ty(self_ty);
|
||||
|
||||
|
@ -324,7 +322,6 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::numeric_literal::{NumericLiteral, Radix};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Expr, ExprKind, LitKind};
|
||||
use rustc_ast::token;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -255,11 +254,10 @@ impl LiteralDigitGrouping {
|
|||
}
|
||||
|
||||
fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
|
||||
if_chain! {
|
||||
if let Some(src) = snippet_opt(cx, span);
|
||||
if let Ok(lit_kind) = LitKind::from_token_lit(lit);
|
||||
if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind);
|
||||
then {
|
||||
if let Some(src) = snippet_opt(cx, span)
|
||||
&& let Ok(lit_kind) = LitKind::from_token_lit(lit)
|
||||
&& let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
|
||||
{
|
||||
if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) {
|
||||
return;
|
||||
}
|
||||
|
@ -269,18 +267,17 @@ impl LiteralDigitGrouping {
|
|||
}
|
||||
|
||||
let result = (|| {
|
||||
|
||||
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
|
||||
if let Some(fraction) = num_lit.fraction {
|
||||
let fractional_group_size = Self::get_group_size(
|
||||
fraction.rsplit('_'),
|
||||
num_lit.radix,
|
||||
self.lint_fraction_readability)?;
|
||||
let fractional_group_size =
|
||||
Self::get_group_size(fraction.rsplit('_'), num_lit.radix, self.lint_fraction_readability)?;
|
||||
|
||||
let consistent = Self::parts_consistent(integral_group_size,
|
||||
let consistent = Self::parts_consistent(
|
||||
integral_group_size,
|
||||
fractional_group_size,
|
||||
num_lit.integer.len(),
|
||||
fraction.len());
|
||||
fraction.len(),
|
||||
);
|
||||
if !consistent {
|
||||
return Err(WarningType::InconsistentDigitGrouping);
|
||||
};
|
||||
|
@ -289,18 +286,13 @@ impl LiteralDigitGrouping {
|
|||
Ok(())
|
||||
})();
|
||||
|
||||
|
||||
if let Err(warning_type) = result {
|
||||
let should_warn = match warning_type {
|
||||
| WarningType::UnreadableLiteral
|
||||
WarningType::UnreadableLiteral
|
||||
| WarningType::InconsistentDigitGrouping
|
||||
| WarningType::UnusualByteGroupings
|
||||
| WarningType::LargeDigitGroups => {
|
||||
!span.from_expansion()
|
||||
}
|
||||
WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
|
||||
true
|
||||
}
|
||||
| WarningType::LargeDigitGroups => !span.from_expansion(),
|
||||
WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true,
|
||||
};
|
||||
if should_warn {
|
||||
warning_type.display(num_lit.format(), cx, span);
|
||||
|
@ -308,7 +300,6 @@ impl LiteralDigitGrouping {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `false` if the check fails
|
||||
fn check_for_mistyped_suffix(
|
||||
|
@ -478,14 +469,13 @@ impl DecimalLiteralRepresentation {
|
|||
}
|
||||
fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
|
||||
// Lint integral literals.
|
||||
if_chain! {
|
||||
if let Ok(lit_kind) = LitKind::from_token_lit(lit);
|
||||
if let LitKind::Int(val, _) = lit_kind;
|
||||
if let Some(src) = snippet_opt(cx, span);
|
||||
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind);
|
||||
if num_lit.radix == Radix::Decimal;
|
||||
if val >= u128::from(self.threshold);
|
||||
then {
|
||||
if let Ok(lit_kind) = LitKind::from_token_lit(lit)
|
||||
&& let LitKind::Int(val, _) = lit_kind
|
||||
&& let Some(src) = snippet_opt(cx, span)
|
||||
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
|
||||
&& num_lit.radix == Radix::Decimal
|
||||
&& val >= u128::from(self.threshold)
|
||||
{
|
||||
let hex = format!("{val:#X}");
|
||||
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
|
||||
let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| {
|
||||
|
@ -493,7 +483,6 @@ impl DecimalLiteralRepresentation {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_lint(digits: &str) -> Result<(), WarningType> {
|
||||
if digits.len() == 1 {
|
||||
|
|
|
@ -2,7 +2,6 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{get_enclosing_block, is_integer_const};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_block, walk_expr};
|
||||
use rustc_hir::{Expr, Pat};
|
||||
|
@ -30,10 +29,9 @@ pub(super) fn check<'tcx>(
|
|||
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
|
||||
walk_block(&mut initialize_visitor, block);
|
||||
|
||||
if_chain! {
|
||||
if let Some((name, ty, initializer)) = initialize_visitor.get_result();
|
||||
if is_integer_const(cx, initializer, 0);
|
||||
then {
|
||||
if let Some((name, ty, initializer)) = initialize_visitor.get_result()
|
||||
&& is_integer_const(cx, initializer, 0)
|
||||
{
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let span = expr.span.with_hi(arg.span.hi());
|
||||
|
||||
|
@ -54,7 +52,7 @@ pub(super) fn check<'tcx>(
|
|||
applicability,
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some(ty::Int(int_ty)) => int_ty.name_str(),
|
||||
Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
|
||||
_ => return,
|
||||
|
@ -85,5 +83,4 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
|
@ -23,18 +22,21 @@ pub(super) fn check<'tcx>(
|
|||
let inner_expr = peel_blocks_with_stmt(body);
|
||||
// Check for the specific case that the result is returned and optimize suggestion for that (more
|
||||
// cases can be added later)
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr);
|
||||
if let Some(binding_id) = get_binding(pat);
|
||||
if let ExprKind::Block(block, _) = then.kind;
|
||||
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(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 {
|
||||
if let Some(higher::If {
|
||||
cond,
|
||||
then,
|
||||
r#else: None,
|
||||
}) = higher::If::hir(inner_expr)
|
||||
&& let Some(binding_id) = get_binding(pat)
|
||||
&& let ExprKind::Block(block, _) = then.kind
|
||||
&& let [stmt] = block.stmts
|
||||
&& let StmtKind::Semi(semi) = stmt.kind
|
||||
&& let ExprKind::Ret(Some(ret_value)) = semi.kind
|
||||
&& let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome)
|
||||
&& path_res(cx, inner_ret) == Res::Local(binding_id)
|
||||
&& let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
|
||||
// Checks if `pat` is a single reference to a binding (`&x`)
|
||||
|
@ -52,7 +54,12 @@ pub(super) fn check<'tcx>(
|
|||
);
|
||||
}
|
||||
let ty = cx.typeck_results().expr_ty(inner_ret);
|
||||
if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) {
|
||||
if cx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.copy_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
snippet.push_str(
|
||||
&format!(
|
||||
".find(|{}{}| {})",
|
||||
|
@ -85,16 +92,10 @@ pub(super) fn check<'tcx>(
|
|||
if applicability == Applicability::MaybeIncorrect {
|
||||
diag.note("you may need to dereference some variables");
|
||||
}
|
||||
diag.span_suggestion(
|
||||
lint_span,
|
||||
"replace with an iterator",
|
||||
snippet,
|
||||
applicability,
|
||||
);
|
||||
diag.span_suggestion(lint_span, "replace with an iterator", snippet, applicability);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_binding(pat: &Pat<'_>) -> Option<HirId> {
|
||||
|
@ -124,34 +125,30 @@ fn last_stmt_and_ret<'tcx>(
|
|||
if let Some(ret) = block.expr {
|
||||
return Some((last_stmt, ret));
|
||||
}
|
||||
if_chain! {
|
||||
if let [.., snd_last, _] = block.stmts;
|
||||
if let StmtKind::Semi(last_expr) = last_stmt.kind;
|
||||
if let ExprKind::Ret(Some(ret)) = last_expr.kind;
|
||||
then {
|
||||
if let [.., snd_last, _] = block.stmts
|
||||
&& let StmtKind::Semi(last_expr) = last_stmt.kind
|
||||
&& let ExprKind::Ret(Some(ret)) = last_expr.kind
|
||||
{
|
||||
return Some((snd_last, ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
|
||||
if_chain! {
|
||||
if let Some((node_hir, Node::Stmt(..))) = parent_iter.next()
|
||||
// This should be the loop
|
||||
if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
|
||||
// This should be the function body
|
||||
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 is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone);
|
||||
if let Some((_, Node::Expr(_block))) = parent_iter.next();
|
||||
&& let Some((_, Node::Block(block))) = parent_iter.next()
|
||||
&& let Some((last_stmt, last_ret)) = extract(block)
|
||||
&& last_stmt.hir_id == node_hir
|
||||
&& is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone)
|
||||
&& let Some((_, Node::Expr(_block))) = parent_iter.next()
|
||||
// This includes the function header
|
||||
if let Some((_, func)) = parent_iter.next();
|
||||
if func.fn_kind().is_some();
|
||||
then {
|
||||
&& let Some((_, func)) = parent_iter.next()
|
||||
&& func.fn_kind().is_some()
|
||||
{
|
||||
Some((block.stmts.last().unwrap(), last_ret))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use super::MANUAL_FLATTEN;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, Pat, PatKind};
|
||||
|
@ -21,25 +20,25 @@ pub(super) fn check<'tcx>(
|
|||
span: Span,
|
||||
) {
|
||||
let inner_expr = peel_blocks_with_stmt(body);
|
||||
if_chain! {
|
||||
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
|
||||
= higher::IfLet::hir(cx, inner_expr);
|
||||
= higher::IfLet::hir(cx, inner_expr)
|
||||
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
|
||||
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
|
||||
if path_to_local_id(let_expr, pat_hir_id);
|
||||
&& let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind
|
||||
&& 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;
|
||||
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;
|
||||
&& let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind
|
||||
&& let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id)
|
||||
&& 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)
|
||||
&& (some_ctor || ok_ctor)
|
||||
// Ensure expr in `if let` is not used afterwards
|
||||
if !is_local_used(cx, if_then, pat_hir_id);
|
||||
then {
|
||||
&& !is_local_used(cx, if_then, pat_hir_id)
|
||||
{
|
||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||
// Prepare the error message
|
||||
let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
|
||||
let msg =
|
||||
format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
|
||||
|
||||
// Prepare the help message
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
|
@ -47,40 +46,25 @@ pub(super) fn check<'tcx>(
|
|||
let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
|
||||
ty::Ref(_, inner, _) => match inner.kind() {
|
||||
ty::Ref(..) => ".copied()",
|
||||
_ => ""
|
||||
}
|
||||
_ => ""
|
||||
_ => "",
|
||||
},
|
||||
_ => "",
|
||||
};
|
||||
|
||||
let sugg = format!("{arg_snippet}{copied}.flatten()");
|
||||
|
||||
// If suggestion is not a one-liner, it won't be shown inline within the error message. In that case,
|
||||
// it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs
|
||||
// to refer to the correct relative position of the suggestion.
|
||||
// If suggestion is not a one-liner, it won't be shown inline within the error message. In that
|
||||
// case, it will be shown in the extra `help` message at the end, which is why the first
|
||||
// `help_msg` needs to refer to the correct relative position of the suggestion.
|
||||
let help_msg = if sugg.contains('\n') {
|
||||
"remove the `if let` statement in the for loop and then..."
|
||||
} else {
|
||||
"...and remove the `if let` statement in the for loop"
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_FLATTEN,
|
||||
span,
|
||||
&msg,
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
arg.span,
|
||||
"try",
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
diag.span_help(
|
||||
inner_expr.span,
|
||||
help_msg,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
span_lint_and_then(cx, MANUAL_FLATTEN, span, &msg, |diag| {
|
||||
diag.span_suggestion(arg.span, "try", sugg, applicability);
|
||||
diag.span_help(inner_expr.span, help_msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::walk_block;
|
||||
|
@ -59,23 +58,32 @@ pub(super) fn check<'tcx>(
|
|||
.map(|o| {
|
||||
o.and_then(|(lhs, rhs)| {
|
||||
let rhs = fetch_cloned_expr(rhs);
|
||||
if_chain! {
|
||||
if let ExprKind::Index(base_left, idx_left, _) = lhs.kind;
|
||||
if let ExprKind::Index(base_right, idx_right, _) = rhs.kind;
|
||||
if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left));
|
||||
if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some();
|
||||
if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts);
|
||||
if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
|
||||
if let ExprKind::Index(base_left, idx_left, _) = lhs.kind
|
||||
&& let ExprKind::Index(base_right, idx_right, _) = rhs.kind
|
||||
&& let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left))
|
||||
&& get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some()
|
||||
&& let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts)
|
||||
&& let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts)
|
||||
|
||||
// Source and destination must be different
|
||||
if path_to_local(base_left) != path_to_local(base_right);
|
||||
then {
|
||||
Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
|
||||
IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
|
||||
&& path_to_local(base_left) != path_to_local(base_right)
|
||||
{
|
||||
Some((
|
||||
ty,
|
||||
IndexExpr {
|
||||
base: base_left,
|
||||
idx: start_left,
|
||||
idx_offset: offset_left,
|
||||
},
|
||||
IndexExpr {
|
||||
base: base_right,
|
||||
idx: start_right,
|
||||
idx_offset: offset_right,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
|
||||
|
@ -118,11 +126,10 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
}
|
||||
|
||||
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, recv, [], _) = end.kind;
|
||||
if method.ident.name == sym::len;
|
||||
if path_to_local(recv) == path_to_local(base);
|
||||
then {
|
||||
if let ExprKind::MethodCall(method, recv, [], _) = end.kind
|
||||
&& method.ident.name == sym::len
|
||||
&& path_to_local(recv) == path_to_local(base)
|
||||
{
|
||||
if sugg.to_string() == end_str {
|
||||
sugg::EMPTY.into()
|
||||
} else {
|
||||
|
@ -130,13 +137,10 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
}
|
||||
} else {
|
||||
match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
sugg + &sugg::ONE.into()
|
||||
},
|
||||
ast::RangeLimits::Closed => sugg + &sugg::ONE.into(),
|
||||
ast::RangeLimits::HalfOpen => sugg,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let start_str = Sugg::hir(cx, start, "").into();
|
||||
|
@ -331,10 +335,12 @@ fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Opti
|
|||
}
|
||||
|
||||
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, arg, [], _) = expr.kind;
|
||||
if method.ident.name == sym::clone;
|
||||
then { arg } else { expr }
|
||||
if let ExprKind::MethodCall(method, arg, [], _) = expr.kind
|
||||
&& method.ident.name == sym::clone
|
||||
{
|
||||
arg
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,13 +31,17 @@ fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
|||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind;
|
||||
if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind;
|
||||
if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name);
|
||||
if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did());
|
||||
then {
|
||||
if let ExprKind::Block(
|
||||
Block {
|
||||
stmts: [], expr: None, ..
|
||||
},
|
||||
_,
|
||||
) = body.kind
|
||||
&& let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind
|
||||
&& [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name)
|
||||
&& let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did())
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MISSING_SPIN_LOOP,
|
||||
|
@ -48,9 +52,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
|
|||
"{ core::hint::spin_loop() }"
|
||||
} else {
|
||||
"{ std::hint::spin_loop() }"
|
||||
}).into(),
|
||||
Applicability::MachineApplicable
|
||||
})
|
||||
.into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use super::MUT_RANGE_BOUND;
|
||||
use clippy_utils::diagnostics::span_lint_and_note;
|
||||
use clippy_utils::{get_enclosing_block, higher, path_to_local};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
|
||||
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
|
@ -12,20 +11,18 @@ use rustc_middle::ty;
|
|||
use rustc_span::source_map::Span;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
..
|
||||
}) = higher::Range::hir(arg);
|
||||
let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end));
|
||||
if mut_id_start.is_some() || mut_id_end.is_some();
|
||||
then {
|
||||
}) = higher::Range::hir(arg)
|
||||
&& let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end))
|
||||
&& (mut_id_start.is_some() || mut_id_end.is_some())
|
||||
{
|
||||
let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
|
||||
mut_warn_with_span(cx, span_low);
|
||||
mut_warn_with_span(cx, span_high);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
|
||||
|
@ -42,14 +39,12 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
|
|||
}
|
||||
|
||||
fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
|
||||
if_chain! {
|
||||
if let Some(hir_id) = path_to_local(bound);
|
||||
if let Node::Pat(pat) = cx.tcx.hir().get(hir_id);
|
||||
if let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind;
|
||||
then {
|
||||
if let Some(hir_id) = path_to_local(bound)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
|
||||
&& let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind
|
||||
{
|
||||
return Some(hir_id);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::source::snippet;
|
|||
use clippy_utils::ty::has_iter_method;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -187,16 +186,14 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, recv, [], _) = expr.kind;
|
||||
if method.ident.name == sym::len;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind;
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.name == var;
|
||||
then {
|
||||
if let ExprKind::MethodCall(method, recv, [], _) = expr.kind
|
||||
&& method.ident.name == sym::len
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind
|
||||
&& path.segments.len() == 1
|
||||
&& path.segments[0].ident.name == var
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -207,18 +204,16 @@ fn is_end_eq_array_len<'tcx>(
|
|||
limits: ast::RangeLimits,
|
||||
indexed_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(lit) = end.kind;
|
||||
if let ast::LitKind::Int(end_int, _) = lit.node;
|
||||
if let ty::Array(_, arr_len_const) = indexed_ty.kind();
|
||||
if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env);
|
||||
then {
|
||||
if let ExprKind::Lit(lit) = end.kind
|
||||
&& let ast::LitKind::Int(end_int, _) = lit.node
|
||||
&& let ty::Array(_, arr_len_const) = indexed_ty.kind()
|
||||
&& let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
{
|
||||
return match limits {
|
||||
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
|
||||
ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
@ -248,13 +243,12 @@ struct VarVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind
|
||||
// the indexed container is referenced by a name
|
||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
|
||||
if let QPath::Resolved(None, seqvar) = *seqpath;
|
||||
if seqvar.segments.len() == 1;
|
||||
if is_local_used(self.cx, idx, self.var);
|
||||
then {
|
||||
&& let QPath::Resolved(None, seqvar) = *seqpath
|
||||
&& seqvar.segments.len() == 1
|
||||
&& is_local_used(self.cx, idx, self.var)
|
||||
{
|
||||
if self.prefer_mutable {
|
||||
self.indexed_mut.insert(seqvar.segments[0].ident.name);
|
||||
}
|
||||
|
@ -294,43 +288,37 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
|||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind
|
||||
// a range index op
|
||||
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind;
|
||||
if let Some(trait_id) = self
|
||||
&& let Some(trait_id) = self
|
||||
.cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|def_id| self.cx.tcx.trait_of_item(def_id));
|
||||
if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id))
|
||||
|| (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id));
|
||||
if !self.check(args_1, args_0, expr);
|
||||
then {
|
||||
.and_then(|def_id| self.cx.tcx.trait_of_item(def_id))
|
||||
&& ((meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id))
|
||||
|| (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)))
|
||||
&& !self.check(args_1, args_0, expr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Index(seqexpr, idx, _) = expr.kind
|
||||
// an index op
|
||||
if let ExprKind::Index(seqexpr, idx, _) = expr.kind;
|
||||
if !self.check(idx, seqexpr, expr);
|
||||
then {
|
||||
&& !self.check(idx, seqexpr, expr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
|
||||
// directly using a variable
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
|
||||
if let Res::Local(local_id) = path.res;
|
||||
then {
|
||||
&& let Res::Local(local_id) = path.res
|
||||
{
|
||||
if local_id == self.var {
|
||||
self.nonindex = true;
|
||||
} else {
|
||||
|
@ -338,7 +326,6 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
|||
self.referenced.insert(path.segments[0].ident.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let old = self.prefer_mutable;
|
||||
match expr.kind {
|
||||
|
|
|
@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
|||
use clippy_utils::path_to_local;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -44,17 +43,16 @@ pub(super) fn check<'tcx>(
|
|||
// Determine whether it is safe to lint the body
|
||||
let mut same_item_push_visitor = SameItemPushVisitor::new(cx);
|
||||
walk_expr(&mut same_item_push_visitor, body);
|
||||
if_chain! {
|
||||
if same_item_push_visitor.should_lint();
|
||||
if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push;
|
||||
let vec_ty = cx.typeck_results().expr_ty(vec);
|
||||
let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
|
||||
if cx
|
||||
if same_item_push_visitor.should_lint()
|
||||
&& let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push
|
||||
&& let vec_ty = cx.typeck_results().expr_ty(vec)
|
||||
&& let ty = vec_ty.walk().nth(1).unwrap().expect_ty()
|
||||
&& cx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.clone_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]));
|
||||
then {
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
// Make sure that the push does not involve possibly mutating values
|
||||
match pushed_item.kind {
|
||||
ExprKind::Path(ref qpath) => {
|
||||
|
@ -62,14 +60,13 @@ pub(super) fn check<'tcx>(
|
|||
// immutable bindings that are initialized with literal or constant
|
||||
Res::Local(hir_id) => {
|
||||
let node = cx.tcx.hir().get(hir_id);
|
||||
if_chain! {
|
||||
if let Node::Pat(pat) = node;
|
||||
if let PatKind::Binding(bind_ann, ..) = pat.kind;
|
||||
if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut));
|
||||
let parent_node = cx.tcx.hir().parent_id(hir_id);
|
||||
if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
|
||||
if let Some(init) = parent_let_expr.init;
|
||||
then {
|
||||
if let Node::Pat(pat) = node
|
||||
&& let PatKind::Binding(bind_ann, ..) = pat.kind
|
||||
&& !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut))
|
||||
&& let parent_node = cx.tcx.hir().parent_id(hir_id)
|
||||
&& let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node)
|
||||
&& let Some(init) = parent_let_expr.init
|
||||
{
|
||||
match init.kind {
|
||||
// immutable bindings that are initialized with literal
|
||||
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
|
@ -78,11 +75,10 @@ pub(super) fn check<'tcx>(
|
|||
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
|
||||
emit_lint(cx, vec, pushed_item, ctxt);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// constant
|
||||
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
|
||||
|
@ -93,7 +89,6 @@ pub(super) fn check<'tcx>(
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scans the body of the for loop and determines whether lint should be given
|
||||
|
@ -118,18 +113,16 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
fn should_lint(&self) -> bool {
|
||||
if_chain! {
|
||||
if !self.non_deterministic_expr;
|
||||
if !self.multiple_pushes;
|
||||
if let Some((vec, _, _)) = self.vec_push;
|
||||
if let Some(hir_id) = path_to_local(vec);
|
||||
then {
|
||||
if !self.non_deterministic_expr
|
||||
&& !self.multiple_pushes
|
||||
&& let Some((vec, _, _)) = self.vec_push
|
||||
&& let Some(hir_id) = path_to_local(vec)
|
||||
{
|
||||
!self.used_locals.contains(&hir_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
|
||||
|
@ -180,18 +173,16 @@ fn get_vec_push<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
stmt: &'tcx Stmt<'_>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(semi_stmt) = &stmt.kind
|
||||
// Extract method being called
|
||||
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
|
||||
if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind;
|
||||
&& let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind
|
||||
// Figure out the parameters for the method call
|
||||
if let Some(pushed_item) = args.first();
|
||||
&& let Some(pushed_item) = args.first()
|
||||
// Check that the method being called is push() on a Vec
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
|
||||
if path.ident.name.as_str() == "push";
|
||||
then {
|
||||
return Some((self_expr, pushed_item, semi_stmt.span.ctxt()))
|
||||
}
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec)
|
||||
&& path.ident.name.as_str() == "push"
|
||||
{
|
||||
return Some((self_expr, pushed_item, semi_stmt.span.ctxt()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use super::SINGLE_ELEMENT_LOOP;
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, snippet_with_applicability};
|
||||
use clippy_utils::visitors::contains_break_or_continue;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::util::parser::PREC_PREFIX;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -66,11 +65,10 @@ pub(super) fn check<'tcx>(
|
|||
ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
|
||||
_ => return,
|
||||
};
|
||||
if_chain! {
|
||||
if let ExprKind::Block(block, _) = body.kind;
|
||||
if !block.stmts.is_empty();
|
||||
if !contains_break_or_continue(body);
|
||||
then {
|
||||
if let ExprKind::Block(block, _) = body.kind
|
||||
&& !block.stmts.is_empty()
|
||||
&& !contains_break_or_continue(body)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
|
||||
let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
|
||||
|
@ -80,10 +78,12 @@ pub(super) fn check<'tcx>(
|
|||
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
|
||||
|
||||
// Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
|
||||
if !prefix.is_empty() && (
|
||||
if !prefix.is_empty()
|
||||
&& (
|
||||
// Precedence of internal expression is less than or equal to precedence of `&expr`.
|
||||
arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
|
||||
) {
|
||||
)
|
||||
{
|
||||
arg_snip = format!("({arg_snip})").into();
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,6 @@ pub(super) fn check<'tcx>(
|
|||
"try",
|
||||
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::ty::{has_iter_method, implements_trait};
|
||||
use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
|
||||
|
@ -145,10 +144,9 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
|||
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>) {
|
||||
// Look for declarations of the variable
|
||||
if_chain! {
|
||||
if l.pat.hir_id == self.var_id;
|
||||
if let PatKind::Binding(.., ident, _) = l.pat.kind;
|
||||
then {
|
||||
if l.pat.hir_id == self.var_id
|
||||
&& let PatKind::Binding(.., ident, _) = l.pat.kind
|
||||
{
|
||||
let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat));
|
||||
|
||||
self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
|
||||
|
@ -157,8 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
|||
ty,
|
||||
name: ident.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
walk_local(self, l);
|
||||
|
|
|
@ -2,7 +2,6 @@ use super::WHILE_IMMUTABLE_CONDITION;
|
|||
use clippy_utils::consts::constant;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
|
@ -95,10 +94,9 @@ struct VarCollectorVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
|
||||
fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref qpath) = ex.kind;
|
||||
if let QPath::Resolved(None, _) = *qpath;
|
||||
then {
|
||||
if let ExprKind::Path(ref qpath) = ex.kind
|
||||
&& let QPath::Resolved(None, _) = *qpath
|
||||
{
|
||||
match self.cx.qpath_res(qpath, ex.hir_id) {
|
||||
Res::Local(hir_id) => {
|
||||
self.ids.insert(hir_id);
|
||||
|
@ -111,7 +109,6 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
|
||||
|
|
|
@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::visitors::is_res_used;
|
||||
use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
|
@ -15,25 +14,18 @@ use rustc_span::symbol::sym;
|
|||
use rustc_span::Symbol;
|
||||
|
||||
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);
|
||||
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr)
|
||||
// check for `Some(..)` pattern
|
||||
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);
|
||||
&& let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
|
||||
&& 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;
|
||||
if is_trait_method(cx, let_expr, sym::Iterator);
|
||||
if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
|
||||
&& let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind
|
||||
&& method_name.ident.name == sym::next
|
||||
&& is_trait_method(cx, let_expr, sym::Iterator)
|
||||
&& let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr)
|
||||
// get the loop containing the match expression
|
||||
if !uses_iter(cx, &iter_expr_struct, if_then);
|
||||
then {
|
||||
(let_expr, iter_expr_struct, iter_expr, some_pat, expr)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
&& !uses_iter(cx, &iter_expr_struct, if_then)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let loop_var = if let Some(some_pat) = some_pat.first() {
|
||||
if is_refutable(cx, some_pat) {
|
||||
|
@ -51,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|
||||
|| !iter_expr_struct.can_move
|
||||
|| !iter_expr_struct.fields.is_empty()
|
||||
|| needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
|
||||
|| needs_mutable_borrow(cx, &iter_expr_struct, expr)
|
||||
{
|
||||
".by_ref()"
|
||||
} else {
|
||||
|
@ -62,12 +54,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
span_lint_and_sugg(
|
||||
cx,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
expr.span.with_hi(scrutinee_expr.span.hi()),
|
||||
expr.span.with_hi(let_expr.span.hi()),
|
||||
"this loop could be written as a `for` loop",
|
||||
"try",
|
||||
format!("for {loop_var} in {iterator}{by_ref}"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use hir::def::{DefKind, Res};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -90,18 +89,17 @@ impl MacroUseImports {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if_chain! {
|
||||
if cx.sess().opts.edition >= Edition::Edition2018;
|
||||
if let hir::ItemKind::Use(path, _kind) = &item.kind;
|
||||
let hir_id = item.hir_id();
|
||||
let attrs = cx.tcx.hir().attrs(hir_id);
|
||||
if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
|
||||
if let Some(id) = path.res.iter().find_map(|res| match res {
|
||||
if cx.sess().opts.edition >= Edition::Edition2018
|
||||
&& let hir::ItemKind::Use(path, _kind) = &item.kind
|
||||
&& let hir_id = item.hir_id()
|
||||
&& let attrs = cx.tcx.hir().attrs(hir_id)
|
||||
&& let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use))
|
||||
&& let Some(id) = path.res.iter().find_map(|res| match res {
|
||||
Res::Def(DefKind::Mod, id) => Some(id),
|
||||
_ => None,
|
||||
});
|
||||
if !id.is_local();
|
||||
then {
|
||||
})
|
||||
&& !id.is_local()
|
||||
{
|
||||
for kid in cx.tcx.module_children(id) {
|
||||
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
|
||||
let span = mac_attr.span;
|
||||
|
@ -109,13 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
|||
self.imports.push((def_path, span, hir_id));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if item.span.from_expansion() {
|
||||
} else if item.span.from_expansion() {
|
||||
self.push_unique_macro_pat_ty(cx, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
||||
if attr.span.from_expansion() {
|
||||
self.push_unique_macro(cx, attr.span);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_entrypoint_fn, is_no_std_crate};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
@ -43,21 +42,19 @@ impl LateLintPass<'_> for MainRecursion {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, _) = &expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
if is_entrypoint_fn(cx, def_id);
|
||||
then {
|
||||
if let ExprKind::Call(func, _) = &expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
&& is_entrypoint_fn(cx, def_id)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAIN_RECURSION,
|
||||
func.span,
|
||||
&format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
|
||||
None,
|
||||
"consider using another function for this recursion"
|
||||
)
|
||||
}
|
||||
"consider using another function for this recursion",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
|
@ -47,21 +46,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
span: Span,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(header) = kind.header();
|
||||
if !header.asyncness.is_async();
|
||||
if let Some(header) = kind.header()
|
||||
&& !header.asyncness.is_async()
|
||||
// Check that this function returns `impl Future`
|
||||
if let FnRetTy::Return(ret_ty) = decl.output;
|
||||
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
|
||||
if let Some(output) = future_output_ty(trait_ref);
|
||||
if captures_all_lifetimes(decl.inputs, &output_lifetimes);
|
||||
&& let FnRetTy::Return(ret_ty) = decl.output
|
||||
&& let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty)
|
||||
&& let Some(output) = future_output_ty(trait_ref)
|
||||
&& captures_all_lifetimes(decl.inputs, &output_lifetimes)
|
||||
// Check that the body of the function consists of one async block
|
||||
if let ExprKind::Block(block, _) = body.value.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(closure_body) = desugared_async_block(cx, block);
|
||||
if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
|
||||
cx.tcx.hir().get_by_def_id(def_id);
|
||||
then {
|
||||
&& let ExprKind::Block(block, _) = body.value.kind
|
||||
&& block.stmts.is_empty()
|
||||
&& let Some(closure_body) = desugared_async_block(cx, block)
|
||||
&& let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
|
||||
cx.tcx.hir().get_by_def_id(def_id)
|
||||
{
|
||||
let header_span = span.with_hi(ret_ty.span.hi());
|
||||
|
||||
span_lint_and_then(
|
||||
|
@ -70,12 +68,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
header_span,
|
||||
"this function can be simplified using the `async fn` syntax",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if let Some(vis_snip) = snippet_opt(cx, *vis_span);
|
||||
if let Some(header_snip) = snippet_opt(cx, header_span);
|
||||
if let Some(ret_pos) = position_before_rarrow(&header_snip);
|
||||
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
|
||||
then {
|
||||
if let Some(vis_snip) = snippet_opt(cx, *vis_span)
|
||||
&& let Some(header_snip) = snippet_opt(cx, header_span)
|
||||
&& let Some(ret_pos) = position_before_rarrow(&header_snip)
|
||||
&& let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
|
||||
{
|
||||
let header_snip = if vis_snip.is_empty() {
|
||||
format!("async {}", &header_snip[..ret_pos])
|
||||
} else {
|
||||
|
@ -87,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
header_span,
|
||||
help,
|
||||
format!("{header_snip}{ret_snip}"),
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
|
||||
|
@ -95,34 +92,31 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|
|||
block.span,
|
||||
"move the body of the async block to the enclosing function",
|
||||
body_snip,
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn future_trait_ref<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: &'tcx Ty<'tcx>,
|
||||
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
|
||||
if_chain! {
|
||||
if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind;
|
||||
let item = cx.tcx.hir().item(item_id);
|
||||
if let ItemKind::OpaqueTy(opaque) = &item.kind;
|
||||
if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind
|
||||
&& let item = cx.tcx.hir().item(item_id)
|
||||
&& let ItemKind::OpaqueTy(opaque) = &item.kind
|
||||
&& let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
|
||||
if let GenericBound::Trait(poly, _) = bound {
|
||||
Some(&poly.trait_ref)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
|
||||
then {
|
||||
})
|
||||
&& trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait()
|
||||
{
|
||||
let output_lifetimes = bounds
|
||||
.iter()
|
||||
.filter_map(|bound| {
|
||||
|
@ -136,23 +130,20 @@ fn future_trait_ref<'tcx>(
|
|||
|
||||
return Some((trait_ref, output_lifetimes));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(segment) = trait_ref.path.segments.last();
|
||||
if let Some(args) = segment.args;
|
||||
if args.bindings.len() == 1;
|
||||
let binding = &args.bindings[0];
|
||||
if binding.ident.name == sym::Output;
|
||||
if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind;
|
||||
then {
|
||||
if let Some(segment) = trait_ref.path.segments.last()
|
||||
&& let Some(args) = segment.args
|
||||
&& args.bindings.len() == 1
|
||||
&& let binding = &args.bindings[0]
|
||||
&& binding.ident.name == sym::Output
|
||||
&& let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind
|
||||
{
|
||||
return Some(output);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -181,18 +172,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
|
|||
}
|
||||
|
||||
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(block_expr) = block.expr;
|
||||
if let Expr {
|
||||
if let Some(block_expr) = block.expr
|
||||
&& let Expr {
|
||||
kind: ExprKind::Closure(&Closure { body, .. }),
|
||||
..
|
||||
} = block_expr;
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block));
|
||||
then {
|
||||
} = block_expr
|
||||
&& let closure_body = cx.tcx.hir().body(body)
|
||||
&& closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block))
|
||||
{
|
||||
return Some(closure_body);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
@ -53,18 +53,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
|
||||
if let BinOpKind::Mul = &bin_op.node;
|
||||
if !in_external_macro(cx.sess(), expr.span);
|
||||
let ctxt = expr.span.ctxt();
|
||||
if left_expr.span.ctxt() == ctxt;
|
||||
if right_expr.span.ctxt() == ctxt;
|
||||
if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
|
||||
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
|
||||
if let ExprKind::Lit(lit) = &other_expr.kind;
|
||||
if let LitKind::Int(8, _) = lit.node;
|
||||
then {
|
||||
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
|
||||
&& let BinOpKind::Mul = &bin_op.node
|
||||
&& !in_external_macro(cx.sess(), expr.span)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& left_expr.span.ctxt() == ctxt
|
||||
&& right_expr.span.ctxt() == ctxt
|
||||
&& let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr)
|
||||
&& matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_))
|
||||
&& let ExprKind::Lit(lit) = &other_expr.kind
|
||||
&& let LitKind::Int(8, _) = lit.node
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
|
||||
let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
|
||||
|
@ -80,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -98,23 +96,23 @@ fn get_one_size_of_ty<'tcx>(
|
|||
}
|
||||
|
||||
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(count_func, _func_args) = expr.kind;
|
||||
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
|
||||
|
||||
if let QPath::Resolved(_, count_func_path) = count_func_qpath;
|
||||
if let Some(segment_zero) = count_func_path.segments.first();
|
||||
if let Some(args) = segment_zero.args;
|
||||
if let Some(GenericArg::Type(real_ty)) = args.args.first();
|
||||
|
||||
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
|
||||
then {
|
||||
cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty))
|
||||
if let ExprKind::Call(count_func, _func_args) = expr.kind
|
||||
&& let ExprKind::Path(ref count_func_qpath) = count_func.kind
|
||||
&& let QPath::Resolved(_, count_func_path) = count_func_qpath
|
||||
&& let Some(segment_zero) = count_func_path.segments.first()
|
||||
&& let Some(args) = segment_zero.args
|
||||
&& let Some(GenericArg::Type(real_ty)) = args.args.first()
|
||||
&& let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
|
||||
{
|
||||
cx.typeck_results()
|
||||
.node_args(count_func.hir_id)
|
||||
.types()
|
||||
.next()
|
||||
.map(|resolved_ty| (*real_ty, resolved_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
|||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
|
@ -71,12 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
|
||||
if let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
|
||||
if let ExprKind::Path(target_path) = &target_arg.kind;
|
||||
then {
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
|
||||
&& let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
|
||||
&& let ExprKind::Path(target_path) = &target_arg.kind
|
||||
{
|
||||
let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
|
||||
StripKind::Prefix
|
||||
} else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
|
||||
|
@ -89,37 +87,44 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
return;
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let Res::Local(hir_id) = target_res;
|
||||
if let Some(used_mutably) = mutated_variables(then, cx);
|
||||
if used_mutably.contains(&hir_id);
|
||||
then {
|
||||
if let Res::Local(hir_id) = target_res
|
||||
&& let Some(used_mutably) = mutated_variables(then, cx)
|
||||
&& used_mutably.contains(&hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
|
||||
if !strippings.is_empty() {
|
||||
|
||||
let kind_word = match strip_kind {
|
||||
StripKind::Prefix => "prefix",
|
||||
StripKind::Suffix => "suffix",
|
||||
};
|
||||
|
||||
let test_span = expr.span.until(then.span);
|
||||
span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_STRIP,
|
||||
strippings[0],
|
||||
&format!("stripping a {kind_word} manually"),
|
||||
|diag| {
|
||||
diag.span_note(test_span, format!("the {kind_word} was tested here"));
|
||||
multispan_sugg(
|
||||
diag,
|
||||
&format!("try using the `strip_{kind_word}` method"),
|
||||
vec![(test_span,
|
||||
format!("if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
|
||||
vec![(
|
||||
test_span,
|
||||
format!(
|
||||
"if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
|
||||
snippet(cx, target_arg.span, ".."),
|
||||
snippet(cx, pattern.span, "..")))]
|
||||
.into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
|
||||
snippet(cx, pattern.span, "..")
|
||||
),
|
||||
)]
|
||||
.into_iter()
|
||||
.chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,16 +134,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
|
||||
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
|
||||
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, arg, [], _) = expr.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, method_def_id, &paths::STR_LEN);
|
||||
then {
|
||||
if let ExprKind::MethodCall(_, arg, [], _) = expr.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& match_def_path(cx, method_def_id, &paths::STR_LEN)
|
||||
{
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the length of the `expr` if it's a constant string or char.
|
||||
|
@ -201,14 +204,13 @@ fn find_stripping<'tcx>(
|
|||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_ref_str(self.cx, ex);
|
||||
let unref = peel_ref(ex);
|
||||
if let ExprKind::Index(indexed, index, _) = &unref.kind;
|
||||
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index);
|
||||
if let ExprKind::Path(path) = &indexed.kind;
|
||||
if self.cx.qpath_res(path, ex.hir_id) == self.target;
|
||||
then {
|
||||
if is_ref_str(self.cx, ex)
|
||||
&& let unref = peel_ref(ex)
|
||||
&& let ExprKind::Index(indexed, index, _) = &unref.kind
|
||||
&& let Some(higher::Range { start, end, .. }) = higher::Range::hir(index)
|
||||
&& let ExprKind::Path(path) = &indexed.kind
|
||||
&& self.cx.qpath_res(path, ex.hir_id) == self.target
|
||||
{
|
||||
match (self.strip_kind, start, end) {
|
||||
(StripKind::Prefix, Some(start), None) => {
|
||||
if eq_pattern_length(self.cx, self.pattern, start) {
|
||||
|
@ -217,20 +219,23 @@ fn find_stripping<'tcx>(
|
|||
}
|
||||
},
|
||||
(StripKind::Suffix, None, Some(end)) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
|
||||
if let Some(left_arg) = len_arg(self.cx, left);
|
||||
if let ExprKind::Path(left_path) = &left_arg.kind;
|
||||
if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target;
|
||||
if eq_pattern_length(self.cx, self.pattern, right);
|
||||
then {
|
||||
if let ExprKind::Binary(
|
||||
Spanned {
|
||||
node: BinOpKind::Sub, ..
|
||||
},
|
||||
left,
|
||||
right,
|
||||
) = end.kind
|
||||
&& let Some(left_arg) = len_arg(self.cx, left)
|
||||
&& let ExprKind::Path(left_path) = &left_arg.kind
|
||||
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
|
||||
&& eq_pattern_length(self.cx, self.pattern, right)
|
||||
{
|
||||
self.results.push(ex.span);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{iter_input_pats, method_chain_args};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -164,17 +163,15 @@ fn unit_closure<'tcx>(
|
|||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
let body_expr = &body.value;
|
||||
if fn_decl.inputs.len() == 1;
|
||||
if is_unit_expression(cx, body_expr);
|
||||
if let Some(binding) = iter_input_pats(fn_decl, body).next();
|
||||
then {
|
||||
if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind
|
||||
&& let body = cx.tcx.hir().body(body)
|
||||
&& let body_expr = &body.value
|
||||
&& fn_decl.inputs.len() == 1
|
||||
&& is_unit_expression(cx, body_expr)
|
||||
&& let Some(binding) = iter_input_pats(fn_decl, body).next()
|
||||
{
|
||||
return Some((binding, body_expr));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{higher, is_res_lang_ctor};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -56,16 +55,15 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
|
|||
return;
|
||||
};
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation
|
||||
if ok_path.ident.as_str() == "ok";
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
||||
if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome);
|
||||
let ctxt = expr.span.ctxt();
|
||||
if let_expr.span.ctxt() == ctxt;
|
||||
if let_pat.span.ctxt() == ctxt;
|
||||
then {
|
||||
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
&& let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation
|
||||
&& ok_path.ident.as_str() == "ok"
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& let_expr.span.ctxt() == ctxt
|
||||
&& let_pat.span.ctxt() == ctxt
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
|
||||
let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
|
||||
|
@ -84,5 +82,4 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ use clippy_utils::visitors::is_local_used;
|
|||
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;
|
||||
use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
|
||||
|
@ -40,54 +39,55 @@ fn check_arm<'tcx>(
|
|||
outer_else_body: Option<&'tcx Expr<'tcx>>,
|
||||
) {
|
||||
let inner_expr = peel_blocks_with_stmt(outer_then_body);
|
||||
if_chain! {
|
||||
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
|
||||
if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
|
||||
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
|
||||
&& let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
|
||||
IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)),
|
||||
IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! {
|
||||
IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none())
|
||||
// if there are more than two arms, collapsing would be non-trivial
|
||||
if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none());
|
||||
// one of the arms must be "wild-like"
|
||||
if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a));
|
||||
then {
|
||||
&& let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a))
|
||||
{
|
||||
let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]);
|
||||
Some((scrutinee, then.pat, Some(els.body)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
if outer_pat.span.eq_ctxt(inner_scrutinee.span);
|
||||
}
|
||||
&& outer_pat.span.eq_ctxt(inner_scrutinee.span)
|
||||
// match expression must be a local binding
|
||||
// match <local> { .. }
|
||||
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
|
||||
if !pat_contains_or(inner_then_pat);
|
||||
&& let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
|
||||
&& !pat_contains_or(inner_then_pat)
|
||||
// the binding must come from the pattern of the containing match arm
|
||||
// ..<local>.. => match <local> { .. }
|
||||
if let (Some(binding_span), is_innermost_parent_pat_struct)
|
||||
= find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id);
|
||||
&& let (Some(binding_span), is_innermost_parent_pat_struct)
|
||||
= find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id)
|
||||
// the "else" branches must be equal
|
||||
if match (outer_else_body, inner_else_body) {
|
||||
&& match (outer_else_body, inner_else_body) {
|
||||
(None, None) => true,
|
||||
(None, Some(e)) | (Some(e), None) => is_unit_expr(e),
|
||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
|
||||
};
|
||||
}
|
||||
// the binding must not be used in the if guard
|
||||
if outer_guard.map_or(
|
||||
&& outer_guard.map_or(
|
||||
true,
|
||||
|(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
|
||||
);
|
||||
)
|
||||
// ...or anywhere in the inner expression
|
||||
if match inner {
|
||||
&& match inner {
|
||||
IfLetOrMatch::IfLet(_, _, body, els) => {
|
||||
!is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
|
||||
},
|
||||
IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
|
||||
};
|
||||
then {
|
||||
}
|
||||
{
|
||||
let msg = format!(
|
||||
"this `{}` can be collapsed into the outer `{}`",
|
||||
if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" },
|
||||
if matches!(inner, IfLetOrMatch::Match(..)) {
|
||||
"match"
|
||||
} else {
|
||||
"if let"
|
||||
},
|
||||
if outer_is_match { "match" } else { "if let" },
|
||||
);
|
||||
// collapsing patterns need an explicit field name in struct pattern matching
|
||||
|
@ -97,19 +97,15 @@ fn check_arm<'tcx>(
|
|||
} else {
|
||||
String::new()
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
COLLAPSIBLE_MATCH,
|
||||
inner_expr.span,
|
||||
&msg,
|
||||
|diag| {
|
||||
span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, &msg, |diag| {
|
||||
let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
|
||||
help_span.push_span_label(binding_span, "replace this binding");
|
||||
help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}"));
|
||||
diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
|
||||
},
|
||||
diag.span_help(
|
||||
help_span,
|
||||
"the outer pattern can be modified to include the inner pattern",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,19 +8,17 @@ use rustc_lint::LateContext;
|
|||
use super::INFALLIBLE_DESTRUCTURING_MATCH;
|
||||
|
||||
pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
|
||||
if_chain! {
|
||||
if !local.span.from_expansion();
|
||||
if let Some(expr) = local.init;
|
||||
if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
|
||||
if arms.len() == 1 && arms[0].guard.is_none();
|
||||
if let PatKind::TupleStruct(
|
||||
QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
|
||||
if args.len() == 1;
|
||||
if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind;
|
||||
let body = peel_blocks(arms[0].body);
|
||||
if path_to_local_id(body, arg);
|
||||
|
||||
then {
|
||||
if !local.span.from_expansion()
|
||||
&& let Some(expr) = local.init
|
||||
&& let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind
|
||||
&& arms.len() == 1
|
||||
&& arms[0].guard.is_none()
|
||||
&& let PatKind::TupleStruct(QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind
|
||||
&& args.len() == 1
|
||||
&& let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind
|
||||
&& let body = peel_blocks(arms[0].body)
|
||||
&& path_to_local_id(body, arg)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -40,6 +38,5 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
|
|||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -21,19 +21,19 @@ fn get_cond_expr<'tcx>(
|
|||
expr: &'tcx Expr<'_>,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<SomeExpr<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr);
|
||||
if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind;
|
||||
if let PatKind::Binding(_,target, ..) = pat.kind;
|
||||
if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr)
|
||||
|| is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None`
|
||||
then {
|
||||
if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr)
|
||||
&& let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind
|
||||
&& let PatKind::Binding(_, target, ..) = pat.kind
|
||||
&& (is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr)
|
||||
|| is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr))
|
||||
// check that one expr resolves to `Some(x)`, the other to `None`
|
||||
{
|
||||
return Some(SomeExpr {
|
||||
expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
|
||||
needs_unsafe_block: contains_unsafe_block(cx, expr),
|
||||
needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond
|
||||
})
|
||||
}
|
||||
needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the
|
||||
* cond */
|
||||
});
|
||||
};
|
||||
None
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ 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_res_lang_ctor, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::LangItem::{OptionNone, ResultErr};
|
||||
|
@ -16,65 +15,57 @@ use super::MANUAL_UNWRAP_OR;
|
|||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
|
||||
let ty = cx.typeck_results().expr_ty(scrutinee);
|
||||
if_chain! {
|
||||
if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
Some("Option")
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
|
||||
Some("Result")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(or_arm) = applicable_or_arm(cx, arms);
|
||||
if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
|
||||
then {
|
||||
let reindented_or_body =
|
||||
reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
} && let Some(or_arm) = applicable_or_arm(cx, arms)
|
||||
&& let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span)
|
||||
&& let Some(indent) = indent_of(cx, expr.span)
|
||||
&& constant_simple(cx, cx.typeck_results(), or_arm.body).is_some()
|
||||
{
|
||||
let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent));
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let suggestion = sugg::Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_par();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_UNWRAP_OR, expr.span,
|
||||
MANUAL_UNWRAP_OR,
|
||||
expr.span,
|
||||
&format!("this pattern reimplements `{ty_name}::unwrap_or`"),
|
||||
"replace with",
|
||||
format!(
|
||||
"{suggestion}.unwrap_or({reindented_or_body})",
|
||||
),
|
||||
format!("{suggestion}.unwrap_or({reindented_or_body})",),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
|
||||
match arm.pat.kind {
|
||||
if arms.len() == 2
|
||||
&& arms.iter().all(|arm| arm.guard.is_none())
|
||||
&& let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) =>
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) => {
|
||||
matches!(pat.kind, PatKind::Wild)
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr),
|
||||
&& 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 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();
|
||||
if !contains_return_break_continue_macro(or_arm.body);
|
||||
then {
|
||||
})
|
||||
&& let unwrap_arm = &arms[1 - idx]
|
||||
&& let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind
|
||||
&& let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, unwrap_arm.pat.hir_id)
|
||||
&& let Some(variant_id) = cx.tcx.opt_parent(ctor_id)
|
||||
&& (cx.tcx.lang_items().option_some_variant() == Some(variant_id)
|
||||
|| cx.tcx.lang_items().result_ok_variant() == Some(variant_id))
|
||||
&& let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind
|
||||
&& path_to_local_id(unwrap_arm.body, binding_hir_id)
|
||||
&& cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty()
|
||||
&& !contains_return_break_continue_macro(or_arm.body)
|
||||
{
|
||||
Some(or_arm)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,11 +127,10 @@ where
|
|||
|
||||
let closure_expr_snip = some_expr.to_snippet_with_context(cx, expr_ctxt, &mut app);
|
||||
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
|
||||
if_chain! {
|
||||
if !some_expr.needs_unsafe_block;
|
||||
if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
|
||||
if func.span.eq_ctxt(some_expr.expr.span);
|
||||
then {
|
||||
if !some_expr.needs_unsafe_block
|
||||
&& let Some(func) = can_pass_as_func(cx, id, some_expr.expr)
|
||||
&& func.span.eq_ctxt(some_expr.expr.span)
|
||||
{
|
||||
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
|
||||
} else {
|
||||
if path_to_local_id(some_expr.expr, id)
|
||||
|
@ -154,7 +153,6 @@ where
|
|||
format!("|{annotation}{some_binding}| {closure_expr_snip}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !is_wild_none && explicit_ref.is_none() {
|
||||
// TODO: handle explicit reference annotations.
|
||||
let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
|
||||
|
|
|
@ -26,18 +26,16 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
let output_ty = cx.typeck_results().expr_ty(expr);
|
||||
let input_ty = cx.typeck_results().expr_ty(ex);
|
||||
|
||||
let cast = if_chain! {
|
||||
if let ty::Adt(_, args) = input_ty.kind();
|
||||
let input_ty = args.type_at(0);
|
||||
if let ty::Adt(_, args) = output_ty.kind();
|
||||
let output_ty = args.type_at(0);
|
||||
if let ty::Ref(_, output_ty, _) = *output_ty.kind();
|
||||
if input_ty != output_ty;
|
||||
then {
|
||||
let cast = if let ty::Adt(_, args) = input_ty.kind()
|
||||
&& let input_ty = args.type_at(0)
|
||||
&& let ty::Adt(_, args) = output_ty.kind()
|
||||
&& let output_ty = args.type_at(0)
|
||||
&& let ty::Ref(_, output_ty, _) = *output_ty.kind()
|
||||
&& input_ty != output_ty
|
||||
{
|
||||
".map(|x| x as _)"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
@ -67,17 +65,16 @@ fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
|||
|
||||
// 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_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 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 {
|
||||
return Some(mutabl)
|
||||
}
|
||||
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionSome)
|
||||
&& let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., ident, _) = first_pat.kind
|
||||
&& let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, e), LangItem::OptionSome)
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind
|
||||
&& path2.segments.len() == 1
|
||||
&& ident.name == path2.segments[0].ident.name
|
||||
{
|
||||
return Some(mutabl);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -76,23 +76,19 @@ where
|
|||
),
|
||||
>,
|
||||
{
|
||||
if_chain! {
|
||||
if !span_contains_comment(cx.sess().source_map(), expr.span);
|
||||
if iter.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
|
||||
let iter_without_last = iter.clone();
|
||||
if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
|
||||
if let Some(b0) = find_bool_lit(&first_expr.kind);
|
||||
if let Some(b1) = find_bool_lit(&last_expr.kind);
|
||||
if b0 != b1;
|
||||
if first_guard.is_none() || iter.len() == 0;
|
||||
if first_attrs.is_empty();
|
||||
if iter
|
||||
.all(|arm| {
|
||||
find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
|
||||
});
|
||||
then {
|
||||
if !span_contains_comment(cx.sess().source_map(), expr.span)
|
||||
&& iter.len() >= 2
|
||||
&& cx.typeck_results().expr_ty(expr).is_bool()
|
||||
&& let Some((_, last_pat_opt, last_expr, _)) = iter.next_back()
|
||||
&& let iter_without_last = iter.clone()
|
||||
&& let Some((first_attrs, _, first_expr, first_guard)) = iter.next()
|
||||
&& let Some(b0) = find_bool_lit(&first_expr.kind)
|
||||
&& let Some(b1) = find_bool_lit(&last_expr.kind)
|
||||
&& b0 != b1
|
||||
&& (first_guard.is_none() || iter.len() == 0)
|
||||
&& first_attrs.is_empty()
|
||||
&& iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty())
|
||||
{
|
||||
if let Some(last_pat) = last_pat_opt {
|
||||
if !is_wild(last_pat) {
|
||||
return false;
|
||||
|
@ -120,7 +116,10 @@ where
|
|||
.join(" | ")
|
||||
};
|
||||
let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
|
||||
format!("{pat} if {}", snippet_with_applicability(cx, g.span, "..", &mut applicability))
|
||||
format!(
|
||||
"{pat} if {}",
|
||||
snippet_with_applicability(cx, g.span, "..", &mut applicability)
|
||||
)
|
||||
} else {
|
||||
pat
|
||||
};
|
||||
|
@ -136,7 +135,10 @@ where
|
|||
cx,
|
||||
MATCH_LIKE_MATCHES_MACRO,
|
||||
expr.span,
|
||||
&format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
|
||||
&format!(
|
||||
"{} expression looks like `matches!` macro",
|
||||
if is_if_let { "if let .. else" } else { "match" }
|
||||
),
|
||||
"try",
|
||||
format!(
|
||||
"{}matches!({}, {pat_and_guard})",
|
||||
|
@ -149,7 +151,6 @@ where
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract a `bool` or `{ bool }`
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -10,11 +9,9 @@ use rustc_span::sym;
|
|||
use super::MATCH_ON_VEC_ITEMS;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(idx_expr) = is_vec_indexing(cx, scrutinee);
|
||||
if let ExprKind::Index(vec, idx, _) = idx_expr.kind;
|
||||
|
||||
then {
|
||||
if let Some(idx_expr) = is_vec_indexing(cx, scrutinee)
|
||||
&& let ExprKind::Index(vec, idx, _) = idx_expr.kind
|
||||
{
|
||||
// FIXME: could be improved to suggest surrounding every pattern with Some(_),
|
||||
// but only when `or_patterns` are stabilized.
|
||||
span_lint_and_sugg(
|
||||
|
@ -23,27 +20,19 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
|
|||
scrutinee.span,
|
||||
"indexing into a vector may panic",
|
||||
"try",
|
||||
format!(
|
||||
"{}.get({})",
|
||||
snippet(cx, vec.span, ".."),
|
||||
snippet(cx, idx.span, "..")
|
||||
),
|
||||
Applicability::MaybeIncorrect
|
||||
format!("{}.get({})", snippet(cx, vec.span, ".."), snippet(cx, idx.span, "..")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Index(array, index, _) = expr.kind;
|
||||
if is_vector(cx, array);
|
||||
if !is_full_range(cx, index);
|
||||
|
||||
then {
|
||||
if let ExprKind::Index(array, index, _) = expr.kind
|
||||
&& is_vector(cx, array)
|
||||
&& !is_full_range(cx, index)
|
||||
{
|
||||
return Some(expr);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
@ -66,26 +66,24 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
|||
|
||||
let mut local_map: HirIdMap<HirId> = HirIdMap::default();
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
if_chain! {
|
||||
if let Some(a_id) = path_to_local(a);
|
||||
if let Some(b_id) = path_to_local(b);
|
||||
let entry = match local_map.entry(a_id) {
|
||||
if let Some(a_id) = path_to_local(a)
|
||||
&& let Some(b_id) = path_to_local(b)
|
||||
&& let entry = match local_map.entry(a_id) {
|
||||
HirIdMapEntry::Vacant(entry) => entry,
|
||||
// check if using the same bindings as before
|
||||
HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id,
|
||||
};
|
||||
}
|
||||
// the names technically don't have to match; this makes the lint more conservative
|
||||
if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
|
||||
if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
|
||||
if pat_contains_local(lhs.pat, a_id);
|
||||
if pat_contains_local(rhs.pat, b_id);
|
||||
then {
|
||||
&& cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id)
|
||||
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
|
||||
&& pat_contains_local(lhs.pat, a_id)
|
||||
&& pat_contains_local(rhs.pat, b_id)
|
||||
{
|
||||
entry.insert(b_id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
// Arms with a guard are ignored, those can’t always be merged together
|
||||
// If both arms overlap with an arm in between then these can't be merged either.
|
||||
|
|
|
@ -20,14 +20,10 @@ enum CaseMethod {
|
|||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind();
|
||||
if let ty::Str = ty.kind();
|
||||
then {
|
||||
let mut visitor = MatchExprVisitor {
|
||||
cx,
|
||||
case_method: None,
|
||||
};
|
||||
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind()
|
||||
&& let ty::Str = ty.kind()
|
||||
{
|
||||
let mut visitor = MatchExprVisitor { cx, case_method: None };
|
||||
|
||||
visitor.visit_expr(scrutinee);
|
||||
|
||||
|
@ -37,7 +33,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MatchExprVisitor<'a, 'tcx> {
|
||||
|
@ -88,19 +83,17 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(
|
|||
};
|
||||
|
||||
for arm in arms {
|
||||
if_chain! {
|
||||
if let PatKind::Lit(Expr {
|
||||
kind: ExprKind::Lit(lit),
|
||||
..
|
||||
}) = arm.pat.kind;
|
||||
if let LitKind::Str(symbol, _) = lit.node;
|
||||
let input = symbol.as_str();
|
||||
if !case_check(input);
|
||||
then {
|
||||
}) = arm.pat.kind
|
||||
&& let LitKind::Str(symbol, _) = lit.node
|
||||
&& let input = symbol.as_str()
|
||||
&& !case_check(input)
|
||||
{
|
||||
return Some((lit.span, symbol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
|
|
@ -34,13 +34,13 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
|||
}
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if matching_wild;
|
||||
if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
|
||||
if is_panic(cx, macro_call.def_id);
|
||||
then {
|
||||
if matching_wild
|
||||
&& let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span)
|
||||
&& is_panic(cx, macro_call.def_id)
|
||||
{
|
||||
// `Err(_)` or `Err(_e)` arm with `panic!` found
|
||||
span_lint_and_note(cx,
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
MATCH_WILD_ERR_ARM,
|
||||
arm.pat.span,
|
||||
&format!("`Err({ident_bind_name})` matches all errors"),
|
||||
|
@ -52,5 +52,4 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ 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, for_each_expr};
|
||||
use clippy_utils::{higher, is_expn_of, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
|
@ -35,16 +34,14 @@ pub(super) fn check_if_let<'tcx>(
|
|||
|
||||
// Extract the generic arguments out of a type
|
||||
fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(_, subs) = ty.kind();
|
||||
if let Some(sub) = subs.get(index);
|
||||
if let GenericArgKind::Type(sub_ty) = sub.unpack();
|
||||
then {
|
||||
if let ty::Adt(_, subs) = ty.kind()
|
||||
&& let Some(sub) = subs.get(index)
|
||||
&& let GenericArgKind::Type(sub_ty) = sub.unpack()
|
||||
{
|
||||
Some(sub_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_method_and_type<'tcx>(
|
||||
|
@ -142,15 +139,13 @@ fn find_sugg_for_if_let<'tcx>(
|
|||
let needs_drop = needs_ordered_drop(cx, check_ty) || any_temporaries_need_ordered_drop(cx, let_expr);
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
if let ExprKind::MethodCall(method_path, ..) = let_expr.kind;
|
||||
if method_path.ident.name == sym::next;
|
||||
if is_trait_method(cx, let_expr, sym::Iterator);
|
||||
then {
|
||||
if keyword == "while"
|
||||
&& let ExprKind::MethodCall(method_path, ..) = let_expr.kind
|
||||
&& method_path.ident.name == sym::next
|
||||
&& is_trait_method(cx, let_expr, sym::Iterator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let result_expr = match &let_expr.kind {
|
||||
ExprKind::AddrOf(_, _, borrowed) => borrowed,
|
||||
|
|
|
@ -6,17 +6,15 @@ use rustc_middle::ty;
|
|||
use super::REST_PAT_IN_FULLY_BOUND_STRUCTS;
|
||||
|
||||
pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||
if_chain! {
|
||||
if !pat.span.from_expansion();
|
||||
if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
let ty = cx.tcx.type_of(def_id).instantiate_identity();
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
if def.is_struct() || def.is_union();
|
||||
if fields.len() == def.non_enum_variant().fields.len();
|
||||
if !def.non_enum_variant().is_field_list_non_exhaustive();
|
||||
|
||||
then {
|
||||
if !pat.span.from_expansion()
|
||||
&& let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
&& let ty = cx.tcx.type_of(def_id).instantiate_identity()
|
||||
&& let ty::Adt(def, _) = ty.kind()
|
||||
&& (def.is_struct() || def.is_union())
|
||||
&& fields.len() == def.non_enum_variant().fields.len()
|
||||
&& !def.non_enum_variant().is_field_list_non_exhaustive()
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
REST_PAT_IN_FULLY_BOUND_STRUCTS,
|
||||
|
@ -26,5 +24,4 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
|
|||
"consider removing `..` from this binding",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,19 +89,22 @@ fn report_single_pattern(
|
|||
});
|
||||
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||
let (msg, sugg) = if_chain! {
|
||||
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
||||
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
||||
if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||
if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
|
||||
if ty.is_integral() || ty.is_char() || ty.is_str()
|
||||
|| (implements_trait(cx, ty, spe_trait_id, &[])
|
||||
&& implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
|
||||
then {
|
||||
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
|
||||
&& let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex))
|
||||
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
|
||||
&& let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
|
||||
&& (ty.is_integral()
|
||||
|| ty.is_char()
|
||||
|| ty.is_str()
|
||||
|| (implements_trait(cx, ty, spe_trait_id, &[]) && implements_trait(cx, ty, pe_trait_id, &[ty.into()])))
|
||||
{
|
||||
// scrutinee derives PartialEq and the pattern is a constant.
|
||||
let pat_ref_count = match pat.kind {
|
||||
// string literals are already a reference.
|
||||
PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
|
||||
PatKind::Lit(Expr {
|
||||
kind: ExprKind::Lit(lit),
|
||||
..
|
||||
}) if lit.node.is_str() => pat_ref_count + 1,
|
||||
_ => pat_ref_count,
|
||||
};
|
||||
// References are only implicitly added to the pattern, so no overflow here.
|
||||
|
@ -132,7 +135,6 @@ fn report_single_pattern(
|
|||
expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
|
||||
);
|
||||
(msg, sugg)
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
|
||||
|
|
|
@ -2,7 +2,6 @@ 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_res_lang_ctor, path_res};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||
|
@ -22,14 +21,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
// #[allow(unreachable_code)]
|
||||
// val,
|
||||
// };
|
||||
if_chain! {
|
||||
if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind;
|
||||
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 is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr);
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
then {
|
||||
if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind
|
||||
&& let ExprKind::Path(ref match_fun_path) = match_fun.kind
|
||||
&& matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..))
|
||||
&& let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr)
|
||||
&& let Some(return_ty) = find_return_type(cx, &expr.kind)
|
||||
{
|
||||
let prefix;
|
||||
let suffix;
|
||||
let err_ty;
|
||||
|
@ -75,7 +73,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds function return type by examining return expressions in match arms.
|
||||
|
@ -92,51 +89,42 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O
|
|||
|
||||
/// Extracts the error type from Result<T, E>.
|
||||
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(_, subst) = ty.kind();
|
||||
if is_type_diagnostic_item(cx, ty, sym::Result);
|
||||
then {
|
||||
if let ty::Adt(_, subst) = ty.kind()
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Result)
|
||||
{
|
||||
Some(subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Result<T, E>>.
|
||||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did());
|
||||
then {
|
||||
if let ty::Adt(def, subst) = ty.kind()
|
||||
&& cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
|
||||
&& let ready_ty = subst.type_at(0)
|
||||
&& let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, ready_def.did())
|
||||
{
|
||||
Some(ready_subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts the error type from Poll<Option<Result<T, E>>>.
|
||||
fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if_chain! {
|
||||
if let ty::Adt(def, subst) = ty.kind();
|
||||
if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did());
|
||||
let ready_ty = subst.type_at(0);
|
||||
|
||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did());
|
||||
let some_ty = ready_subst.type_at(0);
|
||||
|
||||
if let ty::Adt(some_def, some_subst) = some_ty.kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Result, some_def.did());
|
||||
then {
|
||||
if let ty::Adt(def, subst) = ty.kind()
|
||||
&& cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
|
||||
&& let ready_ty = subst.type_at(0)
|
||||
&& let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Option, ready_def.did())
|
||||
&& let some_ty = ready_subst.type_at(0)
|
||||
&& let ty::Adt(some_def, some_subst) = some_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, some_def.did())
|
||||
{
|
||||
Some(some_subst.type_at(1))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
|
|||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_non_aggregate_primitive_type;
|
||||
use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res, peel_ref_operators};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -125,11 +124,10 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, dest: &Expr<'_>, expr_sp
|
|||
}
|
||||
|
||||
fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id)
|
||||
// check if replacement is mem::MaybeUninit::uninit().assume_init()
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
|
||||
then {
|
||||
&& cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -145,13 +143,11 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(repl_func, []) = src.kind;
|
||||
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
|
||||
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
|
||||
then {
|
||||
if let ExprKind::Call(repl_func, []) = src.kind
|
||||
&& let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
|
||||
&& let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
|
@ -166,8 +162,9 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
),
|
||||
applicability,
|
||||
);
|
||||
} else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
|
||||
!cx.typeck_results().expr_ty(src).is_primitive() {
|
||||
} else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id)
|
||||
&& !cx.typeck_results().expr_ty(src).is_primitive()
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MEM_REPLACE_WITH_UNINIT,
|
||||
|
@ -178,7 +175,6 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
|
@ -222,13 +218,12 @@ impl MemReplace {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [dest, src]) = expr.kind
|
||||
// Check that `expr` is a call to `mem::replace()`
|
||||
if let ExprKind::Call(func, [dest, src]) = expr.kind;
|
||||
if let ExprKind::Path(ref func_qpath) = func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
|
||||
then {
|
||||
&& let ExprKind::Path(ref func_qpath) = func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
|
||||
&& cx.tcx.is_diagnostic_item(sym::mem_replace, def_id)
|
||||
{
|
||||
// Check that second argument is `Option::None`
|
||||
if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
|
||||
check_replace_option_with_none(cx, dest, expr.span);
|
||||
|
@ -238,6 +233,5 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
|||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and
|
|||
use clippy_utils::peel_blocks;
|
||||
use clippy_utils::source::{snippet, snippet_with_context};
|
||||
use clippy_utils::visitors::find_all_ret_expressions;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
|
@ -70,57 +69,50 @@ pub(crate) trait BindInsteadOfMap {
|
|||
closure_expr: &hir::Expr<'_>,
|
||||
closure_args_span: Span,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind;
|
||||
if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind;
|
||||
if Self::is_variant(cx, path.res);
|
||||
if !contains_return(inner_expr);
|
||||
if let Some(msg) = Self::lint_msg(cx);
|
||||
then {
|
||||
if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind
|
||||
&& let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind
|
||||
&& Self::is_variant(cx, path.res)
|
||||
&& !contains_return(inner_expr)
|
||||
&& let Some(msg) = Self::lint_msg(cx)
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0;
|
||||
|
||||
let closure_args_snip = snippet(cx, closure_args_span, "..");
|
||||
let option_snip = snippet(cx, recv.span, "..");
|
||||
let note = format!("{option_snip}.{}({closure_args_snip} {some_inner_snip})", Self::GOOD_METHOD_NAME);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BIND_INSTEAD_OF_MAP,
|
||||
expr.span,
|
||||
&msg,
|
||||
"try",
|
||||
note,
|
||||
app,
|
||||
let note = format!(
|
||||
"{option_snip}.{}({closure_args_snip} {some_inner_snip})",
|
||||
Self::GOOD_METHOD_NAME
|
||||
);
|
||||
span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, "try", note, app);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool {
|
||||
let mut suggs = Vec::new();
|
||||
let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
|
||||
if_chain! {
|
||||
if !ret_expr.span.from_expansion();
|
||||
if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind;
|
||||
if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind;
|
||||
if Self::is_variant(cx, path.res);
|
||||
if !contains_return(arg);
|
||||
then {
|
||||
if !ret_expr.span.from_expansion()
|
||||
&& let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind
|
||||
&& let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind
|
||||
&& Self::is_variant(cx, path.res)
|
||||
&& !contains_return(arg)
|
||||
{
|
||||
suggs.push((ret_expr.span, arg.span.source_callsite()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
let (span, msg) = if_chain! {
|
||||
if can_sugg;
|
||||
if let hir::ExprKind::MethodCall(segment, ..) = expr.kind;
|
||||
if let Some(msg) = Self::lint_msg(cx);
|
||||
then { (segment.ident.span, msg) } else { return false; }
|
||||
let (span, msg) = if can_sugg
|
||||
&& let hir::ExprKind::MethodCall(segment, ..) = expr.kind
|
||||
&& let Some(msg) = Self::lint_msg(cx)
|
||||
{
|
||||
(segment.ident.span, msg)
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
|
@ -139,11 +131,12 @@ pub(crate) trait BindInsteadOfMap {
|
|||
|
||||
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
|
||||
fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def();
|
||||
if let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM);
|
||||
if adt.did() == cx.tcx.parent(vid);
|
||||
then {} else { return false; }
|
||||
if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)
|
||||
&& adt.did() == cx.tcx.parent(vid)
|
||||
{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
match arg.kind {
|
||||
|
|
|
@ -3,7 +3,6 @@ use clippy_utils::source::snippet_with_applicability;
|
|||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -18,32 +17,28 @@ pub(super) fn check<'tcx>(
|
|||
filter_recv: &'tcx Expr<'_>,
|
||||
filter_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if is_type_diagnostic_item(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
sym::SliceIter);
|
||||
let operand_is_arg = |expr| {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind
|
||||
&& let body = cx.tcx.hir().body(body)
|
||||
&& let [param] = body.params
|
||||
&& let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind
|
||||
&& let ExprKind::Binary(ref op, l, r) = body.value.kind
|
||||
&& op.node == BinOpKind::Eq
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv).peel_refs(), sym::SliceIter)
|
||||
&& let operand_is_arg = (|expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
})
|
||||
&& let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, receiver, [], _) =
|
||||
filter_recv.kind {
|
||||
}
|
||||
&& ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind()
|
||||
&& !is_local_used(cx, needle, arg_id)
|
||||
{
|
||||
let haystack = if let ExprKind::MethodCall(path, receiver, [], _) = filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if p == sym::iter || p == sym::iter_mut {
|
||||
receiver
|
||||
|
@ -60,11 +55,12 @@ pub(super) fn check<'tcx>(
|
|||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
format!(
|
||||
"bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -14,13 +13,12 @@ pub(super) fn check<'tcx>(
|
|||
count_recv: &'tcx hir::Expr<'_>,
|
||||
bytes_recv: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
|
||||
if cx.tcx.type_of(impl_id).instantiate_identity().is_str();
|
||||
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
|
||||
if ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String);
|
||||
then {
|
||||
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(bytes_id)
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_str()
|
||||
&& let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs()
|
||||
&& (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String))
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -28,9 +26,11 @@ pub(super) fn check<'tcx>(
|
|||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
|
||||
applicability
|
||||
format!(
|
||||
"{}.len()",
|
||||
snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
|
@ -27,19 +26,21 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
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 cx.tcx.type_of(impl_id).instantiate_identity().is_str();
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
let ext_str = ext_literal.as_str();
|
||||
if ext_str.starts_with('.');
|
||||
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String);
|
||||
then {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& cx.tcx.type_of(impl_id).instantiate_identity().is_str()
|
||||
&& let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Str(ext_literal, ..),
|
||||
..
|
||||
}) = arg.kind
|
||||
&& (2..=6).contains(&ext_literal.as_str().len())
|
||||
&& let ext_str = ext_literal.as_str()
|
||||
&& ext_str.starts_with('.')
|
||||
&& (ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()))
|
||||
&& let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs()
|
||||
&& (recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
|
@ -48,7 +49,6 @@ pub(super) fn check<'tcx>(
|
|||
|diag| {
|
||||
diag.help("consider using a case-insensitive comparison instead");
|
||||
if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
|
||||
|
||||
if !cx.typeck_results().expr_ty(recv).is_ref() {
|
||||
recv_source = format!("&{recv_source}");
|
||||
}
|
||||
|
@ -58,9 +58,12 @@ pub(super) fn check<'tcx>(
|
|||
"std::path::Path::new({})
|
||||
.extension()
|
||||
.map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))",
|
||||
recv_source, ext_str.strip_prefix('.').unwrap()).into(),
|
||||
recv_source,
|
||||
ext_str.strip_prefix('.').unwrap()
|
||||
)
|
||||
.into(),
|
||||
true,
|
||||
Some(indent_of(cx, call_span).unwrap_or(0) + 4)
|
||||
Some(indent_of(cx, call_span).unwrap_or(0) + 4),
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
|
@ -70,8 +73,7 @@ pub(super) fn check<'tcx>(
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{method_chain_args, path_def_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, Lint};
|
||||
|
@ -15,12 +14,11 @@ pub(super) fn check(
|
|||
lint: &'static Lint,
|
||||
suggest: &str,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods);
|
||||
if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
|
||||
if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id));
|
||||
if Some(id) == cx.tcx.lang_items().option_some_variant();
|
||||
then {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods)
|
||||
&& let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind
|
||||
&& let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id))
|
||||
&& Some(id) == cx.tcx.lang_items().option_some_variant()
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs();
|
||||
|
||||
|
@ -34,16 +32,17 @@ pub(super) fn check(
|
|||
info.expr.span,
|
||||
&format!("you should use the `{suggest}` method"),
|
||||
"like this",
|
||||
format!("{}{}.{suggest}({})",
|
||||
format!(
|
||||
"{}{}.{suggest}({})",
|
||||
if info.eq { "" } else { "!" },
|
||||
snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
|
||||
snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::method_chain_args;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -15,11 +14,10 @@ pub(super) fn check(
|
|||
lint: &'static Lint,
|
||||
suggest: &str,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods);
|
||||
if let hir::ExprKind::Lit(lit) = info.other.kind;
|
||||
if let ast::LitKind::Char(c) = lit.node;
|
||||
then {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods)
|
||||
&& let hir::ExprKind::Lit(lit) = info.other.kind
|
||||
&& let ast::LitKind::Char(c) = lit.node
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -27,10 +25,12 @@ pub(super) fn check(
|
|||
info.expr.span,
|
||||
&format!("you should use the `{suggest}` method"),
|
||||
"like this",
|
||||
format!("{}{}.{suggest}('{}')",
|
||||
format!(
|
||||
"{}{}.{suggest}('{}')",
|
||||
if info.eq { "" } else { "!" },
|
||||
snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
|
||||
c.escape_default()),
|
||||
c.escape_default()
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
|
||||
|
@ -38,5 +38,4 @@ pub(super) fn check(
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,20 +16,18 @@ pub(super) fn check(
|
|||
err_span: Span,
|
||||
msrv: &Msrv,
|
||||
) {
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
|
||||
// Test the version to make sure the lint can be showed (expect_err has been
|
||||
// introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
|
||||
if msrv.meets(msrvs::EXPECT_ERR);
|
||||
&& msrv.meets(msrvs::EXPECT_ERR)
|
||||
|
||||
// Grabs the `Result<T, E>` type
|
||||
let result_type = cx.typeck_results().expr_ty(recv);
|
||||
&& let result_type = cx.typeck_results().expr_ty(recv)
|
||||
// Tests if the T type in a `Result<T, E>` is not None
|
||||
if let Some(data_type) = get_data_type(cx, result_type);
|
||||
&& let Some(data_type) = get_data_type(cx, result_type)
|
||||
// Tests if the T type in a `Result<T, E>` implements debug
|
||||
if has_debug_impl(cx, data_type);
|
||||
|
||||
then {
|
||||
&& has_debug_impl(cx, data_type)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ERR_EXPECT,
|
||||
|
@ -37,9 +35,8 @@ pub(super) fn check(
|
|||
"called `.err().expect()` on a `Result` value",
|
||||
"try",
|
||||
"expect_err".to_string(),
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem};
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -11,20 +10,19 @@ use super::EXTEND_WITH_DRAIN;
|
|||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, ty, sym::Vec);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
//check source object
|
||||
if let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind;
|
||||
if src_method.ident.as_str() == "drain";
|
||||
let src_ty = cx.typeck_results().expr_ty(drain_vec);
|
||||
&& let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind
|
||||
&& src_method.ident.as_str() == "drain"
|
||||
&& let src_ty = cx.typeck_results().expr_ty(drain_vec)
|
||||
//check if actual src type is mutable for code suggestion
|
||||
let immutable = src_ty.is_mutable_ptr();
|
||||
let src_ty = src_ty.peel_refs();
|
||||
if is_type_diagnostic_item(cx, src_ty, sym::Vec);
|
||||
&& let immutable = src_ty.is_mutable_ptr()
|
||||
&& let src_ty = src_ty.peel_refs()
|
||||
&& is_type_diagnostic_item(cx, src_ty, sym::Vec)
|
||||
//check drain range
|
||||
if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs();
|
||||
if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull);
|
||||
then {
|
||||
&& let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs()
|
||||
&& is_type_lang_item(cx, src_ty_range, LangItem::RangeFull)
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -41,5 +39,4 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg:
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue