Skip to content

New lint [needless_return_with_question_mark] #11031

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 24, 2023
Merged
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 @@ -5093,6 +5093,7 @@ Released 2018-09-13
[`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes
[`needless_raw_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_strings
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
[`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
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 @@ -569,6 +569,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
crate::returns::LET_AND_RETURN_INFO,
crate::returns::NEEDLESS_RETURN_INFO,
crate::returns::NEEDLESS_RETURN_WITH_QUESTION_MARK_INFO,
crate::same_name_method::SAME_NAME_METHOD_INFO,
crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO,
Expand Down
76 changes: 72 additions & 4 deletions clippy_lints/src/returns.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi};
use clippy_utils::{fn_def_id, is_from_proc_macro, path_to_local_id, span_find_starting_semi};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
use rustc_hir::{
Block, Body, Expr, ExprKind, FnDecl, ItemKind, LangItem, MatchSource, OwnerNode, PatKind, QPath, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
Expand Down Expand Up @@ -77,6 +79,46 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for return statements on `Err` paired with the `?` operator.
///
/// ### Why is this bad?
/// The `return` is unnecessary.
///
/// ### Example
/// ```rust,ignore
/// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
/// if x == 0 {
/// return Err(...)?;
/// }
/// Ok(())
/// }
/// ```
/// simplify to
/// ```rust,ignore
/// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
/// if x == 0 {
/// Err(...)?;
/// }
/// Ok(())
/// }
/// ```
/// if paired with `try_err`, use instead:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it would be better to change suggestions by using is_lint_allowed when warning try_err, but it can be done in another pull request.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably move around try_err then, probably to returns instead of matches, that way we can reuse the logic. But I'm ok with this.

/// ```rust,ignore
/// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
/// if x == 0 {
/// return Err(...);
/// }
/// Ok(())
/// }
/// ```
#[clippy::version = "1.73.0"]
pub NEEDLESS_RETURN_WITH_QUESTION_MARK,
style,
"using a return statement like `return Err(expr)?;` where removing it would suffice"
}

#[derive(PartialEq, Eq)]
enum RetReplacement<'tcx> {
Empty,
Expand Down Expand Up @@ -116,9 +158,35 @@ impl<'tcx> ToString for RetReplacement<'tcx> {
}
}

declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]);

impl<'tcx> LateLintPass<'tcx> for Return {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !in_external_macro(cx.sess(), stmt.span)
&& let StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::Ret(Some(ret)) = expr.kind
&& let ExprKind::Match(.., MatchSource::TryDesugar) = ret.kind
// Ensure this is not the final stmt, otherwise removing it would cause a compile error
&& let OwnerNode::Item(item) = cx.tcx.hir().owner(cx.tcx.hir().get_parent_item(expr.hir_id))
&& let ItemKind::Fn(_, _, body) = item.kind
&& let block = cx.tcx.hir().body(body).value
&& let ExprKind::Block(block, _) = block.kind
&& let [.., final_stmt] = block.stmts
&& final_stmt.hir_id != stmt.hir_id
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_sugg(
cx,
NEEDLESS_RETURN_WITH_QUESTION_MARK,
expr.span.until(ret.span),
"unneeded `return` statement with `?` operator",
"remove it",
String::new(),
Applicability::MachineApplicable,
);
}
}

fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
// we need both a let-binding stmt and an expr
if_chain! {
Expand Down
40 changes: 40 additions & 0 deletions tests/ui/needless_return_with_question_mark.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//@run-rustfix
//@aux-build:proc_macros.rs:proc-macro
#![allow(
clippy::needless_return,
clippy::no_effect,
clippy::unit_arg,
clippy::useless_conversion,
unused
)]

#[macro_use]
extern crate proc_macros;

fn a() -> u32 {
return 0;
}

fn b() -> Result<u32, u32> {
return Err(0);
}

// Do not lint
fn c() -> Option<()> {
return None?;
}

fn main() -> Result<(), ()> {
Err(())?;
return Ok::<(), ()>(());
Err(())?;
Ok::<(), ()>(());
return Err(().into());
external! {
return Err(())?;
}
with_span! {
return Err(())?;
}
Err(())
}
40 changes: 40 additions & 0 deletions tests/ui/needless_return_with_question_mark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//@run-rustfix
//@aux-build:proc_macros.rs:proc-macro
#![allow(
clippy::needless_return,
clippy::no_effect,
clippy::unit_arg,
clippy::useless_conversion,
unused
)]

#[macro_use]
extern crate proc_macros;

fn a() -> u32 {
return 0;
}

fn b() -> Result<u32, u32> {
return Err(0);
}

// Do not lint
fn c() -> Option<()> {
return None?;
}

fn main() -> Result<(), ()> {
return Err(())?;
return Ok::<(), ()>(());
Err(())?;
Ok::<(), ()>(());
return Err(().into());
external! {
return Err(())?;
}
with_span! {
return Err(())?;
}
Err(())
}
10 changes: 10 additions & 0 deletions tests/ui/needless_return_with_question_mark.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: unneeded `return` statement with `?` operator
--> $DIR/needless_return_with_question_mark.rs:28:5
|
LL | return Err(())?;
| ^^^^^^^ help: remove it
|
= note: `-D clippy::needless-return-with-question-mark` implied by `-D warnings`

error: aborting due to previous error

6 changes: 5 additions & 1 deletion tests/ui/try_err.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
//@aux-build:proc_macros.rs:proc-macro

#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
#![allow(
clippy::unnecessary_wraps,
clippy::needless_question_mark,
clippy::needless_return_with_question_mark
)]

extern crate proc_macros;
use proc_macros::{external, inline_macros};
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/try_err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
//@aux-build:proc_macros.rs:proc-macro

#![deny(clippy::try_err)]
#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)]
#![allow(
clippy::unnecessary_wraps,
clippy::needless_question_mark,
clippy::needless_return_with_question_mark
)]

extern crate proc_macros;
use proc_macros::{external, inline_macros};
Expand Down
22 changes: 11 additions & 11 deletions tests/ui/try_err.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:19:9
--> $DIR/try_err.rs:23:9
|
LL | Err(err)?;
| ^^^^^^^^^ help: try: `return Err(err)`
Expand All @@ -11,65 +11,65 @@ LL | #![deny(clippy::try_err)]
| ^^^^^^^^^^^^^^^

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:29:9
--> $DIR/try_err.rs:33:9
|
LL | Err(err)?;
| ^^^^^^^^^ help: try: `return Err(err.into())`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:49:17
--> $DIR/try_err.rs:53:17
|
LL | Err(err)?;
| ^^^^^^^^^ help: try: `return Err(err)`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:68:17
--> $DIR/try_err.rs:72:17
|
LL | Err(err)?;
| ^^^^^^^^^ help: try: `return Err(err.into())`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:88:23
--> $DIR/try_err.rs:92:23
|
LL | Err(_) => Err(1)?,
| ^^^^^^^ help: try: `return Err(1)`
|
= note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info)

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:95:23
--> $DIR/try_err.rs:99:23
|
LL | Err(_) => Err(inline!(1))?,
| ^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(1))`
|
= note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info)

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:122:9
--> $DIR/try_err.rs:126:9
|
LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:129:9
--> $DIR/try_err.rs:133:9
|
LL | Err(io::ErrorKind::WriteZero)?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:131:9
--> $DIR/try_err.rs:135:9
|
LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:139:9
--> $DIR/try_err.rs:143:9
|
LL | Err(io::ErrorKind::NotFound)?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))`

error: returning an `Err(_)` with the `?` operator
--> $DIR/try_err.rs:148:16
--> $DIR/try_err.rs:152:16
|
LL | return Err(42)?;
| ^^^^^^^^ help: try: `Err(42)`
Expand Down