option_option: split part of diagnostic message into help message (#15870)

changelog: [`option_option`]: improve diagnostic message
This commit is contained in:
Alex Macleod 2025-10-20 17:54:59 +00:00 committed by GitHub
commit 973e596eb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 35 deletions

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::qpath_generic_tys;
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::snippet;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, QPath};
use rustc_lint::LateContext;
@ -13,12 +14,21 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
&& let Some(arg) = qpath_generic_tys(qpath).next()
&& arg.basic_res().opt_def_id() == Some(def_id)
{
span_lint(
span_lint_and_then(
cx,
OPTION_OPTION,
hir_ty.span,
"consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
enum if you need to distinguish all 3 cases",
// use just `T` here, as the inner type is not what's problematic
"use of `Option<Option<T>>`",
|diag| {
// but use the specific type here, as:
// - this is kind of a suggestion
// - it's printed right after the linted type
let inner_opt = snippet(cx, arg.span, "_");
diag.help(format!(
"consider using `{inner_opt}`, or a custom enum if you need to distinguish all 3 cases"
));
},
);
true
} else {

View file

@ -1,52 +1,52 @@
#![deny(clippy::option_option)]
#![allow(clippy::unnecessary_wraps, clippy::manual_unwrap_or_default)]
#![warn(clippy::option_option)]
#![expect(clippy::unnecessary_wraps)]
const C: Option<Option<i32>> = None;
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
static S: Option<Option<i32>> = None;
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
fn input(_: Option<Option<u8>>) {}
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
fn output() -> Option<Option<u8>> {
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
None
}
fn output_nested() -> Vec<Option<Option<u8>>> {
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
vec![None]
}
// The lint only generates one warning for this
fn output_nested_nested() -> Option<Option<Option<u8>>> {
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if
//~^ option_option
None
}
struct Struct {
x: Option<Option<u8>>,
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum
//~^ option_option
}
impl Struct {
fn struct_fn() -> Option<Option<u8>> {
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum
//~^ option_option
None
}
}
trait Trait {
fn trait_fn() -> Option<Option<u8>>;
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum
//~^ option_option
}
enum Enum {
Tuple(Option<Option<u8>>),
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum
//~^ option_option
Struct { x: Option<Option<u8>> },
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum
//~^ option_option
}
// The lint allows this
@ -88,7 +88,7 @@ mod issue_4298 {
#[serde(default)]
#[serde(borrow)]
foo: Option<Option<Cow<'a, str>>>,
//~^ ERROR: consider using `Option<T>` instead of `Option<Option<T>>` or a custom
//~^ option_option
}
#[allow(clippy::option_option)]

View file

@ -1,80 +1,100 @@
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:4:10
|
LL | const C: Option<Option<i32>> = None;
| ^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui/option_option.rs:1:9
|
LL | #![deny(clippy::option_option)]
| ^^^^^^^^^^^^^^^^^^^^^
= help: consider using `Option<i32>`, or a custom enum if you need to distinguish all 3 cases
= note: `-D clippy::option-option` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::option_option)]`
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:6:11
|
LL | static S: Option<Option<i32>> = None;
| ^^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<i32>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:9:13
|
LL | fn input(_: Option<Option<u8>>) {}
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:12:16
|
LL | fn output() -> Option<Option<u8>> {
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:17:27
|
LL | fn output_nested() -> Vec<Option<Option<u8>>> {
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:23:30
|
LL | fn output_nested_nested() -> Option<Option<Option<u8>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<Option<u8>>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:29:8
|
LL | x: Option<Option<u8>>,
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:34:23
|
LL | fn struct_fn() -> Option<Option<u8>> {
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:41:22
|
LL | fn trait_fn() -> Option<Option<u8>>;
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:46:11
|
LL | Tuple(Option<Option<u8>>),
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:48:17
|
LL | Struct { x: Option<Option<u8>> },
| ^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<u8>`, or a custom enum if you need to distinguish all 3 cases
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
error: use of `Option<Option<T>>`
--> tests/ui/option_option.rs:90:14
|
LL | foo: Option<Option<Cow<'a, str>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `Option<Cow<'a, str>>`, or a custom enum if you need to distinguish all 3 cases
error: aborting due to 12 previous errors