-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #9275
- Loading branch information
Showing
16 changed files
with
356 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
use clippy_utils::{ | ||
diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg, | ||
ty::is_type_diagnostic_item, | ||
}; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
use rustc_span::sym; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// | ||
/// Checks for binary comparisons to a literal `Option::None`. | ||
/// | ||
/// ### Why is this bad? | ||
/// | ||
/// A programmer checking if some `foo` is `None` via a comparison `foo == None` | ||
/// is usually inspired from other programming languages (e.g. `foo is None` | ||
/// in Python). | ||
/// Checking if a value of type `Option<T>` is (not) equal to `None` in that | ||
/// way relies on `T: PartialEq` to do the comparison, which is unneeded. | ||
/// | ||
/// ### Example | ||
/// ```rust | ||
/// fn foo(f: Option<u32>) -> &'static str { | ||
/// if f != None { "yay" } else { "nay" } | ||
/// } | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// fn foo(f: Option<u32>) -> &'static str { | ||
/// if f.is_some() { "yay" } else { "nay" } | ||
/// } | ||
/// ``` | ||
#[clippy::version = "1.64.0"] | ||
pub PARTIALEQ_TO_NONE, | ||
style, | ||
"Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded" | ||
} | ||
declare_lint_pass!(PartialeqToNone => [PARTIALEQ_TO_NONE]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for PartialeqToNone { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | ||
// Skip expanded code, as we have no control over it anyway... | ||
if e.span.from_expansion() { | ||
return; | ||
} | ||
|
||
// If the expression is of type `Option` | ||
let is_ty_option = | ||
|expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option); | ||
|
||
// If the expression is a literal `Option::None` | ||
let is_none_ctor = |expr: &Expr<'_>| { | ||
matches!(&peel_hir_expr_refs(expr).0.kind, | ||
ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone)) | ||
}; | ||
|
||
let mut applicability = Applicability::MachineApplicable; | ||
|
||
if let ExprKind::Binary(op, left_side, right_side) = e.kind { | ||
// All other comparisons (e.g. `>= None`) have special meaning wrt T | ||
let is_eq = match op.node { | ||
BinOpKind::Eq => true, | ||
BinOpKind::Ne => false, | ||
_ => return, | ||
}; | ||
|
||
// We are only interested in comparisons between `Option` and a literal `Option::None` | ||
let scrutinee = match ( | ||
is_none_ctor(left_side) && is_ty_option(right_side), | ||
is_none_ctor(right_side) && is_ty_option(left_side), | ||
) { | ||
(true, false) => right_side, | ||
(false, true) => left_side, | ||
_ => return, | ||
}; | ||
|
||
// Peel away refs/derefs (as long as we don't cross manual deref impls), as | ||
// autoref/autoderef will take care of those | ||
let sugg = format!( | ||
"{}.{}", | ||
sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability) | ||
.maybe_par(), | ||
if is_eq { "is_none()" } else { "is_some()" } | ||
); | ||
|
||
span_lint_and_sugg( | ||
cx, | ||
PARTIALEQ_TO_NONE, | ||
e.span, | ||
"binary comparison to literal `Option::None`", | ||
if is_eq { | ||
"use `Option::is_none()` instead" | ||
} else { | ||
"use `Option::is_some()` instead" | ||
}, | ||
sugg, | ||
applicability, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// run-rustfix | ||
#![warn(clippy::partialeq_to_none)] | ||
|
||
struct Foobar; | ||
|
||
impl PartialEq<Option<()>> for Foobar { | ||
fn eq(&self, _: &Option<()>) -> bool { | ||
false | ||
} | ||
} | ||
|
||
#[allow(dead_code)] | ||
fn foo(f: Option<u32>) -> &'static str { | ||
if f.is_some() { "yay" } else { "nay" } | ||
} | ||
|
||
fn foobar() -> Option<()> { | ||
None | ||
} | ||
|
||
fn bar() -> Result<(), ()> { | ||
Ok(()) | ||
} | ||
|
||
fn optref() -> &'static &'static Option<()> { | ||
&&None | ||
} | ||
|
||
fn main() { | ||
let x = Some(0); | ||
|
||
let _ = x.is_none(); | ||
let _ = x.is_some(); | ||
let _ = x.is_none(); | ||
let _ = x.is_some(); | ||
|
||
if foobar().is_none() {} | ||
|
||
if bar().ok().is_some() {} | ||
|
||
let _ = Some(1 + 2).is_some(); | ||
|
||
let _ = { Some(0) }.is_none(); | ||
|
||
let _ = { | ||
/* | ||
This comment runs long | ||
*/ | ||
Some(1) | ||
}.is_some(); | ||
|
||
// Should not trigger, as `Foobar` is not an `Option` and has no `is_none` | ||
let _ = Foobar == None; | ||
|
||
let _ = optref().is_none(); | ||
let _ = optref().is_some(); | ||
let _ = optref().is_none(); | ||
let _ = optref().is_some(); | ||
|
||
let x = Box::new(Option::<()>::None); | ||
let _ = (*x).is_some(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// run-rustfix | ||
#![warn(clippy::partialeq_to_none)] | ||
|
||
struct Foobar; | ||
|
||
impl PartialEq<Option<()>> for Foobar { | ||
fn eq(&self, _: &Option<()>) -> bool { | ||
false | ||
} | ||
} | ||
|
||
#[allow(dead_code)] | ||
fn foo(f: Option<u32>) -> &'static str { | ||
if f != None { "yay" } else { "nay" } | ||
} | ||
|
||
fn foobar() -> Option<()> { | ||
None | ||
} | ||
|
||
fn bar() -> Result<(), ()> { | ||
Ok(()) | ||
} | ||
|
||
fn optref() -> &'static &'static Option<()> { | ||
&&None | ||
} | ||
|
||
fn main() { | ||
let x = Some(0); | ||
|
||
let _ = x == None; | ||
let _ = x != None; | ||
let _ = None == x; | ||
let _ = None != x; | ||
|
||
if foobar() == None {} | ||
|
||
if bar().ok() != None {} | ||
|
||
let _ = Some(1 + 2) != None; | ||
|
||
let _ = { Some(0) } == None; | ||
|
||
let _ = { | ||
/* | ||
This comment runs long | ||
*/ | ||
Some(1) | ||
} != None; | ||
|
||
// Should not trigger, as `Foobar` is not an `Option` and has no `is_none` | ||
let _ = Foobar == None; | ||
|
||
let _ = optref() == &&None; | ||
let _ = &&None != optref(); | ||
let _ = **optref() == None; | ||
let _ = &None != *optref(); | ||
|
||
let x = Box::new(Option::<()>::None); | ||
let _ = None != *x; | ||
} |
Oops, something went wrong.