mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-22 04:34:51 +00:00
Auto merge of #7010 - camsteffen:if-chain-lint, r=llogiq
Internal `if_chain!` lints changelog: none We use `if_chain!` a lot. So this enforces some style rules around it, internal only. Lints when... * Nested `if`/`if_chain!` can be collapsed * An `if_chain!` starts with `let` or ends with `let ..; then {..}` * An `if_chain!` has only one `if` * An `if_chain!` contains `if .. && ..;` that spans multiple lines
This commit is contained in:
commit
8cf7d9b037
@ -371,8 +371,8 @@ fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<SymbolStr> {
|
|||||||
if meta_item.path.segments.len() > 1;
|
if meta_item.path.segments.len() > 1;
|
||||||
if let tool_name = meta_item.path.segments[0].ident;
|
if let tool_name = meta_item.path.segments[0].ident;
|
||||||
if tool_name.name == sym::clippy;
|
if tool_name.name == sym::clippy;
|
||||||
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
|
|
||||||
then {
|
then {
|
||||||
|
let lint_name = meta_item.path.segments.last().unwrap().ident.name;
|
||||||
return Some(lint_name.as_str());
|
return Some(lint_name.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,52 +45,48 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
|
|||||||
if filter.ident.name == sym!(filter);
|
if filter.ident.name == sym!(filter);
|
||||||
if filter_args.len() == 2;
|
if filter_args.len() == 2;
|
||||||
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
|
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
|
||||||
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
if body.params.len() == 1;
|
||||||
|
if let Some(argname) = get_pat_name(&body.params[0].pat);
|
||||||
|
if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
|
||||||
|
if op.node == BinOpKind::Eq;
|
||||||
|
if match_type(cx,
|
||||||
|
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
|
||||||
|
&paths::SLICE_ITER);
|
||||||
then {
|
then {
|
||||||
let body = cx.tcx.hir().body(body_id);
|
let needle = match get_path_name(l) {
|
||||||
if_chain! {
|
Some(name) if check_arg(name, argname, r) => r,
|
||||||
if body.params.len() == 1;
|
_ => match get_path_name(r) {
|
||||||
if let Some(argname) = get_pat_name(&body.params[0].pat);
|
Some(name) if check_arg(name, argname, l) => l,
|
||||||
if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind;
|
_ => { return; }
|
||||||
if op.node == BinOpKind::Eq;
|
|
||||||
if match_type(cx,
|
|
||||||
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
|
|
||||||
&paths::SLICE_ITER);
|
|
||||||
then {
|
|
||||||
let needle = match get_path_name(l) {
|
|
||||||
Some(name) if check_arg(name, argname, r) => r,
|
|
||||||
_ => match get_path_name(r) {
|
|
||||||
Some(name) if check_arg(name, argname, l) => l,
|
|
||||||
_ => { return; }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
|
|
||||||
filter_args[0].kind {
|
|
||||||
let p = path.ident.name;
|
|
||||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
|
||||||
&args[0]
|
|
||||||
} else {
|
|
||||||
&filter_args[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
&filter_args[0]
|
|
||||||
};
|
|
||||||
let mut applicability = Applicability::MaybeIncorrect;
|
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
NAIVE_BYTECOUNT,
|
|
||||||
expr.span,
|
|
||||||
"you appear to be counting bytes the naive way",
|
|
||||||
"consider using the bytecount crate",
|
|
||||||
format!("bytecount::count({}, {})",
|
|
||||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
|
||||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) =
|
||||||
|
filter_args[0].kind {
|
||||||
|
let p = path.ident.name;
|
||||||
|
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||||
|
&args[0]
|
||||||
|
} else {
|
||||||
|
&filter_args[0]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
&filter_args[0]
|
||||||
|
};
|
||||||
|
let mut applicability = Applicability::MaybeIncorrect;
|
||||||
|
span_lint_and_sugg(
|
||||||
|
cx,
|
||||||
|
NAIVE_BYTECOUNT,
|
||||||
|
expr.span,
|
||||||
|
"you appear to be counting bytes the naive way",
|
||||||
|
"consider using the bytecount crate",
|
||||||
|
format!("bytecount::count({}, {})",
|
||||||
|
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||||
|
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||||
|
applicability,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -321,9 +321,8 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
|
|||||||
if path.ident.name.as_str() == function;
|
if path.ident.name.as_str() == function;
|
||||||
if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind;
|
if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind;
|
||||||
if let [int] = &*tp.segments;
|
if let [int] = &*tp.segments;
|
||||||
let name = &int.ident.name.as_str();
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let name = &int.ident.name.as_str();
|
||||||
candidates.iter().find(|c| name == *c).cloned()
|
candidates.iter().find(|c| name == *c).cloned()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -336,9 +335,8 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let QPath::Resolved(_, ref path) = *path;
|
if let QPath::Resolved(_, ref path) = *path;
|
||||||
if let [ty] = &*path.segments;
|
if let [ty] = &*path.segments;
|
||||||
let name = &ty.ident.name.as_str();
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let name = &ty.ident.name.as_str();
|
||||||
INTS.iter().find(|c| name == *c).cloned()
|
INTS.iter().find(|c| name == *c).cloned()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
|
fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
|
||||||
|
let expr = strip_singleton_blocks(arm.body);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let expr = strip_singleton_blocks(arm.body);
|
|
||||||
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
|
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
|
||||||
// the outer arm pattern and the inner match
|
// the outer arm pattern and the inner match
|
||||||
if expr_in.span.ctxt() == arm.pat.span.ctxt();
|
if expr_in.span.ctxt() == arm.pat.span.ctxt();
|
||||||
|
@ -84,22 +84,21 @@ impl LateLintPass<'_> for Default {
|
|||||||
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
|
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
|
||||||
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
|
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
|
||||||
if let QPath::Resolved(None, _path) = qpath;
|
if let QPath::Resolved(None, _path) = qpath;
|
||||||
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
|
if let ty::Adt(def, ..) = expr_ty.kind();
|
||||||
then {
|
then {
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
// TODO: Work out a way to put "whatever the imported way of referencing
|
||||||
if let ty::Adt(def, ..) = expr_ty.kind() {
|
// this type in this file" rather than a fully-qualified type.
|
||||||
// TODO: Work out a way to put "whatever the imported way of referencing
|
let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
|
||||||
// this type in this file" rather than a fully-qualified type.
|
span_lint_and_sugg(
|
||||||
let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
|
cx,
|
||||||
span_lint_and_sugg(
|
DEFAULT_TRAIT_ACCESS,
|
||||||
cx,
|
expr.span,
|
||||||
DEFAULT_TRAIT_ACCESS,
|
&format!("calling `{}` is more clear than this expression", replacement),
|
||||||
expr.span,
|
"try",
|
||||||
&format!("calling `{}` is more clear than this expression", replacement),
|
replacement,
|
||||||
"try",
|
Applicability::Unspecified, // First resolve the TODO above
|
||||||
replacement,
|
);
|
||||||
Applicability::Unspecified, // First resolve the TODO above
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,14 +201,14 @@ impl LateLintPass<'_> for Default {
|
|||||||
let binding_type = if_chain! {
|
let binding_type = if_chain! {
|
||||||
if let ty::Adt(adt_def, substs) = binding_type.kind();
|
if let ty::Adt(adt_def, substs) = binding_type.kind();
|
||||||
if !substs.is_empty();
|
if !substs.is_empty();
|
||||||
let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
|
|
||||||
let generic_args = substs.iter().collect::<Vec<_>>();
|
|
||||||
let tys_str = generic_args
|
|
||||||
.iter()
|
|
||||||
.map(ToString::to_string)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
then {
|
then {
|
||||||
|
let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
|
||||||
|
let generic_args = substs.iter().collect::<Vec<_>>();
|
||||||
|
let tys_str = generic_args
|
||||||
|
.iter()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
format!("{}::<{}>", adt_def_ty_name, &tys_str)
|
format!("{}::<{}>", adt_def_ty_name, &tys_str)
|
||||||
} else {
|
} else {
|
||||||
binding_type.to_string()
|
binding_type.to_string()
|
||||||
|
@ -130,8 +130,8 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ExprKind::Struct(_, fields, base) => {
|
ExprKind::Struct(_, fields, base) => {
|
||||||
|
let ty = self.cx.typeck_results().expr_ty(expr);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let ty = self.cx.typeck_results().expr_ty(expr);
|
|
||||||
if let Some(adt_def) = ty.ty_adt_def();
|
if let Some(adt_def) = ty.ty_adt_def();
|
||||||
if adt_def.is_struct();
|
if adt_def.is_struct();
|
||||||
if let Some(variant) = adt_def.variants.iter().next();
|
if let Some(variant) = adt_def.variants.iter().next();
|
||||||
|
@ -32,16 +32,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
|
|||||||
if let ExprKind::Path(ref path) = path_expr.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 let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id();
|
||||||
if match_def_path(cx, def_id, &paths::EXIT);
|
if match_def_path(cx, def_id, &paths::EXIT);
|
||||||
|
let parent = cx.tcx.hir().get_parent_item(e.hir_id);
|
||||||
|
if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent);
|
||||||
|
// If the next item up is a function we check if it is an entry point
|
||||||
|
// and only then emit a linter warning
|
||||||
|
let def_id = cx.tcx.hir().local_def_id(parent);
|
||||||
|
if !is_entrypoint_fn(cx, def_id.to_def_id());
|
||||||
then {
|
then {
|
||||||
let parent = cx.tcx.hir().get_parent_item(e.hir_id);
|
span_lint(cx, EXIT, e.span, "usage of `process::exit`");
|
||||||
if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) {
|
|
||||||
// If the next item up is a function we check if it is an entry point
|
|
||||||
// and only then emit a linter warning
|
|
||||||
let def_id = cx.tcx.hir().local_def_id(parent);
|
|
||||||
if !is_entrypoint_fn(cx, def_id.to_def_id()) {
|
|
||||||
span_lint(cx, EXIT, e.span, "usage of `process::exit`");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
|
|||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||||
|
let ty = cx.typeck_results().expr_ty(expr);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let ty = cx.typeck_results().expr_ty(expr);
|
|
||||||
if let ty::Float(fty) = *ty.kind();
|
if let ty::Float(fty) = *ty.kind();
|
||||||
if let hir::ExprKind::Lit(ref lit) = expr.kind;
|
if let hir::ExprKind::Lit(ref lit) = expr.kind;
|
||||||
if let LitKind::Float(sym, lit_float_ty) = lit.node;
|
if let LitKind::Float(sym, lit_float_ty) = lit.node;
|
||||||
|
@ -217,9 +217,8 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||||||
if let Some(else_snippet) = snippet_opt(cx, else_span);
|
if let Some(else_snippet) = snippet_opt(cx, else_span);
|
||||||
if let Some(else_pos) = else_snippet.find("else");
|
if let Some(else_pos) = else_snippet.find("else");
|
||||||
if else_snippet[else_pos..].contains('\n');
|
if else_snippet[else_pos..].contains('\n');
|
||||||
let else_desc = if is_if(else_) { "if" } else { "{..}" };
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let else_desc = if is_if(else_) { "if" } else { "{..}" };
|
||||||
span_lint_and_note(
|
span_lint_and_note(
|
||||||
cx,
|
cx,
|
||||||
SUSPICIOUS_ELSE_FORMATTING,
|
SUSPICIOUS_ELSE_FORMATTING,
|
||||||
|
@ -94,13 +94,10 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
|
|||||||
type Map = Map<'tcx>;
|
type Map = Map<'tcx>;
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||||
if_chain! {
|
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
|
self.found_mutex = Some(mutex);
|
||||||
then {
|
self.mutex_lock_called = true;
|
||||||
self.found_mutex = Some(mutex);
|
return;
|
||||||
self.mutex_lock_called = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
@ -121,13 +118,10 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
|||||||
type Map = Map<'tcx>;
|
type Map = Map<'tcx>;
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
if_chain! {
|
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr);
|
self.found_mutex = Some(mutex);
|
||||||
then {
|
self.mutex_lock_called = true;
|
||||||
self.found_mutex = Some(mutex);
|
return;
|
||||||
self.mutex_lock_called = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
visit::walk_expr(self, expr);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
@ -550,6 +550,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
#[cfg(feature = "internal-lints")]
|
#[cfg(feature = "internal-lints")]
|
||||||
&utils::internal_lints::DEFAULT_LINT,
|
&utils::internal_lints::DEFAULT_LINT,
|
||||||
#[cfg(feature = "internal-lints")]
|
#[cfg(feature = "internal-lints")]
|
||||||
|
&utils::internal_lints::IF_CHAIN_STYLE,
|
||||||
|
#[cfg(feature = "internal-lints")]
|
||||||
&utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
&utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
||||||
#[cfg(feature = "internal-lints")]
|
#[cfg(feature = "internal-lints")]
|
||||||
&utils::internal_lints::INVALID_PATHS,
|
&utils::internal_lints::INVALID_PATHS,
|
||||||
@ -1026,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
|
||||||
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
|
||||||
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
|
||||||
|
store.register_late_pass(|| box utils::internal_lints::IfChainStyle);
|
||||||
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
|
||||||
store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
|
store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
|
||||||
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
|
||||||
@ -1442,6 +1445,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||||||
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||||
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||||
LintId::of(&utils::internal_lints::DEFAULT_LINT),
|
LintId::of(&utils::internal_lints::DEFAULT_LINT),
|
||||||
|
LintId::of(&utils::internal_lints::IF_CHAIN_STYLE),
|
||||||
LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
||||||
LintId::of(&utils::internal_lints::INVALID_PATHS),
|
LintId::of(&utils::internal_lints::INVALID_PATHS),
|
||||||
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||||
|
@ -46,9 +46,8 @@ pub(super) fn check<'tcx>(
|
|||||||
let some_ctor = is_some_ctor(cx, path.res);
|
let some_ctor = is_some_ctor(cx, path.res);
|
||||||
let ok_ctor = is_ok_ctor(cx, path.res);
|
let ok_ctor = is_ok_ctor(cx, path.res);
|
||||||
if some_ctor || ok_ctor;
|
if some_ctor || ok_ctor;
|
||||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||||
// Prepare the error message
|
// Prepare the error message
|
||||||
let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
|
let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ pub(super) fn check<'tcx>(
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::Index(base_left, idx_left) = lhs.kind;
|
if let ExprKind::Index(base_left, idx_left) = lhs.kind;
|
||||||
if let ExprKind::Index(base_right, idx_right) = rhs.kind;
|
if let ExprKind::Index(base_right, idx_right) = rhs.kind;
|
||||||
if is_slice_like(cx, cx.typeck_results().expr_ty(base_left))
|
if is_slice_like(cx, cx.typeck_results().expr_ty(base_left));
|
||||||
&& is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
|
if is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
|
||||||
if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts);
|
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 Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts);
|
||||||
|
|
||||||
|
@ -26,60 +26,59 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
|
|||||||
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
|
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
|
||||||
if let Some(ref generic_args) = chain_method.args;
|
if let Some(ref generic_args) = chain_method.args;
|
||||||
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
|
||||||
|
let ty = cx.typeck_results().node_type(ty.hir_id);
|
||||||
|
if is_type_diagnostic_item(cx, ty, sym::vec_type)
|
||||||
|
|| is_type_diagnostic_item(cx, ty, sym::vecdeque_type)
|
||||||
|
|| match_type(cx, ty, &paths::BTREEMAP)
|
||||||
|
|| is_type_diagnostic_item(cx, ty, sym::hashmap_type);
|
||||||
then {
|
then {
|
||||||
let ty = cx.typeck_results().node_type(ty.hir_id);
|
if method.ident.name == sym!(len) {
|
||||||
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
|
let span = shorten_needless_collect_span(expr);
|
||||||
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
|
span_lint_and_sugg(
|
||||||
match_type(cx, ty, &paths::BTREEMAP) ||
|
cx,
|
||||||
is_type_diagnostic_item(cx, ty, sym::hashmap_type) {
|
NEEDLESS_COLLECT,
|
||||||
if method.ident.name == sym!(len) {
|
span,
|
||||||
let span = shorten_needless_collect_span(expr);
|
NEEDLESS_COLLECT_MSG,
|
||||||
span_lint_and_sugg(
|
"replace with",
|
||||||
cx,
|
"count()".to_string(),
|
||||||
NEEDLESS_COLLECT,
|
Applicability::MachineApplicable,
|
||||||
span,
|
);
|
||||||
NEEDLESS_COLLECT_MSG,
|
}
|
||||||
"replace with",
|
if method.ident.name == sym!(is_empty) {
|
||||||
"count()".to_string(),
|
let span = shorten_needless_collect_span(expr);
|
||||||
Applicability::MachineApplicable,
|
span_lint_and_sugg(
|
||||||
);
|
cx,
|
||||||
}
|
NEEDLESS_COLLECT,
|
||||||
if method.ident.name == sym!(is_empty) {
|
span,
|
||||||
let span = shorten_needless_collect_span(expr);
|
NEEDLESS_COLLECT_MSG,
|
||||||
span_lint_and_sugg(
|
"replace with",
|
||||||
cx,
|
"next().is_none()".to_string(),
|
||||||
NEEDLESS_COLLECT,
|
Applicability::MachineApplicable,
|
||||||
span,
|
);
|
||||||
NEEDLESS_COLLECT_MSG,
|
}
|
||||||
"replace with",
|
if method.ident.name == sym!(contains) {
|
||||||
"next().is_none()".to_string(),
|
let contains_arg = snippet(cx, args[1].span, "??");
|
||||||
Applicability::MachineApplicable,
|
let span = shorten_needless_collect_span(expr);
|
||||||
);
|
span_lint_and_then(
|
||||||
}
|
cx,
|
||||||
if method.ident.name == sym!(contains) {
|
NEEDLESS_COLLECT,
|
||||||
let contains_arg = snippet(cx, args[1].span, "??");
|
span,
|
||||||
let span = shorten_needless_collect_span(expr);
|
NEEDLESS_COLLECT_MSG,
|
||||||
span_lint_and_then(
|
|diag| {
|
||||||
cx,
|
let (arg, pred) = contains_arg
|
||||||
NEEDLESS_COLLECT,
|
.strip_prefix('&')
|
||||||
span,
|
.map_or(("&x", &*contains_arg), |s| ("x", s));
|
||||||
NEEDLESS_COLLECT_MSG,
|
diag.span_suggestion(
|
||||||
|diag| {
|
span,
|
||||||
let (arg, pred) = contains_arg
|
"replace with",
|
||||||
.strip_prefix('&')
|
format!(
|
||||||
.map_or(("&x", &*contains_arg), |s| ("x", s));
|
"any(|{}| x == {})",
|
||||||
diag.span_suggestion(
|
arg, pred
|
||||||
span,
|
),
|
||||||
"replace with",
|
Applicability::MachineApplicable,
|
||||||
format!(
|
);
|
||||||
"any(|{}| x == {})",
|
}
|
||||||
arg, pred
|
);
|
||||||
),
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,49 +256,47 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
|||||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
|
if let ExprKind::Path(ref seqpath) = seqexpr.kind;
|
||||||
if let QPath::Resolved(None, ref seqvar) = *seqpath;
|
if let QPath::Resolved(None, ref seqvar) = *seqpath;
|
||||||
if seqvar.segments.len() == 1;
|
if seqvar.segments.len() == 1;
|
||||||
|
let index_used_directly = path_to_local_id(idx, self.var);
|
||||||
|
let indexed_indirectly = {
|
||||||
|
let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
|
||||||
|
walk_expr(&mut used_visitor, idx);
|
||||||
|
used_visitor.used
|
||||||
|
};
|
||||||
|
if indexed_indirectly || index_used_directly;
|
||||||
then {
|
then {
|
||||||
let index_used_directly = path_to_local_id(idx, self.var);
|
if self.prefer_mutable {
|
||||||
let indexed_indirectly = {
|
self.indexed_mut.insert(seqvar.segments[0].ident.name);
|
||||||
let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
|
}
|
||||||
walk_expr(&mut used_visitor, idx);
|
let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
|
||||||
used_visitor.used
|
match res {
|
||||||
};
|
Res::Local(hir_id) => {
|
||||||
|
let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||||
if indexed_indirectly || index_used_directly {
|
let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
|
||||||
if self.prefer_mutable {
|
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
|
||||||
self.indexed_mut.insert(seqvar.segments[0].ident.name);
|
if indexed_indirectly {
|
||||||
}
|
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
|
||||||
let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
|
|
||||||
match res {
|
|
||||||
Res::Local(hir_id) => {
|
|
||||||
let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
|
|
||||||
let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id);
|
|
||||||
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
|
|
||||||
if indexed_indirectly {
|
|
||||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
|
|
||||||
}
|
|
||||||
if index_used_directly {
|
|
||||||
self.indexed_directly.insert(
|
|
||||||
seqvar.segments[0].ident.name,
|
|
||||||
(Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false; // no need to walk further *on the variable*
|
|
||||||
}
|
}
|
||||||
Res::Def(DefKind::Static | DefKind::Const, ..) => {
|
if index_used_directly {
|
||||||
if indexed_indirectly {
|
self.indexed_directly.insert(
|
||||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
|
seqvar.segments[0].ident.name,
|
||||||
}
|
(Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
||||||
if index_used_directly {
|
);
|
||||||
self.indexed_directly.insert(
|
|
||||||
seqvar.segments[0].ident.name,
|
|
||||||
(None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false; // no need to walk further *on the variable*
|
|
||||||
}
|
}
|
||||||
_ => (),
|
return false; // no need to walk further *on the variable*
|
||||||
}
|
}
|
||||||
|
Res::Def(DefKind::Static | DefKind::Const, ..) => {
|
||||||
|
if indexed_indirectly {
|
||||||
|
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
|
||||||
|
}
|
||||||
|
if index_used_directly {
|
||||||
|
self.indexed_directly.insert(
|
||||||
|
seqvar.segments[0].ident.name,
|
||||||
|
(None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false; // no need to walk further *on the variable*
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,8 +63,8 @@ pub(super) fn check<'tcx>(
|
|||||||
match cx.qpath_res(qpath, pushed_item.hir_id) {
|
match cx.qpath_res(qpath, pushed_item.hir_id) {
|
||||||
// immutable bindings that are initialized with literal or constant
|
// immutable bindings that are initialized with literal or constant
|
||||||
Res::Local(hir_id) => {
|
Res::Local(hir_id) => {
|
||||||
|
let node = cx.tcx.hir().get(hir_id);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let node = cx.tcx.hir().get(hir_id);
|
|
||||||
if let Node::Binding(pat) = node;
|
if let Node::Binding(pat) = node;
|
||||||
if let PatKind::Binding(bind_ann, ..) = pat.kind;
|
if let PatKind::Binding(bind_ann, ..) = pat.kind;
|
||||||
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
|
if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
|
||||||
|
@ -103,9 +103,8 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::Path(ref qpath) = ex.kind;
|
if let ExprKind::Path(ref qpath) = ex.kind;
|
||||||
if let QPath::Resolved(None, _) = *qpath;
|
if let QPath::Resolved(None, _) = *qpath;
|
||||||
let res = self.cx.qpath_res(qpath, ex.hir_id);
|
|
||||||
then {
|
then {
|
||||||
match res {
|
match self.cx.qpath_res(qpath, ex.hir_id) {
|
||||||
Res::Local(hir_id) => {
|
Res::Local(hir_id) => {
|
||||||
self.ids.insert(hir_id);
|
self.ids.insert(hir_id);
|
||||||
},
|
},
|
||||||
|
@ -113,8 +113,8 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
|
|
||||||
if let Some(marker) = markers.next();
|
if let Some(marker) = markers.next();
|
||||||
if markers.count() == 0 && variants.len() > 1;
|
if markers.count() == 0 && variants.len() > 1;
|
||||||
then {
|
then {
|
||||||
|
@ -58,9 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for MapClone {
|
|||||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||||
if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator);
|
if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator);
|
||||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
|
if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
|
||||||
let closure_body = cx.tcx.hir().body(body_id);
|
|
||||||
let closure_expr = remove_blocks(&closure_body.value);
|
|
||||||
then {
|
then {
|
||||||
|
let closure_body = cx.tcx.hir().body(body_id);
|
||||||
|
let closure_expr = remove_blocks(&closure_body.value);
|
||||||
match closure_body.params[0].pat.kind {
|
match closure_body.params[0].pat.kind {
|
||||||
hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||||
hir::BindingAnnotation::Unannotated, .., name, None
|
hir::BindingAnnotation::Unannotated, .., name, None
|
||||||
|
@ -168,17 +168,15 @@ fn unit_closure<'tcx>(
|
|||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
expr: &hir::Expr<'_>,
|
expr: &hir::Expr<'_>,
|
||||||
) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
|
) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
|
||||||
if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind {
|
if_chain! {
|
||||||
|
if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind;
|
||||||
let body = cx.tcx.hir().body(inner_expr_id);
|
let body = cx.tcx.hir().body(inner_expr_id);
|
||||||
let body_expr = &body.value;
|
let body_expr = &body.value;
|
||||||
|
if decl.inputs.len() == 1;
|
||||||
if_chain! {
|
if is_unit_expression(cx, body_expr);
|
||||||
if decl.inputs.len() == 1;
|
if let Some(binding) = iter_input_pats(&decl, body).next();
|
||||||
if is_unit_expression(cx, body_expr);
|
then {
|
||||||
if let Some(binding) = iter_input_pats(&decl, body).next();
|
return Some((binding, body_expr));
|
||||||
then {
|
|
||||||
return Some((binding, body_expr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -732,8 +732,8 @@ fn report_single_match_single_pattern(
|
|||||||
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
|
format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
||||||
let (msg, sugg) = if_chain! {
|
let (msg, sugg) = if_chain! {
|
||||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
|
|
||||||
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
||||||
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
||||||
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||||
@ -1480,8 +1480,8 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
|
|||||||
|
|
||||||
/// Returns true if the `ex` match expression is in a local (`let`) statement
|
/// Returns true if the `ex` match expression is in a local (`let`) statement
|
||||||
fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
|
fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
|
||||||
|
let map = &cx.tcx.hir();
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let map = &cx.tcx.hir();
|
|
||||||
if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
|
if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
|
||||||
if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
|
if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
|
||||||
then {
|
then {
|
||||||
|
@ -225,34 +225,33 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||||
if let ExprKind::Call(ref repl_func, _) = src.kind {
|
if_chain! {
|
||||||
if_chain! {
|
if let ExprKind::Call(ref repl_func, _) = src.kind;
|
||||||
if !in_external_macro(cx.tcx.sess, expr_span);
|
if !in_external_macro(cx.tcx.sess, expr_span);
|
||||||
if let ExprKind::Path(ref repl_func_qpath) = repl_func.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();
|
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
|
||||||
if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
|
if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
|
||||||
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
|
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
|
||||||
|
|
||||||
then {
|
then {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
MEM_REPLACE_WITH_DEFAULT,
|
MEM_REPLACE_WITH_DEFAULT,
|
||||||
expr_span,
|
expr_span,
|
||||||
"replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
|
"replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
|
||||||
|diag| {
|
|diag| {
|
||||||
if !in_macro(expr_span) {
|
if !in_macro(expr_span) {
|
||||||
let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
|
let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
|
||||||
|
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
expr_span,
|
expr_span,
|
||||||
"consider using",
|
"consider using",
|
||||||
suggestion,
|
suggestion,
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||||
use clippy_utils::source::snippet_with_applicability;
|
use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
@ -10,31 +9,26 @@ use rustc_span::sym;
|
|||||||
use super::BYTES_NTH;
|
use super::BYTES_NTH;
|
||||||
|
|
||||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) {
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) {
|
||||||
if_chain! {
|
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
let caller_type = if ty.is_str() {
|
||||||
let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
"str"
|
||||||
Some("String")
|
} else if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||||
} else if ty.is_str() {
|
"String"
|
||||||
Some("str")
|
} else {
|
||||||
} else {
|
return;
|
||||||
None
|
};
|
||||||
};
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
if let Some(caller_type) = caller_type;
|
span_lint_and_sugg(
|
||||||
then {
|
cx,
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
BYTES_NTH,
|
||||||
span_lint_and_sugg(
|
expr.span,
|
||||||
cx,
|
&format!("called `.byte().nth()` on a `{}`", caller_type),
|
||||||
BYTES_NTH,
|
"try",
|
||||||
expr.span,
|
format!(
|
||||||
&format!("called `.byte().nth()` on a `{}`", caller_type),
|
"{}.as_bytes().get({})",
|
||||||
"try",
|
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
||||||
format!(
|
snippet_with_applicability(cx, n_arg.span, "..", &mut applicability)
|
||||||
"{}.as_bytes().get({})",
|
),
|
||||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
applicability,
|
||||||
snippet_with_applicability(cx, n_arg.span, "..", &mut applicability)
|
);
|
||||||
),
|
|
||||||
applicability,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
||||||
|
let call_site = expr.span.source_callsite();
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let call_site = expr.span.source_callsite();
|
|
||||||
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
|
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
|
||||||
let snippet_split = snippet.split("::").collect::<Vec<_>>();
|
let snippet_split = snippet.split("::").collect::<Vec<_>>();
|
||||||
if let Some((_, elements)) = snippet_split.split_last();
|
if let Some((_, elements)) = snippet_split.split_last();
|
||||||
|
@ -1882,11 +1882,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
||||||
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
||||||
let first_arg_span = first_arg_ty.span;
|
|
||||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
|
||||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let first_arg_span = first_arg_ty.span;
|
||||||
|
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||||
|
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
|
||||||
wrong_self_convention::check(
|
wrong_self_convention::check(
|
||||||
cx,
|
cx,
|
||||||
&item.ident.name.as_str(),
|
&item.ident.name.as_str(),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use clippy_utils::usage::mutated_variables;
|
use clippy_utils::usage::mutated_variables;
|
||||||
use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
|
use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
|
||||||
use if_chain::if_chain;
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
@ -54,18 +53,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
|
|||||||
fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
|
fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
hir::ExprKind::Call(ref func, ref args) => {
|
hir::ExprKind::Call(ref func, ref args) => {
|
||||||
if_chain! {
|
if let hir::ExprKind::Path(ref path) = func.kind {
|
||||||
if let hir::ExprKind::Path(ref path) = func.kind;
|
if match_qpath(path, &paths::OPTION_SOME) {
|
||||||
then {
|
if path_to_local_id(&args[0], arg_id) {
|
||||||
if match_qpath(path, &paths::OPTION_SOME) {
|
return (false, false);
|
||||||
if path_to_local_id(&args[0], arg_id) {
|
|
||||||
return (false, false)
|
|
||||||
}
|
|
||||||
return (true, false);
|
|
||||||
}
|
}
|
||||||
// We don't know. It might do anything.
|
return (true, false);
|
||||||
return (true, true);
|
|
||||||
}
|
}
|
||||||
|
// We don't know. It might do anything.
|
||||||
|
return (true, true);
|
||||||
}
|
}
|
||||||
(true, true)
|
(true, true)
|
||||||
},
|
},
|
||||||
|
@ -308,46 +308,45 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
|||||||
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
|
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
|
||||||
if let Some(ref init) = local.init;
|
if let Some(ref init) = local.init;
|
||||||
if !higher::is_from_for_desugar(local);
|
if !higher::is_from_for_desugar(local);
|
||||||
|
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
|
||||||
then {
|
then {
|
||||||
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
|
// use the macro callsite when the init span (but not the whole local span)
|
||||||
// use the macro callsite when the init span (but not the whole local span)
|
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
|
||||||
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
|
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
|
||||||
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
|
Sugg::hir_with_macro_callsite(cx, init, "..")
|
||||||
Sugg::hir_with_macro_callsite(cx, init, "..")
|
} else {
|
||||||
} else {
|
Sugg::hir(cx, init, "..")
|
||||||
Sugg::hir(cx, init, "..")
|
};
|
||||||
};
|
let (mutopt, initref) = if an == BindingAnnotation::RefMut {
|
||||||
let (mutopt, initref) = if an == BindingAnnotation::RefMut {
|
("mut ", sugg_init.mut_addr())
|
||||||
("mut ", sugg_init.mut_addr())
|
} else {
|
||||||
} else {
|
("", sugg_init.addr())
|
||||||
("", sugg_init.addr())
|
};
|
||||||
};
|
let tyopt = if let Some(ref ty) = local.ty {
|
||||||
let tyopt = if let Some(ref ty) = local.ty {
|
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
|
||||||
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
|
} else {
|
||||||
} else {
|
String::new()
|
||||||
String::new()
|
};
|
||||||
};
|
span_lint_hir_and_then(
|
||||||
span_lint_hir_and_then(
|
cx,
|
||||||
cx,
|
TOPLEVEL_REF_ARG,
|
||||||
TOPLEVEL_REF_ARG,
|
init.hir_id,
|
||||||
init.hir_id,
|
local.pat.span,
|
||||||
local.pat.span,
|
"`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
|
||||||
"`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
|
|diag| {
|
||||||
|diag| {
|
diag.span_suggestion(
|
||||||
diag.span_suggestion(
|
stmt.span,
|
||||||
stmt.span,
|
"try",
|
||||||
"try",
|
format!(
|
||||||
format!(
|
"let {name}{tyopt} = {initref};",
|
||||||
"let {name}{tyopt} = {initref};",
|
name=snippet(cx, name.span, ".."),
|
||||||
name=snippet(cx, name.span, ".."),
|
tyopt=tyopt,
|
||||||
tyopt=tyopt,
|
initref=initref,
|
||||||
initref=initref,
|
),
|
||||||
),
|
Applicability::MachineApplicable,
|
||||||
Applicability::MachineApplicable,
|
);
|
||||||
);
|
}
|
||||||
}
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if_chain! {
|
if_chain! {
|
||||||
@ -462,21 +461,18 @@ fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if !in_constant(cx, cmp_expr.hir_id);
|
if !in_constant(cx, cmp_expr.hir_id);
|
||||||
if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
|
if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
|
||||||
|
if match value {
|
||||||
|
Constant::F32(num) => num.is_nan(),
|
||||||
|
Constant::F64(num) => num.is_nan(),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
then {
|
then {
|
||||||
let needs_lint = match value {
|
span_lint(
|
||||||
Constant::F32(num) => num.is_nan(),
|
cx,
|
||||||
Constant::F64(num) => num.is_nan(),
|
CMP_NAN,
|
||||||
_ => false,
|
cmp_expr.span,
|
||||||
};
|
"doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
|
||||||
|
);
|
||||||
if needs_lint {
|
|
||||||
span_lint(
|
|
||||||
cx,
|
|
||||||
CMP_NAN,
|
|
||||||
cmp_expr.span,
|
|
||||||
"doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,60 +97,60 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
|||||||
// impl of `Default`
|
// impl of `Default`
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if sig.decl.inputs.is_empty() && name == sym::new && cx.access_levels.is_reachable(id) {
|
if_chain! {
|
||||||
|
if sig.decl.inputs.is_empty();
|
||||||
|
if name == sym::new;
|
||||||
|
if cx.access_levels.is_reachable(id);
|
||||||
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
|
let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
|
||||||
let self_ty = cx.tcx.type_of(self_def_id);
|
let self_ty = cx.tcx.type_of(self_def_id);
|
||||||
if_chain! {
|
if TyS::same_type(self_ty, return_ty(cx, id));
|
||||||
if TyS::same_type(self_ty, return_ty(cx, id));
|
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
|
||||||
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
|
then {
|
||||||
then {
|
if self.impling_types.is_none() {
|
||||||
if self.impling_types.is_none() {
|
let mut impls = HirIdSet::default();
|
||||||
let mut impls = HirIdSet::default();
|
cx.tcx.for_each_impl(default_trait_id, |d| {
|
||||||
cx.tcx.for_each_impl(default_trait_id, |d| {
|
if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
|
||||||
if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
|
if let Some(local_def_id) = ty_def.did.as_local() {
|
||||||
if let Some(local_def_id) = ty_def.did.as_local() {
|
impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
|
||||||
impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.impling_types = Some(impls);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a Default implementation exists for the Self type, regardless of
|
|
||||||
// generics
|
|
||||||
if_chain! {
|
|
||||||
if let Some(ref impling_types) = self.impling_types;
|
|
||||||
if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
|
|
||||||
if let Some(self_local_did) = self_def.did.as_local();
|
|
||||||
then {
|
|
||||||
let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
|
|
||||||
if impling_types.contains(&self_id) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
self.impling_types = Some(impls);
|
||||||
let generics_sugg = snippet(cx, generics.span, "");
|
|
||||||
span_lint_hir_and_then(
|
|
||||||
cx,
|
|
||||||
NEW_WITHOUT_DEFAULT,
|
|
||||||
id,
|
|
||||||
impl_item.span,
|
|
||||||
&format!(
|
|
||||||
"you should consider adding a `Default` implementation for `{}`",
|
|
||||||
self_ty
|
|
||||||
),
|
|
||||||
|diag| {
|
|
||||||
diag.suggest_prepend_item(
|
|
||||||
cx,
|
|
||||||
item.span,
|
|
||||||
"try this",
|
|
||||||
&create_new_without_default_suggest_msg(self_ty, &generics_sugg),
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if a Default implementation exists for the Self type, regardless of
|
||||||
|
// generics
|
||||||
|
if_chain! {
|
||||||
|
if let Some(ref impling_types) = self.impling_types;
|
||||||
|
if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
|
||||||
|
if let Some(self_local_did) = self_def.did.as_local();
|
||||||
|
let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
|
||||||
|
if impling_types.contains(&self_id);
|
||||||
|
then {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let generics_sugg = snippet(cx, generics.span, "");
|
||||||
|
span_lint_hir_and_then(
|
||||||
|
cx,
|
||||||
|
NEW_WITHOUT_DEFAULT,
|
||||||
|
id,
|
||||||
|
impl_item.span,
|
||||||
|
&format!(
|
||||||
|
"you should consider adding a `Default` implementation for `{}`",
|
||||||
|
self_ty
|
||||||
|
),
|
||||||
|
|diag| {
|
||||||
|
diag.suggest_prepend_item(
|
||||||
|
cx,
|
||||||
|
item.span,
|
||||||
|
"try this",
|
||||||
|
&create_new_without_default_suggest_msg(self_ty, &generics_sugg),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,19 +307,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
|
|||||||
// we should use here as a frozen variant is a potential to be frozen
|
// we should use here as a frozen variant is a potential to be frozen
|
||||||
// similar to unknown layouts.
|
// similar to unknown layouts.
|
||||||
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
||||||
|
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||||
|
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
||||||
|
if is_unfrozen(cx, normalized);
|
||||||
|
if is_value_unfrozen_poly(cx, *body_id, normalized);
|
||||||
then {
|
then {
|
||||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
lint(
|
||||||
let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
|
cx,
|
||||||
if is_unfrozen(cx, normalized)
|
Source::Assoc {
|
||||||
&& is_value_unfrozen_poly(cx, *body_id, normalized)
|
item: impl_item.span,
|
||||||
{
|
},
|
||||||
lint(
|
);
|
||||||
cx,
|
|
||||||
Source::Assoc {
|
|
||||||
item: impl_item.span,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -97,11 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||||||
fn get_outer_span(expr: &Expr<'_>) -> Span {
|
fn get_outer_span(expr: &Expr<'_>) -> Span {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if expr.span.from_expansion();
|
if expr.span.from_expansion();
|
||||||
let first = expr.span.ctxt().outer_expn_data();
|
let first = expr.span.ctxt().outer_expn_data().call_site;
|
||||||
if first.call_site.from_expansion();
|
if first.from_expansion();
|
||||||
let second = first.call_site.ctxt().outer_expn_data();
|
|
||||||
then {
|
then {
|
||||||
second.call_site
|
first.ctxt().outer_expn_data().call_site
|
||||||
} else {
|
} else {
|
||||||
expr.span
|
expr.span
|
||||||
}
|
}
|
||||||
|
@ -271,19 +271,18 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
|
|||||||
GenericArg::Type(ty) => Some(ty),
|
GenericArg::Type(ty) => Some(ty),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
let replacement = snippet_opt(cx, inner.span);
|
||||||
|
if let Some(r) = replacement;
|
||||||
then {
|
then {
|
||||||
let replacement = snippet_opt(cx, inner.span);
|
span_lint_and_sugg(
|
||||||
if let Some(r) = replacement {
|
cx,
|
||||||
span_lint_and_sugg(
|
PTR_ARG,
|
||||||
cx,
|
arg.span,
|
||||||
PTR_ARG,
|
"using a reference to `Cow` is not recommended",
|
||||||
arg.span,
|
"change this to",
|
||||||
"using a reference to `Cow` is not recommended",
|
"&".to_owned() + &r,
|
||||||
"change this to",
|
Applicability::Unspecified,
|
||||||
"&".to_owned() + &r,
|
);
|
||||||
Applicability::Unspecified,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,32 +320,29 @@ fn match_ident(e: &Expr<'_>) -> Option<Ident> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
|
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
|
||||||
let name = path.ident.as_str();
|
if_chain! {
|
||||||
if name == "zip" && args.len() == 2 {
|
if path.ident.as_str() == "zip";
|
||||||
let iter = &args[0].kind;
|
if let [iter, zip_arg] = args;
|
||||||
let zip_arg = &args[1];
|
// `.iter()` call
|
||||||
if_chain! {
|
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = iter.kind;
|
||||||
// `.iter()` call
|
if iter_path.ident.name == sym::iter;
|
||||||
if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter;
|
// range expression in `.zip()` call: `0..x.len()`
|
||||||
if iter_path.ident.name == sym::iter;
|
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
|
||||||
// range expression in `.zip()` call: `0..x.len()`
|
if is_integer_const(cx, start, 0);
|
||||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
|
// `.len()` call
|
||||||
if is_integer_const(cx, start, 0);
|
if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
|
||||||
// `.len()` call
|
if len_path.ident.name == sym!(len) && len_args.len() == 1;
|
||||||
if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
|
// `.iter()` and `.len()` called on same `Path`
|
||||||
if len_path.ident.name == sym!(len) && len_args.len() == 1;
|
if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
|
||||||
// `.iter()` and `.len()` called on same `Path`
|
if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
|
||||||
if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
|
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
|
||||||
if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
|
then {
|
||||||
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
|
span_lint(cx,
|
||||||
then {
|
RANGE_ZIP_WITH_LEN,
|
||||||
span_lint(cx,
|
span,
|
||||||
RANGE_ZIP_WITH_LEN,
|
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||||
span,
|
snippet(cx, iter_args[0].span, "_"))
|
||||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
);
|
||||||
snippet(cx, iter_args[0].span, "_"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let hir::ExprKind::Call(ref closure, _) = expr.kind;
|
if let hir::ExprKind::Call(ref closure, _) = expr.kind;
|
||||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind;
|
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind;
|
||||||
if self.path.segments[0].ident == path.segments[0].ident
|
if self.path.segments[0].ident == path.segments[0].ident;
|
||||||
&& self.path.res == path.res;
|
if self.path.res == path.res;
|
||||||
then {
|
then {
|
||||||
self.count += 1;
|
self.count += 1;
|
||||||
}
|
}
|
||||||
|
@ -68,14 +68,13 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
|||||||
|
|
||||||
// Check for more than one binary operation in the implemented function
|
// Check for more than one binary operation in the implemented function
|
||||||
// Linting when multiple operations are involved can result in false positives
|
// Linting when multiple operations are involved can result in false positives
|
||||||
|
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id);
|
|
||||||
if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
|
if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn);
|
||||||
if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
||||||
let body = cx.tcx.hir().body(body_id);
|
|
||||||
let mut visitor = BinaryExprVisitor { nb_binops: 0 };
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
let mut visitor = BinaryExprVisitor { nb_binops: 0 };
|
||||||
walk_expr(&mut visitor, &body.value);
|
walk_expr(&mut visitor, &body.value);
|
||||||
if visitor.nb_binops > 1 {
|
if visitor.nb_binops > 1 {
|
||||||
return;
|
return;
|
||||||
|
@ -36,10 +36,10 @@ pub(super) fn check<'tcx>(
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
// if the expression is a float literal and it is unsuffixed then
|
// if the expression is a float literal and it is unsuffixed then
|
||||||
// add a suffix so the suggestion is valid and unambiguous
|
// add a suffix so the suggestion is valid and unambiguous
|
||||||
let op = format!("{}{}", arg, float_ty.name_str()).into();
|
|
||||||
if let ExprKind::Lit(lit) = &expr.kind;
|
if let ExprKind::Lit(lit) = &expr.kind;
|
||||||
if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
|
if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
|
||||||
then {
|
then {
|
||||||
|
let op = format!("{}{}", arg, float_ty.name_str()).into();
|
||||||
match arg {
|
match arg {
|
||||||
sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
|
sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
|
||||||
_ => arg = sugg::Sugg::NonParen(op)
|
_ => arg = sugg::Sugg::NonParen(op)
|
||||||
|
@ -138,9 +138,8 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let ty::Adt(_, subst) = ty.kind();
|
if let ty::Adt(_, subst) = ty.kind();
|
||||||
if is_type_diagnostic_item(cx, ty, sym::result_type);
|
if is_type_diagnostic_item(cx, ty, sym::result_type);
|
||||||
let err_ty = subst.type_at(1);
|
|
||||||
then {
|
then {
|
||||||
Some(err_ty)
|
Some(subst.type_at(1))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -156,10 +155,8 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<
|
|||||||
|
|
||||||
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
|
||||||
if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did);
|
if cx.tcx.is_diagnostic_item(sym::result_type, ready_def.did);
|
||||||
let err_ty = ready_subst.type_at(1);
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
Some(err_ty)
|
Some(ready_subst.type_at(1))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -179,10 +176,8 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) ->
|
|||||||
|
|
||||||
if let ty::Adt(some_def, some_subst) = some_ty.kind();
|
if let ty::Adt(some_def, some_subst) = some_ty.kind();
|
||||||
if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did);
|
if cx.tcx.is_diagnostic_item(sym::result_type, some_def.did);
|
||||||
let err_ty = some_subst.type_at(1);
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
Some(err_ty)
|
Some(some_subst.type_at(1))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,8 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa
|
|||||||
let ty = cx.tcx.erase_late_bound_regions(ret_ty);
|
let ty = cx.tcx.erase_late_bound_regions(ret_ty);
|
||||||
if ty.is_unit();
|
if ty.is_unit();
|
||||||
then {
|
then {
|
||||||
|
let body = cx.tcx.hir().body(body_id);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let body = cx.tcx.hir().body(body_id);
|
|
||||||
if let ExprKind::Block(block, _) = body.value.kind;
|
if let ExprKind::Block(block, _) = body.value.kind;
|
||||||
if block.expr.is_none();
|
if block.expr.is_none();
|
||||||
if let Some(stmt) = block.stmts.last();
|
if let Some(stmt) = block.stmts.last();
|
||||||
|
@ -19,9 +19,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||||||
if is_questionmark_desugar_marked_call(expr) {
|
if is_questionmark_desugar_marked_call(expr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let map = &cx.tcx.hir();
|
||||||
|
let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
|
||||||
if_chain! {
|
if_chain! {
|
||||||
let map = &cx.tcx.hir();
|
|
||||||
let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
|
|
||||||
if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
|
if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
|
||||||
if is_questionmark_desugar_marked_call(parent_expr);
|
if is_questionmark_desugar_marked_call(parent_expr);
|
||||||
then {
|
then {
|
||||||
|
@ -116,8 +116,8 @@ impl LateLintPass<'_> for UnnamedAddress {
|
|||||||
if_chain! {
|
if_chain! {
|
||||||
if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
|
if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
|
||||||
if is_comparison(binop.node);
|
if is_comparison(binop.node);
|
||||||
if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() &&
|
if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr();
|
||||||
cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr();
|
if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr();
|
||||||
if is_fn_def(cx, left) || is_fn_def(cx, right);
|
if is_fn_def(cx, left) || is_fn_def(cx, right);
|
||||||
then {
|
then {
|
||||||
span_lint(
|
span_lint(
|
||||||
|
@ -49,21 +49,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
|
|||||||
if assoc_item.fn_has_self_parameter;
|
if assoc_item.fn_has_self_parameter;
|
||||||
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
|
if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
|
||||||
let body = cx.tcx.hir().body(*body_id);
|
let body = cx.tcx.hir().body(*body_id);
|
||||||
if !body.params.is_empty();
|
if let [self_param, ..] = body.params;
|
||||||
|
let self_hir_id = self_param.pat.hir_id;
|
||||||
|
if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body);
|
||||||
then {
|
then {
|
||||||
let self_param = &body.params[0];
|
span_lint_and_help(
|
||||||
let self_hir_id = self_param.pat.hir_id;
|
cx,
|
||||||
if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body) {
|
UNUSED_SELF,
|
||||||
span_lint_and_help(
|
self_param.span,
|
||||||
cx,
|
"unused `self` argument",
|
||||||
UNUSED_SELF,
|
None,
|
||||||
self_param.span,
|
"consider refactoring to a associated function",
|
||||||
"unused `self` argument",
|
);
|
||||||
None,
|
|
||||||
"consider refactoring to a associated function",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,31 +110,27 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
|
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||||
if_chain! {
|
if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
|
||||||
|
let body = cx.tcx.hir().body(body_id);
|
||||||
|
let mut fpu = FindExpectUnwrap {
|
||||||
|
lcx: cx,
|
||||||
|
typeck_results: cx.tcx.typeck(impl_item.def_id),
|
||||||
|
result: Vec::new(),
|
||||||
|
};
|
||||||
|
fpu.visit_expr(&body.value);
|
||||||
|
|
||||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
|
// if we've found one, lint
|
||||||
then {
|
if !fpu.result.is_empty() {
|
||||||
let body = cx.tcx.hir().body(body_id);
|
span_lint_and_then(
|
||||||
let mut fpu = FindExpectUnwrap {
|
cx,
|
||||||
lcx: cx,
|
UNWRAP_IN_RESULT,
|
||||||
typeck_results: cx.tcx.typeck(impl_item.def_id),
|
impl_span,
|
||||||
result: Vec::new(),
|
"used unwrap or expect in a function that returns result or option",
|
||||||
};
|
move |diag| {
|
||||||
fpu.visit_expr(&body.value);
|
diag.help("unwrap and expect should not be used in a function that returns result or option");
|
||||||
|
diag.span_note(fpu.result, "potential non-recoverable error(s)");
|
||||||
// if we've found one, lint
|
},
|
||||||
if !fpu.result.is_empty() {
|
);
|
||||||
span_lint_and_then(
|
|
||||||
cx,
|
|
||||||
UNWRAP_IN_RESULT,
|
|
||||||
impl_span,
|
|
||||||
"used unwrap or expect in a function that returns result or option",
|
|
||||||
move |diag| {
|
|
||||||
diag.help(
|
|
||||||
"unwrap and expect should not be used in a function that returns result or option" );
|
|
||||||
diag.span_note(fpu.result, "potential non-recoverable error(s)");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,25 +103,23 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" {
|
if_chain! {
|
||||||
if_chain! {
|
if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into";
|
||||||
let a = cx.typeck_results().expr_ty(e);
|
let a = cx.typeck_results().expr_ty(e);
|
||||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||||
if is_type_diagnostic_item(cx, a, sym::result_type);
|
if is_type_diagnostic_item(cx, a, sym::result_type);
|
||||||
if let ty::Adt(_, substs) = a.kind();
|
if let ty::Adt(_, substs) = a.kind();
|
||||||
if let Some(a_type) = substs.types().next();
|
if let Some(a_type) = substs.types().next();
|
||||||
if TyS::same_type(a_type, b);
|
if TyS::same_type(a_type, b);
|
||||||
|
then {
|
||||||
then {
|
span_lint_and_help(
|
||||||
span_lint_and_help(
|
cx,
|
||||||
cx,
|
USELESS_CONVERSION,
|
||||||
USELESS_CONVERSION,
|
e.span,
|
||||||
e.span,
|
&format!("useless conversion to the same type: `{}`", b),
|
||||||
&format!("useless conversion to the same type: `{}`", b),
|
None,
|
||||||
None,
|
"consider removing `.try_into()`",
|
||||||
"consider removing `.try_into()`",
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -131,10 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||||||
if args.len() == 1;
|
if args.len() == 1;
|
||||||
if let ExprKind::Path(ref qpath) = path.kind;
|
if let ExprKind::Path(ref qpath) = path.kind;
|
||||||
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
|
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
|
||||||
let a = cx.typeck_results().expr_ty(e);
|
|
||||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
|
||||||
|
|
||||||
then {
|
then {
|
||||||
|
let a = cx.typeck_results().expr_ty(e);
|
||||||
|
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if match_def_path(cx, def_id, &paths::TRY_FROM);
|
if match_def_path(cx, def_id, &paths::TRY_FROM);
|
||||||
if is_type_diagnostic_item(cx, a, sym::result_type);
|
if is_type_diagnostic_item(cx, a, sym::result_type);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::consts::{constant_simple, Constant};
|
use crate::consts::{constant_simple, Constant};
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::match_type;
|
use clippy_utils::ty::match_type;
|
||||||
use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq};
|
use clippy_utils::{
|
||||||
|
is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq,
|
||||||
|
};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
|
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
|
||||||
use rustc_ast::visit::FnKind;
|
use rustc_ast::visit::FnKind;
|
||||||
@ -14,15 +16,17 @@ use rustc_hir::def_id::DefId;
|
|||||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp,
|
BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt,
|
||||||
|
StmtKind, Ty, TyKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use rustc_middle::mir::interpret::ConstValue;
|
use rustc_middle::mir::interpret::ConstValue;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::source_map::{Span, Spanned};
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::symbol::{Symbol, SymbolStr};
|
use rustc_span::symbol::{Symbol, SymbolStr};
|
||||||
|
use rustc_span::{BytePos, Span};
|
||||||
use rustc_typeck::hir_ty_to_ty;
|
use rustc_typeck::hir_ty_to_ty;
|
||||||
|
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::{Borrow, Cow};
|
||||||
@ -297,6 +301,13 @@ declare_clippy_lint! {
|
|||||||
"unnecessary conversion between Symbol and string"
|
"unnecessary conversion between Symbol and string"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// Finds unidiomatic usage of `if_chain!`
|
||||||
|
pub IF_CHAIN_STYLE,
|
||||||
|
internal,
|
||||||
|
"non-idiomatic `if_chain!` usage"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||||
|
|
||||||
impl EarlyLintPass for ClippyLintsInternal {
|
impl EarlyLintPass for ClippyLintsInternal {
|
||||||
@ -577,9 +588,9 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
|||||||
if stmts.len() == 1 && block.expr.is_none();
|
if stmts.len() == 1 && block.expr.is_none();
|
||||||
if let StmtKind::Semi(only_expr) = &stmts[0].kind;
|
if let StmtKind::Semi(only_expr) = &stmts[0].kind;
|
||||||
if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind;
|
if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind;
|
||||||
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
|
|
||||||
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
|
||||||
then {
|
then {
|
||||||
|
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
|
||||||
|
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
||||||
match &*ps.ident.as_str() {
|
match &*ps.ident.as_str() {
|
||||||
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
|
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
|
||||||
suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
|
suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
|
||||||
@ -1063,3 +1074,159 @@ impl<'tcx> SymbolStrExpr<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
|
||||||
|
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
|
||||||
|
let (local, after, if_chain_span) = if_chain! {
|
||||||
|
if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
|
||||||
|
if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
|
||||||
|
then { (local, after, if_chain_span) } else { return }
|
||||||
|
};
|
||||||
|
if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
|
||||||
|
span_lint(
|
||||||
|
cx,
|
||||||
|
IF_CHAIN_STYLE,
|
||||||
|
if_chain_local_span(cx, local, if_chain_span),
|
||||||
|
"`let` expression should be above the `if_chain!`",
|
||||||
|
);
|
||||||
|
} else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
|
||||||
|
span_lint(
|
||||||
|
cx,
|
||||||
|
IF_CHAIN_STYLE,
|
||||||
|
if_chain_local_span(cx, local, if_chain_span),
|
||||||
|
"`let` expression should be inside `then { .. }`",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||||
|
let (cond, then, els) = match expr.kind {
|
||||||
|
ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()),
|
||||||
|
ExprKind::Match(
|
||||||
|
_,
|
||||||
|
[arm, ..],
|
||||||
|
MatchSource::IfLetDesugar {
|
||||||
|
contains_else_clause: els,
|
||||||
|
},
|
||||||
|
) => (None, arm.body, els),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let then_block = match then.kind {
|
||||||
|
ExprKind::Block(block, _) => block,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
let if_chain_span = is_expn_of(expr.span, "if_chain");
|
||||||
|
if !els {
|
||||||
|
check_nested_if_chains(cx, expr, then_block, if_chain_span);
|
||||||
|
}
|
||||||
|
let if_chain_span = match if_chain_span {
|
||||||
|
None => return,
|
||||||
|
Some(span) => span,
|
||||||
|
};
|
||||||
|
// check for `if a && b;`
|
||||||
|
if_chain! {
|
||||||
|
if let Some(cond) = cond;
|
||||||
|
if let ExprKind::Binary(op, _, _) = cond.kind;
|
||||||
|
if op.node == BinOpKind::And;
|
||||||
|
if cx.sess().source_map().is_multiline(cond.span);
|
||||||
|
then {
|
||||||
|
span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
|
||||||
|
&& is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
|
||||||
|
{
|
||||||
|
span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_nested_if_chains(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
if_expr: &Expr<'_>,
|
||||||
|
then_block: &Block<'_>,
|
||||||
|
if_chain_span: Option<Span>,
|
||||||
|
) {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let (head, tail) = match *then_block {
|
||||||
|
Block { stmts, expr: Some(tail), .. } => (stmts, tail),
|
||||||
|
Block {
|
||||||
|
stmts: &[
|
||||||
|
ref head @ ..,
|
||||||
|
Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
|
||||||
|
],
|
||||||
|
..
|
||||||
|
} => (head, tail),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
if_chain! {
|
||||||
|
if matches!(tail.kind,
|
||||||
|
ExprKind::If(_, _, None)
|
||||||
|
| ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false }));
|
||||||
|
let sm = cx.sess().source_map();
|
||||||
|
if head
|
||||||
|
.iter()
|
||||||
|
.all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
|
||||||
|
if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
|
||||||
|
then {} else { return }
|
||||||
|
}
|
||||||
|
let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
|
||||||
|
(None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
|
||||||
|
(Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
|
||||||
|
(Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
|
||||||
|
let (span, msg) = match head {
|
||||||
|
[] => return,
|
||||||
|
[stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
|
||||||
|
[a, .., b] => (
|
||||||
|
a.span.to(b.span),
|
||||||
|
"these `let` statements can also be in the `if_chain!`",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
diag.span_help(span, msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
|
||||||
|
cx.tcx
|
||||||
|
.hir()
|
||||||
|
.parent_iter(hir_id)
|
||||||
|
.find(|(_, node)| {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
!matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
|
||||||
|
})
|
||||||
|
.map_or(false, |(id, _)| {
|
||||||
|
is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
|
||||||
|
/// of the `then {..}` portion of an `if_chain!`
|
||||||
|
fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
|
||||||
|
let span = if let [stmt, ..] = stmts {
|
||||||
|
stmt.span
|
||||||
|
} else if let Some(expr) = expr {
|
||||||
|
expr.span
|
||||||
|
} else {
|
||||||
|
// empty `then {}`
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
|
||||||
|
fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
|
||||||
|
let mut span = local.pat.span;
|
||||||
|
if let Some(init) = local.init {
|
||||||
|
span = span.to(init.span);
|
||||||
|
}
|
||||||
|
span.adjust(if_chain_span.ctxt().outer_expn());
|
||||||
|
let sm = cx.sess().source_map();
|
||||||
|
let span = sm.span_extend_to_prev_str(span, "let", false);
|
||||||
|
let span = sm.span_extend_to_next_char(span, ';', false);
|
||||||
|
Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt())
|
||||||
|
}
|
||||||
|
@ -108,22 +108,21 @@ impl LateLintPass<'_> for VecInitThenPush {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if self.searcher.is_none() {
|
if_chain! {
|
||||||
if_chain! {
|
if self.searcher.is_none();
|
||||||
if !in_external_macro(cx.sess(), expr.span);
|
if !in_external_macro(cx.sess(), expr.span);
|
||||||
if let ExprKind::Assign(left, right, _) = expr.kind;
|
if let ExprKind::Assign(left, right, _) = expr.kind;
|
||||||
if let Some(id) = path_to_local(left);
|
if let Some(id) = path_to_local(left);
|
||||||
if let Some(init_kind) = get_vec_init_kind(cx, right);
|
if let Some(init_kind) = get_vec_init_kind(cx, right);
|
||||||
then {
|
then {
|
||||||
self.searcher = Some(VecPushSearcher {
|
self.searcher = Some(VecPushSearcher {
|
||||||
local_id: id,
|
local_id: id,
|
||||||
init: init_kind,
|
init: init_kind,
|
||||||
lhs_is_local: false,
|
lhs_is_local: false,
|
||||||
lhs_span: left.span,
|
lhs_span: left.span,
|
||||||
err_span: expr.span,
|
err_span: expr.span,
|
||||||
found: 0,
|
found: 0,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
92
tests/ui-internal/if_chain_style.rs
Normal file
92
tests/ui-internal/if_chain_style.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#![warn(clippy::if_chain_style)]
|
||||||
|
#![allow(clippy::no_effect)]
|
||||||
|
|
||||||
|
extern crate if_chain;
|
||||||
|
|
||||||
|
use if_chain::if_chain;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if true {
|
||||||
|
let x = "";
|
||||||
|
// `if_chain!` inside `if`
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if true;
|
||||||
|
then {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if_chain! {
|
||||||
|
if true
|
||||||
|
// multi-line AND'ed conditions
|
||||||
|
&& false;
|
||||||
|
if let Some(1) = Some(1);
|
||||||
|
// `let` before `then`
|
||||||
|
let x = "";
|
||||||
|
then {
|
||||||
|
();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if_chain! {
|
||||||
|
// single `if` condition
|
||||||
|
if true;
|
||||||
|
then {
|
||||||
|
let x = "";
|
||||||
|
// nested if
|
||||||
|
if true {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if_chain! {
|
||||||
|
// starts with `let ..`
|
||||||
|
let x = "";
|
||||||
|
if let Some(1) = Some(1);
|
||||||
|
then {
|
||||||
|
let x = "";
|
||||||
|
let x = "";
|
||||||
|
// nested if_chain!
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if true;
|
||||||
|
then {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn negative() {
|
||||||
|
if true {
|
||||||
|
();
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if true;
|
||||||
|
then { (); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
let x = "";
|
||||||
|
if true;
|
||||||
|
then { (); }
|
||||||
|
}
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if true;
|
||||||
|
then {
|
||||||
|
if true { 1 } else { 2 }
|
||||||
|
} else {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if true {
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if true;
|
||||||
|
then {}
|
||||||
|
}
|
||||||
|
} else if false {
|
||||||
|
if_chain! {
|
||||||
|
if true;
|
||||||
|
if false;
|
||||||
|
then {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
tests/ui-internal/if_chain_style.stderr
Normal file
85
tests/ui-internal/if_chain_style.stderr
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
error: this `if` can be part of the inner `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:9:5
|
||||||
|
|
|
||||||
|
LL | / if true {
|
||||||
|
LL | | let x = "";
|
||||||
|
LL | | // `if_chain!` inside `if`
|
||||||
|
LL | | if_chain! {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::if-chain-style` implied by `-D warnings`
|
||||||
|
help: this `let` statement can also be in the `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:10:9
|
||||||
|
|
|
||||||
|
LL | let x = "";
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: `if a && b;` should be `if a; if b;`
|
||||||
|
--> $DIR/if_chain_style.rs:19:12
|
||||||
|
|
|
||||||
|
LL | if true
|
||||||
|
| ____________^
|
||||||
|
LL | | // multi-line AND'ed conditions
|
||||||
|
LL | | && false;
|
||||||
|
| |____________________^
|
||||||
|
|
||||||
|
error: `let` expression should be inside `then { .. }`
|
||||||
|
--> $DIR/if_chain_style.rs:24:9
|
||||||
|
|
|
||||||
|
LL | let x = "";
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this `if` can be part of the outer `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:35:13
|
||||||
|
|
|
||||||
|
LL | if true {}
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: this `let` statement can also be in the `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:33:13
|
||||||
|
|
|
||||||
|
LL | let x = "";
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: `if_chain!` only has one `if`
|
||||||
|
--> $DIR/if_chain_style.rs:29:5
|
||||||
|
|
|
||||||
|
LL | / if_chain! {
|
||||||
|
LL | | // single `if` condition
|
||||||
|
LL | | if true;
|
||||||
|
LL | | then {
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_____^
|
||||||
|
|
|
||||||
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: `let` expression should be above the `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:40:9
|
||||||
|
|
|
||||||
|
LL | let x = "";
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this `if_chain!` can be merged with the outer `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:46:13
|
||||||
|
|
|
||||||
|
LL | / if_chain! {
|
||||||
|
LL | | if true;
|
||||||
|
LL | | if true;
|
||||||
|
LL | | then {}
|
||||||
|
LL | | }
|
||||||
|
| |_____________^
|
||||||
|
|
|
||||||
|
help: these `let` statements can also be in the `if_chain!`
|
||||||
|
--> $DIR/if_chain_style.rs:43:13
|
||||||
|
|
|
||||||
|
LL | / let x = "";
|
||||||
|
LL | | let x = "";
|
||||||
|
| |_______________________^
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user