Skip to content
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

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9a68b0c
cargo dev new_lint.
xiongmao86 Jan 9, 2021
ef88eac
Add test.
xiongmao86 Jan 9, 2021
2461b29
Lint declaration.
xiongmao86 Jan 9, 2021
81bb1ec
is_diagnostic_item doesn't trigger the lint.
xiongmao86 Jan 22, 2021
5d1f388
Rename lint into `into_instead_of_from`.
xiongmao86 Jan 23, 2021
90b86b5
Move if condiction from then block out.
xiongmao86 Jan 23, 2021
5e5730b
Fixed mistake made in rebase, test try_from_trait.
xiongmao86 Jan 31, 2021
bc9481f
Remove extra is diagnositc item check.
xiongmao86 Jan 31, 2021
d7c5152
Documentation.
xiongmao86 Feb 2, 2021
d7822db
Waiting to fill sugg.
xiongmao86 Feb 2, 2021
d0d3698
Emit suggestion.
xiongmao86 Feb 12, 2021
e4013af
Rename lint.
xiongmao86 Feb 12, 2021
3d8a1ea
Satisfy with test.
xiongmao86 Feb 12, 2021
62ab8da
Update lints.
xiongmao86 Feb 12, 2021
b04f8a1
cargo dev fmt.
xiongmao86 Feb 12, 2021
72e4786
Update test files after fmt.
xiongmao86 Feb 12, 2021
c4cae20
Using From trait in this test is irrelevant.
xiongmao86 Feb 13, 2021
236b82e
Suggestion may be incorrect.
xiongmao86 Feb 14, 2021
95c1123
Migrate to use find.
xiongmao86 Feb 17, 2021
867a07f
Work with multiply bounds now.
xiongmao86 Feb 17, 2021
cf542a2
Update due to review suggestion.
xiongmao86 Feb 18, 2021
32d4643
Fix wrong filter closure.
xiongmao86 Feb 18, 2021
04b00ea
Finish lint logic, add 1 more case for test.
xiongmao86 Feb 19, 2021
655520f
Satisfy with test.
xiongmao86 Feb 19, 2021
9eea1b6
Refactor and update test.
xiongmao86 Feb 19, 2021
ae2b38d
Fix workspace warning.
xiongmao86 Feb 19, 2021
94232f3
Using span_lint_and_then method to lint.
xiongmao86 Feb 23, 2021
785d607
Reference for pull request.
xiongmao86 Feb 24, 2021
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 @@ -2091,6 +2091,7 @@ Released 2018-09-13
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`from_instead_of_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_instead_of_into
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
Expand Down
119 changes: 119 additions & 0 deletions clippy_lints/src/from_instead_of_into.rs
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,
);
}
);
Comment on lines +88 to +114
Copy link
Contributor Author

@xiongmao86 xiongmao86 Feb 23, 2021

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 a , left. replacing suggestion is weird because the old span is used. I have showed that below, @flip1995 .

Copy link
Contributor Author

@xiongmao86 xiongmao86 Feb 24, 2021

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 using multispan_sugg? If yes, why do you choose it?

Are you suggesting me using multispan_sugg, or just using two span_suggestion call? The underlying intent would be different here by guess.

}
}
}
}
}
6 changes: 6 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
#![feature(control_flow_enum)]
#![feature(array_map)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
Expand Down Expand Up @@ -210,6 +211,7 @@ mod float_literal;
mod floating_point_arithmetic;
mod format;
mod formatting;
mod from_instead_of_into;
mod from_over_into;
mod from_str_radix_10;
mod functions;
Expand Down Expand Up @@ -641,6 +643,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
&formatting::SUSPICIOUS_ELSE_FORMATTING,
&formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
&from_instead_of_into::FROM_INSTEAD_OF_INTO,
&from_over_into::FROM_OVER_INTO,
&from_str_radix_10::FROM_STR_RADIX_10,
&functions::DOUBLE_MUST_USE,
Expand Down Expand Up @@ -1268,6 +1271,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
store.register_late_pass(|| box manual_map::ManualMap);
store.register_late_pass(|| box from_instead_of_into::FromInsteadOfInto);

store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
Expand Down Expand Up @@ -1482,6 +1486,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_instead_of_into::FROM_INSTEAD_OF_INTO),
LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(&functions::DOUBLE_MUST_USE),
Expand Down Expand Up @@ -1739,6 +1744,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_instead_of_into::FROM_INSTEAD_OF_INTO),
LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&from_str_radix_10::FROM_STR_RADIX_10),
LintId::of(&functions::DOUBLE_MUST_USE),
Expand Down
39 changes: 39 additions & 0 deletions tests/ui/from_instead_of_into.fixed
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() {}
39 changes: 39 additions & 0 deletions tests/ui/from_instead_of_into.rs
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() {}
78 changes: 78 additions & 0 deletions tests/ui/from_instead_of_into.stderr
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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is probably not the best diagnostic message, From<T> is removed and only comma left.

Copy link
Member

Choose a reason for hiding this comment

The 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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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>,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this probably should be u32: Copy + Clone, T: Into<u32>, but I don't know how to remove From showing in this span.

Copy link
Member

Choose a reason for hiding this comment

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

You have to get the span of the From<T> bound and extend it to the end of the span of the Clone bound with span.with_lo(clone_bound_span.hi()) And as the suggestion you can just use String::new(), which will automatically suggest removing it.

| ^^^^^^^^^^^^^^

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

1 change: 1 addition & 0 deletions tests/ui/needless_question_mark.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
Err(false)
}

#[allow(clippy::from_instead_of_into)]
fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
where
T: From<U>,
Expand Down
1 change: 1 addition & 0 deletions tests/ui/needless_question_mark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
Err(false)
}

#[allow(clippy::from_instead_of_into)]
fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
where
T: From<U>,
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/needless_question_mark.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ LL | return Ok(t.magic?);
| ^^^^^^^^^^^^ help: try: `t.magic`

error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:138:9
--> $DIR/needless_question_mark.rs:139:9
|
LL | Ok(to.magic?) // should be triggered
| ^^^^^^^^^^^^^ help: try: `to.magic`

error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:154:9
--> $DIR/needless_question_mark.rs:155:9
|
LL | Some(to.magic?) // should be triggered
| ^^^^^^^^^^^^^^^ help: try: `to.magic`

error: Question mark operator is useless here
--> $DIR/needless_question_mark.rs:162:9
--> $DIR/needless_question_mark.rs:163:9
|
LL | Ok(to.magic?) // should be triggered
| ^^^^^^^^^^^^^ help: try: `to.magic`
Expand Down