-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Fix4894 Using Into
and TryInto
instead of From
and TryFrom
#6620
Changes from all commits
9a68b0c
ef88eac
2461b29
81bb1ec
5d1f388
90b86b5
5e5730b
bc9481f
d7c5152
d7822db
d0d3698
e4013af
3d8a1ea
62ab8da
b04f8a1
72e4786
c4cae20
236b82e
95c1123
867a07f
cf542a2
32d4643
04b00ea
655520f
9eea1b6
ae2b38d
94232f3
785d607
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
use crate::utils::span_lint_and_then; | ||
use crate::utils::snippet_opt; | ||
use if_chain::if_chain; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{GenericBound, WherePredicate}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
use rustc_span::symbol::sym; | ||
|
||
declare_clippy_lint! { | ||
/// **What it does:** Checking for using of From or TryFrom trait as a generic bound. | ||
/// | ||
/// **Why is this bad?** Into and TryInto are supersets of From and TryFrom. Due to | ||
/// coherence rules, sometimes From and TryFrom are forbid to implemented but Into and | ||
/// TryInto are not. So Into is a more generic bound than From, We should choose Into or | ||
/// TryInto instead of From or TryFrom. | ||
/// | ||
/// **Known problems:** None. | ||
/// | ||
/// **Example:** | ||
/// | ||
/// ```rust | ||
/// fn foo<T>(a: T) where u32: From<T> {} | ||
/// fn bar<T>(a: T) where u32: TryFrom<T> {} | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// fn foo<T>(a: T) where T: Into<u32> {} | ||
/// fn bar<T>(a: T) where T: TryInto<u32> {} | ||
/// ``` | ||
pub FROM_INSTEAD_OF_INTO, | ||
style, | ||
"Into or TryInto trait is a better choice than From or TryFrom trait as a generic bound" | ||
} | ||
|
||
declare_lint_pass!(FromInsteadOfInto => [FROM_INSTEAD_OF_INTO]); | ||
|
||
impl LateLintPass<'tcx> for FromInsteadOfInto { | ||
fn check_where_predicate(&mut self, cx: &LateContext<'tcx>, wp: &'tcx WherePredicate<'tcx>) { | ||
fn is_target_generic_bound(cx: &LateContext<'tcx>, b: &GenericBound<'_>) -> bool { | ||
if_chain! { | ||
if let Some(r) = b.trait_ref(); | ||
if let Some(def_id) = r.trait_def_id(); | ||
then { | ||
cx.tcx.is_diagnostic_item(sym::from_trait, def_id) || | ||
cx.tcx.is_diagnostic_item(sym::try_from_trait, def_id) | ||
} else { | ||
false | ||
} | ||
} | ||
} | ||
|
||
if let WherePredicate::BoundPredicate(wbp) = wp { | ||
if_chain! { | ||
let bounds = wbp.bounds; | ||
if let Some(position) = bounds.iter().position(|b| is_target_generic_bound(cx, b)); | ||
let target_bound = &bounds[position]; | ||
if let Some(tr_ref) = target_bound.trait_ref(); | ||
if let Some(def_id) = tr_ref.trait_def_id(); | ||
if let Some(last_seg) = tr_ref.path.segments.last(); | ||
if let Some(generic_arg) = last_seg.args().args.first(); | ||
if let Some(bounded_ty) = snippet_opt(cx, wbp.bounded_ty.span); | ||
if let Some(generic_arg_of_from_or_try_from) = snippet_opt(cx, generic_arg.span()); | ||
then { | ||
let replace_trait_name; | ||
let target_trait_name; | ||
if cx.tcx.is_diagnostic_item(sym::from_trait, def_id) { | ||
replace_trait_name = "Into"; | ||
target_trait_name = "From"; | ||
} else if cx.tcx.is_diagnostic_item(sym::try_from_trait, def_id) { | ||
replace_trait_name = "TryInto"; | ||
target_trait_name = "TryFrom"; | ||
} else { | ||
return; | ||
} | ||
let message = format!("{} trait is preferable than {} as a generic bound", replace_trait_name, target_trait_name); | ||
let switched_predicate = format!("{}: {}<{}>", generic_arg_of_from_or_try_from, replace_trait_name, bounded_ty); | ||
|
||
let low; | ||
if position == 0 { | ||
low = wp.span().lo(); | ||
} else { | ||
let previous_bound = &bounds[position -1]; | ||
low = previous_bound.span().hi(); | ||
} | ||
let removed_span = target_bound.span().with_lo(low); | ||
|
||
span_lint_and_then( | ||
cx, | ||
FROM_INSTEAD_OF_INTO, | ||
wp.span(), | ||
&message, | ||
|diag| { | ||
diag.span_suggestion( | ||
removed_span, | ||
&format!("remove {} bound", target_trait_name), | ||
"".to_string(), | ||
Applicability::MaybeIncorrect, | ||
); | ||
|
||
let sugg; | ||
if bounds.len() == 1 { | ||
sugg = switched_predicate; | ||
} else { | ||
sugg = format!(", {}", switched_predicate); | ||
} | ||
diag.span_suggestion( | ||
wp.span().with_lo(wp.span().hi()), | ||
"Add this bound predicate", | ||
sugg, | ||
Applicability::MaybeIncorrect, | ||
); | ||
} | ||
); | ||
} | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// run-rustfix | ||
#![allow(unused_imports)] | ||
#![allow(unused_variables)] | ||
#![allow(dead_code)] | ||
#![warn(clippy::from_instead_of_into)] | ||
use std::convert::TryFrom; | ||
use std::convert::TryInto; | ||
|
||
fn foo<T>(a: T) | ||
where | ||
T: Into<u32>, | ||
{ | ||
} | ||
|
||
fn foo1<T>(a: T) | ||
where | ||
T: Into<u32>, u32: Copy + Clone, | ||
{ | ||
} | ||
|
||
fn bar<T>(a: T) | ||
where | ||
T: TryInto<u32>, | ||
{ | ||
} | ||
|
||
fn bar1<T>(a: T) | ||
where | ||
T: TryInto<u32>, u32: Copy + Clone, | ||
{ | ||
} | ||
|
||
fn bar2<T>(a: T) | ||
where | ||
T: TryInto<u32>, u32: Copy + Clone, | ||
{ | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// run-rustfix | ||
#![allow(unused_imports)] | ||
#![allow(unused_variables)] | ||
#![allow(dead_code)] | ||
#![warn(clippy::from_instead_of_into)] | ||
use std::convert::TryFrom; | ||
use std::convert::TryInto; | ||
|
||
fn foo<T>(a: T) | ||
where | ||
u32: From<T>, | ||
xiongmao86 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
} | ||
|
||
fn foo1<T>(a: T) | ||
where | ||
u32: Copy + Clone + From<T>, | ||
{ | ||
} | ||
|
||
fn bar<T>(a: T) | ||
where | ||
u32: TryFrom<T>, | ||
{ | ||
} | ||
|
||
fn bar1<T>(a: T) | ||
where | ||
u32: Copy + TryFrom<T> + Clone, | ||
xiongmao86 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
} | ||
|
||
fn bar2<T>(a: T) | ||
where | ||
u32: TryFrom<T> + Copy + Clone, | ||
{ | ||
} | ||
|
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
error: Into trait is preferable than From as a generic bound | ||
--> $DIR/from_instead_of_into.rs:11:5 | ||
| | ||
LL | u32: From<T>, | ||
| ^^^^^^^^^^^^ | ||
| | ||
= note: `-D clippy::from-instead-of-into` implied by `-D warnings` | ||
help: remove From bound | ||
| | ||
LL | , | ||
| -- | ||
Comment on lines
+8
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably not the best diagnostic message, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I don't even know if that still compiles. I think you have to track how many bounds there are and tweak the suggestion depending on this. |
||
help: Add this bound predicate | ||
| | ||
LL | u32: From<T>T: Into<u32>, | ||
| ^^^^^^^^^^^^ | ||
|
||
Comment on lines
+12
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a mistake here. Didn't notice until now. I'll fix this. |
||
error: Into trait is preferable than From as a generic bound | ||
--> $DIR/from_instead_of_into.rs:17:5 | ||
| | ||
LL | u32: Copy + Clone + From<T>, | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
help: remove From bound | ||
| | ||
LL | u32: Copy + Clone, | ||
| -- | ||
help: Add this bound predicate | ||
| | ||
LL | u32: Copy + Clone + From<T>, T: Into<u32>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this probably should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to get the span of the |
||
| ^^^^^^^^^^^^^^ | ||
|
||
error: TryInto trait is preferable than TryFrom as a generic bound | ||
--> $DIR/from_instead_of_into.rs:23:5 | ||
| | ||
LL | u32: TryFrom<T>, | ||
| ^^^^^^^^^^^^^^^ | ||
| | ||
help: remove TryFrom bound | ||
| | ||
LL | , | ||
| -- | ||
help: Add this bound predicate | ||
| | ||
LL | u32: TryFrom<T>T: TryInto<u32>, | ||
| ^^^^^^^^^^^^^^^ | ||
|
||
error: TryInto trait is preferable than TryFrom as a generic bound | ||
--> $DIR/from_instead_of_into.rs:29:5 | ||
| | ||
LL | u32: Copy + TryFrom<T> + Clone, | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
help: remove TryFrom bound | ||
| | ||
LL | u32: Copy + Clone, | ||
| -- | ||
help: Add this bound predicate | ||
| | ||
LL | u32: Copy + TryFrom<T> + Clone, T: TryInto<u32>, | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
error: TryInto trait is preferable than TryFrom as a generic bound | ||
--> $DIR/from_instead_of_into.rs:35:5 | ||
| | ||
LL | u32: TryFrom<T> + Copy + Clone, | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
help: remove TryFrom bound | ||
| | ||
LL | + Copy + Clone, | ||
| -- | ||
help: Add this bound predicate | ||
| | ||
LL | u32: TryFrom<T> + Copy + Clone, T: TryInto<u32>, | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 5 previous errors | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I handle suggestion with this two
span_suggestion
call the diagnostic output is strange.Remove suggestion is weird because nothing show up in the span only aI have showed that below, @flip1995 .,
left. replacing suggestion is weird because the old span is used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you tell me why you suggest using multi suggestions? Do you intent to suggest usingmultispan_sugg
? If yes, why do you choose it?Are you suggesting me using
multispan_sugg
, or just using twospan_suggestion
call? The underlying intent would be different here by guess.