Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7683,6 +7683,7 @@ Released 2018-09-13
[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_join
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_lazy_evaluations
[`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_literal_bound
[`unnecessary_literal_option`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_literal_option
[`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_literal_unwrap
[`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_map_on_constructor
[`unnecessary_map_or`]: https://rust-lang.github.io/rust-clippy/main/index.html#unnecessary_map_or
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO,
crate::methods::UNNECESSARY_JOIN_INFO,
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
crate::methods::UNNECESSARY_LITERAL_OPTION_INFO,
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
crate::methods::UNNECESSARY_MAP_OR_INFO,
crate::methods::UNNECESSARY_MIN_OR_MAX_INFO,
Expand Down
41 changes: 39 additions & 2 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ mod unnecessary_get_then_check;
mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
mod unnecessary_literal_option;
mod unnecessary_literal_unwrap;
mod unnecessary_map_or;
mod unnecessary_map_or_else;
Expand Down Expand Up @@ -4527,6 +4528,31 @@ declare_clippy_lint! {
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for `is_some`, `is_none`, `or`, and `or_else` calls on a literal `Some` or `None`
/// where the result is already known.
///
/// ### Why is this bad?
/// The call does not change the result. It is either a mistake or leftover code.
///
/// ### Example
/// ```no_run
/// let _ = Some(1).is_some();
/// let _ = Some(1).or(None);
/// ```
///
/// Use instead:
/// ```no_run
/// let _ = true;
/// let _ = Some(1);
/// ```
#[clippy::version = "1.99.0"]
pub UNNECESSARY_LITERAL_OPTION,
complexity,
"using `is_some`, `is_none`, `or`, or `or_else` on a literal `Some` or `None`"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for `.unwrap()` related calls on `Result`s and `Option`s that are constructed.
Expand Down Expand Up @@ -5197,6 +5223,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_GET_THEN_CHECK,
UNNECESSARY_JOIN,
UNNECESSARY_LAZY_EVALUATIONS,
UNNECESSARY_LITERAL_OPTION,
UNNECESSARY_LITERAL_UNWRAP,
UNNECESSARY_MAP_OR,
UNNECESSARY_MIN_OR_MAX,
Expand Down Expand Up @@ -5699,8 +5726,14 @@ impl Methods {
},
(sym::is_file, []) => filetype_is_file::check(cx, expr, recv),
(sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
(sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false, self.msrv),
(sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true, self.msrv),
(sym::is_none, []) => {
check_is_some_is_none(cx, expr, recv, call_span, false, self.msrv);
unnecessary_literal_option::check(cx, expr, recv, name, args);
},
(sym::is_some, []) => {
check_is_some_is_none(cx, expr, recv, call_span, true, self.msrv);
unnecessary_literal_option::check(cx, expr, recv, name, args);
},
(sym::is_some_and, [arg]) => manual_is_variant_and::check_ok_is_some_and(cx, expr, recv, arg),
(sym::iter | sym::iter_mut | sym::into_iter, []) => {
iter_on_single_or_empty_collections::check(cx, expr, name, recv);
Expand Down Expand Up @@ -5841,10 +5874,14 @@ impl Methods {
(sym::open, [_]) => {
open_options::check(cx, expr, recv);
},
(sym::or, [_arg]) => {
unnecessary_literal_option::check(cx, expr, recv, name, args);
},
(sym::or_else, [arg]) => {
if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or", false);
}
unnecessary_literal_option::check(cx, expr, recv, name, args);
},
(sym::peek, []) => {
by_ref_peekable_peek::check(cx, expr, recv);
Expand Down
89 changes: 89 additions & 0 deletions clippy_lints/src/methods/unnecessary_literal_option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef as _, MaybeQPath as _};
use clippy_utils::{is_none_expr, last_path_segment, sym};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, AmbigArg};
use rustc_lint::LateContext;
use rustc_span::Symbol;

use super::UNNECESSARY_LITERAL_OPTION;

fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -> Option<&'a hir::Ty<'a, AmbigArg>> {
let args = args?;

if args.len() <= index {
return None;
}

match args[index] {
hir::GenericArg::Type(ty) => Some(ty),
_ => None,
}
}

pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
method: Symbol,
_args: &[hir::Expr<'_>],
) {
let init = clippy_utils::expr_or_init(cx, recv);
if init.span.from_expansion() {
// don't lint if the receiver or binding initializer comes from a macro
// (e.g. `let x = option_env!(..); x.unwrap()`)
return;
}

let (constructor, _call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
if let Some((qpath, hir_id)) = call.opt_qpath()
&& let args = last_path_segment(qpath).args.map(|args| args.args)
&& let Some(did) = cx.qpath_res(qpath, hir_id).ctor_parent(cx).opt_def_id()
{
let lang_items = cx.tcx.lang_items();
if Some(did) == lang_items.option_some_variant() {
(sym::Some, call_args, get_ty_from_args(args, 0))
} else if Some(did) == lang_items.result_ok_variant() {
(sym::Ok, call_args, get_ty_from_args(args, 0))
} else if Some(did) == lang_items.result_err_variant() {
(sym::Err, call_args, get_ty_from_args(args, 1))
} else {
return;
}
} else {
return;
}
} else if is_none_expr(cx, init) {
let call_args: &[hir::Expr<'_>] = &[];
(sym::None, call_args, None)
} else {
return;
};

let help_message = format!("used `{method}()` on `{constructor}` value");
let (suggestion_message, suggestions) = match (constructor, method, ty) {
(sym::Some, sym::is_some, _) | (sym::None, sym::is_none, _) => (
"replace this with `true`".to_string(),
Some(vec![(expr.span, "true".to_string())]),
),
(sym::Some, sym::is_none, _) | (sym::None, sym::is_some, _) => (
"replace this with `false`".to_string(),
Some(vec![(expr.span, "false".to_string())]),
),
(sym::Some, sym::or | sym::or_else, _) => (
format!("remove this `.{method}()` call"),
Some(vec![(expr.span.with_lo(recv.span.hi()), String::new())]),
),
_ => return,
};
span_lint_and_then(cx, UNNECESSARY_LITERAL_OPTION, expr.span, help_message, |diag| {
match (init.span == recv.span, suggestions) {
(true, Some(suggestions)) => {
diag.multipart_suggestion(suggestion_message, suggestions, Applicability::MachineApplicable);
},
_ => {
diag.span_help(init.span, suggestion_message);
},
}
});
}
6 changes: 5 additions & 1 deletion tests/ui/checked_unwrap/complex_conditionals_nested.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
//@no-rustfix: has placeholders
#![warn(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
#![expect(clippy::branches_sharing_code, clippy::unnecessary_literal_unwrap)]
#![expect(
clippy::branches_sharing_code,
clippy::unnecessary_literal_option,
clippy::unnecessary_literal_unwrap
)]

fn test_nested() {
fn nested() {
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/checked_unwrap/complex_conditionals_nested.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: called `unwrap` on `x` after checking its variant with `is_some`
--> tests/ui/checked_unwrap/complex_conditionals_nested.rs:9:13
--> tests/ui/checked_unwrap/complex_conditionals_nested.rs:13:13
|
LL | if x.is_some() {
| -------------- help: try: `if let Some(<item>) = x`
Expand All @@ -10,7 +10,7 @@ LL | x.unwrap();
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap)]`

error: this call to `unwrap()` will always panic
--> tests/ui/checked_unwrap/complex_conditionals_nested.rs:12:13
--> tests/ui/checked_unwrap/complex_conditionals_nested.rs:16:13
|
LL | if x.is_some() {
| ----------- because of this check
Expand Down
1 change: 1 addition & 0 deletions tests/ui/checked_unwrap/simple_conditionals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
clippy::branches_sharing_code,
clippy::if_same_then_else,
clippy::self_assignment,
clippy::unnecessary_literal_option,
clippy::unnecessary_literal_unwrap
)]

Expand Down
Loading
Loading