summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:03:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:03:36 +0000
commit17d40c6057c88f4c432b0d7bac88e1b84cb7e67f (patch)
tree3f66c4a5918660bb8a758ab6cda5ff8ee4f6cdcd /src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
parentAdding upstream version 1.64.0+dfsg1. (diff)
downloadrustc-f7f0cc2a5d72e2c61c1f6900e70eec992bea4273.tar.xz
rustc-f7f0cc2a5d72e2c61c1f6900e70eec992bea4273.zip
Adding upstream version 1.65.0+dfsg1.upstream/1.65.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/partialeq_to_none.rs')
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_to_none.rs105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
new file mode 100644
index 000000000..000b0ba7a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
@@ -0,0 +1,105 @@
+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<'_>| {
+ !expr.span.from_expansion()
+ && 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,
+ );
+ }
+ }
+}