forked from rust-lang/rust
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#11012 - Centri3:manual_try_fold, r=blyxyas,xF…
…rednet New lint [`manual_try_fold`] Closes rust-lang#10208 --- changelog: New lint [`manual_try_fold`] [rust-lang#11012](rust-lang/rust-clippy#11012)
- Loading branch information
Showing
9 changed files
with
224 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use clippy_utils::{ | ||
diagnostics::span_lint_and_sugg, | ||
is_from_proc_macro, | ||
msrvs::{Msrv, ITERATOR_TRY_FOLD}, | ||
source::snippet_opt, | ||
ty::implements_trait, | ||
}; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{ | ||
def::{DefKind, Res}, | ||
Expr, ExprKind, | ||
}; | ||
use rustc_lint::{LateContext, LintContext}; | ||
use rustc_middle::lint::in_external_macro; | ||
use rustc_span::Span; | ||
|
||
use super::MANUAL_TRY_FOLD; | ||
|
||
pub(super) fn check<'tcx>( | ||
cx: &LateContext<'tcx>, | ||
expr: &Expr<'tcx>, | ||
init: &Expr<'_>, | ||
acc: &Expr<'_>, | ||
fold_span: Span, | ||
msrv: &Msrv, | ||
) { | ||
if !in_external_macro(cx.sess(), fold_span) | ||
&& msrv.meets(ITERATOR_TRY_FOLD) | ||
&& let init_ty = cx.typeck_results().expr_ty(init) | ||
&& let Some(try_trait) = cx.tcx.lang_items().try_trait() | ||
&& implements_trait(cx, init_ty, try_trait, &[]) | ||
&& let ExprKind::Call(path, [first, rest @ ..]) = init.kind | ||
&& let ExprKind::Path(qpath) = path.kind | ||
&& let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id) | ||
&& let ExprKind::Closure(closure) = acc.kind | ||
&& !is_from_proc_macro(cx, expr) | ||
&& let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span)) | ||
{ | ||
let init_snip = rest | ||
.is_empty() | ||
.then_some(first.span) | ||
.and_then(|span| snippet_opt(cx, span)) | ||
.unwrap_or("...".to_owned()); | ||
|
||
span_lint_and_sugg( | ||
cx, | ||
MANUAL_TRY_FOLD, | ||
fold_span, | ||
"usage of `Iterator::fold` on a type that implements `Try`", | ||
"use `try_fold` instead", | ||
format!("try_fold({init_snip}, {args_snip} ...)", ), | ||
Applicability::HasPlaceholders, | ||
); | ||
} | ||
} |
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,100 @@ | ||
//@aux-build:proc_macros.rs:proc-macro | ||
#![allow(clippy::unnecessary_fold, unused)] | ||
#![warn(clippy::manual_try_fold)] | ||
#![feature(try_trait_v2)] | ||
|
||
use std::ops::ControlFlow; | ||
use std::ops::FromResidual; | ||
use std::ops::Try; | ||
|
||
#[macro_use] | ||
extern crate proc_macros; | ||
|
||
// Test custom `Try` with more than 1 argument | ||
struct NotOption(i32, i32); | ||
|
||
impl<R> FromResidual<R> for NotOption { | ||
fn from_residual(_: R) -> Self { | ||
todo!() | ||
} | ||
} | ||
|
||
impl Try for NotOption { | ||
type Output = (); | ||
type Residual = (); | ||
|
||
fn from_output(_: Self::Output) -> Self { | ||
todo!() | ||
} | ||
|
||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { | ||
todo!() | ||
} | ||
} | ||
|
||
// Test custom `Try` with only 1 argument | ||
#[derive(Default)] | ||
struct NotOptionButWorse(i32); | ||
|
||
impl<R> FromResidual<R> for NotOptionButWorse { | ||
fn from_residual(_: R) -> Self { | ||
todo!() | ||
} | ||
} | ||
|
||
impl Try for NotOptionButWorse { | ||
type Output = (); | ||
type Residual = (); | ||
|
||
fn from_output(_: Self::Output) -> Self { | ||
todo!() | ||
} | ||
|
||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> { | ||
todo!() | ||
} | ||
} | ||
|
||
fn main() { | ||
[1, 2, 3] | ||
.iter() | ||
.fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ||
.unwrap(); | ||
[1, 2, 3] | ||
.iter() | ||
.fold(NotOption(0i32, 0i32), |sum, i| NotOption(0i32, 0i32)); | ||
[1, 2, 3] | ||
.iter() | ||
.fold(NotOptionButWorse(0i32), |sum, i| NotOptionButWorse(0i32)); | ||
// Do not lint | ||
[1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap(); | ||
[1, 2, 3].iter().fold(0i32, |sum, i| sum + i); | ||
[1, 2, 3] | ||
.iter() | ||
.fold(NotOptionButWorse::default(), |sum, i| NotOptionButWorse::default()); | ||
external! { | ||
[1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i)).unwrap(); | ||
[1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap(); | ||
} | ||
with_span! { | ||
span | ||
[1, 2, 3].iter().fold(Some(0i32), |sum, i| sum?.checked_add(*i)).unwrap(); | ||
[1, 2, 3].iter().try_fold(0i32, |sum, i| sum.checked_add(*i)).unwrap(); | ||
} | ||
} | ||
|
||
#[clippy::msrv = "1.26.0"] | ||
fn msrv_too_low() { | ||
[1, 2, 3] | ||
.iter() | ||
.fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ||
.unwrap(); | ||
} | ||
|
||
#[clippy::msrv = "1.27.0"] | ||
fn msrv_juust_right() { | ||
[1, 2, 3] | ||
.iter() | ||
.fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ||
.unwrap(); | ||
} |
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,28 @@ | ||
error: usage of `Iterator::fold` on a type that implements `Try` | ||
--> $DIR/manual_try_fold.rs:61:10 | ||
| | ||
LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` | ||
| | ||
= note: `-D clippy::manual-try-fold` implied by `-D warnings` | ||
|
||
error: usage of `Iterator::fold` on a type that implements `Try` | ||
--> $DIR/manual_try_fold.rs:65:10 | ||
| | ||
LL | .fold(NotOption(0i32, 0i32), |sum, i| NotOption(0i32, 0i32)); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(..., |sum, i| ...)` | ||
|
||
error: usage of `Iterator::fold` on a type that implements `Try` | ||
--> $DIR/manual_try_fold.rs:68:10 | ||
| | ||
LL | .fold(NotOptionButWorse(0i32), |sum, i| NotOptionButWorse(0i32)); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` | ||
|
||
error: usage of `Iterator::fold` on a type that implements `Try` | ||
--> $DIR/manual_try_fold.rs:98:10 | ||
| | ||
LL | .fold(Some(0i32), |sum, i| sum?.checked_add(*i)) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `try_fold` instead: `try_fold(0i32, |sum, i| ...)` | ||
|
||
error: aborting due to 4 previous errors | ||
|