]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::paths::INTO; |
2 | use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; | |
3 | use if_chain::if_chain; | |
4 | use rustc_hir as hir; | |
5 | use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
6 | use rustc_semver::RustcVersion; | |
7 | use rustc_session::{declare_tool_lint, impl_lint_pass}; | |
8 | ||
9 | const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); | |
10 | ||
11 | declare_clippy_lint! { | |
12 | /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. | |
13 | /// | |
14 | /// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. | |
15 | /// | |
16 | /// **Known problems:** None. | |
17 | /// | |
18 | /// **Example:** | |
19 | /// | |
20 | /// ```rust | |
21 | /// struct StringWrapper(String); | |
22 | /// | |
23 | /// impl Into<StringWrapper> for String { | |
24 | /// fn into(self) -> StringWrapper { | |
25 | /// StringWrapper(self) | |
26 | /// } | |
27 | /// } | |
28 | /// ``` | |
29 | /// Use instead: | |
30 | /// ```rust | |
31 | /// struct StringWrapper(String); | |
32 | /// | |
33 | /// impl From<String> for StringWrapper { | |
34 | /// fn from(s: String) -> StringWrapper { | |
35 | /// StringWrapper(s) | |
36 | /// } | |
37 | /// } | |
38 | /// ``` | |
39 | pub FROM_OVER_INTO, | |
40 | style, | |
41 | "Warns on implementations of `Into<..>` to use `From<..>`" | |
42 | } | |
43 | ||
44 | pub struct FromOverInto { | |
45 | msrv: Option<RustcVersion>, | |
46 | } | |
47 | ||
48 | impl FromOverInto { | |
49 | #[must_use] | |
50 | pub fn new(msrv: Option<RustcVersion>) -> Self { | |
51 | FromOverInto { msrv } | |
52 | } | |
53 | } | |
54 | ||
55 | impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); | |
56 | ||
57 | impl LateLintPass<'_> for FromOverInto { | |
58 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { | |
59 | if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { | |
60 | return; | |
61 | } | |
62 | ||
63 | if_chain! { | |
64 | if let hir::ItemKind::Impl{ .. } = &item.kind; | |
65 | if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); | |
66 | if match_def_path(cx, impl_trait_ref.def_id, &INTO); | |
67 | ||
68 | then { | |
69 | span_lint_and_help( | |
70 | cx, | |
71 | FROM_OVER_INTO, | |
72 | cx.tcx.sess.source_map().guess_head_span(item.span), | |
73 | "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", | |
74 | None, | |
75 | "consider to implement `From` instead", | |
76 | ); | |
77 | } | |
78 | } | |
79 | } | |
80 | ||
81 | extract_msrv_attr!(LateContext); | |
82 | } |