Auto merge of #12459 - y21:unconditional_recursion_from_into, r=Jarcho

lint when calling the blanket `Into` impl from a `From` impl

Closes #11150
```
warning: function cannot return without recursing
  --> x.rs:9:9
   |
9  | /         fn from(value: f32) -> Self {
10 | |             value.into()
11 | |         }
   | |_________^
   |
note: recursive call site
  --> x.rs:10:13
   |
10 |             value.into()
   |             ^^^^^^^^^^^^
```

I'm also thinking that we can probably generalize this lint to #11032 at some point (instead of hardcoding a bunch of impls), like how rustc's `unconditional_recursion` works, at least up to one indirect call, but this still seems useful for now :)

I've also noticed that we use `fn_def_id` in a bunch of lints and then try to get the node args of the call in a separate step, so I made a helper function that does both in one. I intend to refactor a bunch of uses of `fn_def_id` to use this later

I can add more test cases, but this is already using much of the same logic that exists for the other impls that this lint looks for (e.g. making sure that there are no conditional returns).

changelog: [`unconditional_recursion`]: emit a warning inside of `From::from` when unconditionally calling the blanket `.into()` impl
This commit is contained in:
bors 2024-03-13 16:32:25 +00:00
commit 73be4863f0
4 changed files with 174 additions and 85 deletions

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{expr_or_init, get_trait_def_id, path_def_id}; use clippy_utils::{expr_or_init, fn_def_id_with_node_args, path_def_id};
use rustc_ast::BinOpKind; use rustc_ast::BinOpKind;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
@ -19,11 +19,11 @@ use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks that there isn't an infinite recursion in `PartialEq` trait /// Checks that there isn't an infinite recursion in trait
/// implementation. /// implementations.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// This is a hard to find infinite recursion which will crashing any code /// This is a hard to find infinite recursion that will crash any code.
/// using it. /// using it.
/// ///
/// ### Example /// ### Example
@ -201,7 +201,6 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
} }
} }
#[allow(clippy::unnecessary_def_path)]
fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) { fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
let args = cx let args = cx
.tcx .tcx
@ -224,7 +223,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
&& let Some(trait_) = impl_.of_trait && let Some(trait_) = impl_.of_trait
&& let Some(trait_def_id) = trait_.trait_def_id() && let Some(trait_def_id) = trait_.trait_def_id()
// The trait is `ToString`. // The trait is `ToString`.
&& Some(trait_def_id) == get_trait_def_id(cx, &["alloc", "string", "ToString"]) && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id)
{ {
let is_bad = match expr.kind { let is_bad = match expr.kind {
ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => {
@ -291,7 +290,6 @@ where
self.map self.map
} }
#[allow(clippy::unnecessary_def_path)]
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if self.found_default_call { if self.found_default_call {
return; return;
@ -303,7 +301,7 @@ where
&& is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id)
&& let Some(method_def_id) = path_def_id(self.cx, f) && let Some(method_def_id) = path_def_id(self.cx, f)
&& let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id)
&& Some(trait_def_id) == get_trait_def_id(self.cx, &["core", "default", "Default"]) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id)
{ {
self.found_default_call = true; self.found_default_call = true;
span_error(self.cx, self.method_span, expr); span_error(self.cx, self.method_span, expr);
@ -312,10 +310,9 @@ where
} }
impl UnconditionalRecursion { impl UnconditionalRecursion {
#[allow(clippy::unnecessary_def_path)]
fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) { fn init_default_impl_for_type_if_needed(&mut self, cx: &LateContext<'_>) {
if self.default_impl_for_type.is_empty() if self.default_impl_for_type.is_empty()
&& let Some(default_trait_id) = get_trait_def_id(cx, &["core", "default", "Default"]) && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
{ {
let impls = cx.tcx.trait_impls_of(default_trait_id); let impls = cx.tcx.trait_impls_of(default_trait_id);
for (ty, impl_def_ids) in impls.non_blanket_impls() { for (ty, impl_def_ids) in impls.non_blanket_impls() {
@ -394,6 +391,34 @@ impl UnconditionalRecursion {
} }
} }
fn check_from(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, expr: &Expr<'_>) {
let Some(sig) = cx
.typeck_results()
.liberated_fn_sigs()
.get(cx.tcx.local_def_id_to_hir_id(method_def_id))
else {
return;
};
// Check if we are calling `Into::into` where the node args match with our `From::from` signature:
// From::from signature: fn(S1) -> S2
// <S1 as Into<S2>>::into(s1), node_args=[S1, S2]
// If they do match, then it must mean that it is the blanket impl,
// which calls back into our `From::from` again (`Into` is not specializable).
// rustc's unconditional_recursion already catches calling `From::from` directly
if let Some((fn_def_id, node_args)) = fn_def_id_with_node_args(cx, expr)
&& let [s1, s2] = **node_args
&& let (Some(s1), Some(s2)) = (s1.as_type(), s2.as_type())
&& let Some(trait_def_id) = cx.tcx.trait_of_item(fn_def_id)
&& cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)
&& get_impl_trait_def_id(cx, method_def_id) == cx.tcx.get_diagnostic_item(sym::From)
&& s1 == sig.inputs()[0]
&& s2 == sig.output()
{
span_error(cx, method_span, expr);
}
}
impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion { impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
fn check_fn( fn check_fn(
&mut self, &mut self,
@ -410,10 +435,11 @@ impl<'tcx> LateLintPass<'tcx> for UnconditionalRecursion {
// Doesn't have a conditional return. // Doesn't have a conditional return.
&& !has_conditional_return(body, expr) && !has_conditional_return(body, expr)
{ {
if name.name == sym::eq || name.name == sym::ne { match name.name {
check_partial_eq(cx, method_span, method_def_id, name, expr); sym::eq | sym::ne => check_partial_eq(cx, method_span, method_def_id, name, expr),
} else if name.name == sym::to_string { sym::to_string => check_to_string(cx, method_span, method_def_id, name, expr),
check_to_string(cx, method_span, method_def_id, name, expr); sym::from => check_from(cx, method_span, method_def_id, expr),
_ => {},
} }
self.check_default_new(cx, decl, body, method_span, method_def_id); self.check_default_new(cx, decl, body, method_span, method_def_id);
} }

View file

@ -2249,8 +2249,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
/// Returns the `DefId` of the callee if the given expression is a function or method call. /// Returns the `DefId` of the callee if the given expression is a function or method call.
pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> { pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
fn_def_id_with_node_args(cx, expr).map(|(did, _)| did)
}
/// Returns the `DefId` of the callee if the given expression is a function or method call,
/// as well as its node args.
pub fn fn_def_id_with_node_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &Expr<'_>,
) -> Option<(DefId, rustc_ty::GenericArgsRef<'tcx>)> {
let typeck = cx.typeck_results();
match &expr.kind { match &expr.kind {
ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), ExprKind::MethodCall(..) => Some((
typeck.type_dependent_def_id(expr.hir_id)?,
typeck.node_args(expr.hir_id),
)),
ExprKind::Call( ExprKind::Call(
Expr { Expr {
kind: ExprKind::Path(qpath), kind: ExprKind::Path(qpath),
@ -2262,9 +2275,9 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
// Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
// deref to fn pointers, dyn Fn, impl Fn - #8850 // deref to fn pointers, dyn Fn, impl Fn - #8850
if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
cx.typeck_results().qpath_res(qpath, *path_hir_id) typeck.qpath_res(qpath, *path_hir_id)
{ {
Some(id) Some((id, typeck.node_args(*path_hir_id)))
} else { } else {
None None
} }

View file

@ -1,7 +1,11 @@
//@no-rustfix //@no-rustfix
#![warn(clippy::unconditional_recursion)] #![warn(clippy::unconditional_recursion)]
#![allow(clippy::partialeq_ne_impl, clippy::default_constructed_unit_structs)] #![allow(
clippy::partialeq_ne_impl,
clippy::default_constructed_unit_structs,
clippy::only_used_in_recursion
)]
enum Foo { enum Foo {
A, A,
@ -350,4 +354,48 @@ mod issue12154 {
} }
} }
// From::from -> Into::into -> From::from
struct BadFromTy1<'a>(&'a ());
struct BadIntoTy1<'b>(&'b ());
impl<'a> From<BadFromTy1<'a>> for BadIntoTy1<'static> {
fn from(f: BadFromTy1<'a>) -> Self {
f.into()
}
}
// Using UFCS syntax
struct BadFromTy2<'a>(&'a ());
struct BadIntoTy2<'b>(&'b ());
impl<'a> From<BadFromTy2<'a>> for BadIntoTy2<'static> {
fn from(f: BadFromTy2<'a>) -> Self {
Into::into(f)
}
}
// Different Into impl (<i16 as Into<i32>>), so no infinite recursion
struct BadFromTy3;
impl From<BadFromTy3> for i32 {
fn from(f: BadFromTy3) -> Self {
Into::into(1i16)
}
}
// A conditional return that ends the recursion
struct BadFromTy4;
impl From<BadFromTy4> for i32 {
fn from(f: BadFromTy4) -> Self {
if true {
return 42;
}
f.into()
}
}
// Types differ in refs, don't lint
impl From<&BadFromTy4> for i32 {
fn from(f: &BadFromTy4) -> Self {
BadFromTy4.into()
}
}
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:42:5 --> tests/ui/unconditional_recursion.rs:46:5
| |
LL | fn ne(&self, other: &Self) -> bool { LL | fn ne(&self, other: &Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@ -12,7 +12,7 @@ LL | self.ne(other)
= help: to override `-D warnings` add `#[allow(unconditional_recursion)]` = help: to override `-D warnings` add `#[allow(unconditional_recursion)]`
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:46:5 --> tests/ui/unconditional_recursion.rs:50:5
| |
LL | fn eq(&self, other: &Self) -> bool { LL | fn eq(&self, other: &Self) -> bool {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@ -23,7 +23,7 @@ LL | self.eq(other)
= help: a `loop` may express intention better if this is on purpose = help: a `loop` may express intention better if this is on purpose
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:211:5 --> tests/ui/unconditional_recursion.rs:215:5
| |
LL | fn to_string(&self) -> String { LL | fn to_string(&self) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@ -34,7 +34,7 @@ LL | self.to_string()
= help: a `loop` may express intention better if this is on purpose = help: a `loop` may express intention better if this is on purpose
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:221:5 --> tests/ui/unconditional_recursion.rs:225:5
| |
LL | fn to_string(&self) -> String { LL | fn to_string(&self) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@ -45,7 +45,7 @@ LL | x.to_string()
= help: a `loop` may express intention better if this is on purpose = help: a `loop` may express intention better if this is on purpose
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:232:5 --> tests/ui/unconditional_recursion.rs:236:5
| |
LL | fn to_string(&self) -> String { LL | fn to_string(&self) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@ -56,7 +56,7 @@ LL | (self as &Self).to_string()
= help: a `loop` may express intention better if this is on purpose = help: a `loop` may express intention better if this is on purpose
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:12:5 --> tests/ui/unconditional_recursion.rs:16:5
| |
LL | / fn ne(&self, other: &Self) -> bool { LL | / fn ne(&self, other: &Self) -> bool {
LL | | LL | |
@ -65,7 +65,7 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:14:9 --> tests/ui/unconditional_recursion.rs:18:9
| |
LL | self != other LL | self != other
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -73,7 +73,7 @@ LL | self != other
= help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]` = help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]`
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:16:5 --> tests/ui/unconditional_recursion.rs:20:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -82,13 +82,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:18:9 --> tests/ui/unconditional_recursion.rs:22:9
| |
LL | self == other LL | self == other
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:28:5 --> tests/ui/unconditional_recursion.rs:32:5
| |
LL | / fn ne(&self, other: &Self) -> bool { LL | / fn ne(&self, other: &Self) -> bool {
LL | | self != &Foo2::B // no error here LL | | self != &Foo2::B // no error here
@ -96,13 +96,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:29:9 --> tests/ui/unconditional_recursion.rs:33:9
| |
LL | self != &Foo2::B // no error here LL | self != &Foo2::B // no error here
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:31:5 --> tests/ui/unconditional_recursion.rs:35:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | self == &Foo2::B // no error here LL | | self == &Foo2::B // no error here
@ -110,13 +110,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:32:9 --> tests/ui/unconditional_recursion.rs:36:9
| |
LL | self == &Foo2::B // no error here LL | self == &Foo2::B // no error here
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:42:5 --> tests/ui/unconditional_recursion.rs:46:5
| |
LL | / fn ne(&self, other: &Self) -> bool { LL | / fn ne(&self, other: &Self) -> bool {
LL | | LL | |
@ -125,27 +125,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:44:9 --> tests/ui/unconditional_recursion.rs:48:9
| |
LL | self.ne(other) LL | self.ne(other)
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: parameter is only used in recursion
--> tests/ui/unconditional_recursion.rs:42:18
|
LL | fn ne(&self, other: &Self) -> bool {
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_other`
|
note: parameter used here
--> tests/ui/unconditional_recursion.rs:44:17
|
LL | self.ne(other)
| ^^^^^
= note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::only_used_in_recursion)]`
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:46:5 --> tests/ui/unconditional_recursion.rs:50:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -154,25 +140,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:48:9 --> tests/ui/unconditional_recursion.rs:52:9
| |
LL | self.eq(other) LL | self.eq(other)
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: parameter is only used in recursion
--> tests/ui/unconditional_recursion.rs:46:18
|
LL | fn eq(&self, other: &Self) -> bool {
| ^^^^^ help: if this is intentional, prefix it with an underscore: `_other`
|
note: parameter used here
--> tests/ui/unconditional_recursion.rs:48:17
|
LL | self.eq(other)
| ^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:90:5 --> tests/ui/unconditional_recursion.rs:94:5
| |
LL | / fn ne(&self, other: &Self) -> bool { LL | / fn ne(&self, other: &Self) -> bool {
LL | | LL | |
@ -181,13 +155,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:92:9 --> tests/ui/unconditional_recursion.rs:96:9
| |
LL | other != self LL | other != self
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:94:5 --> tests/ui/unconditional_recursion.rs:98:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -196,13 +170,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:96:9 --> tests/ui/unconditional_recursion.rs:100:9
| |
LL | other == self LL | other == self
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:104:5 --> tests/ui/unconditional_recursion.rs:108:5
| |
LL | / fn ne(&self, other: &Self) -> bool { LL | / fn ne(&self, other: &Self) -> bool {
LL | | LL | |
@ -211,13 +185,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:106:9 --> tests/ui/unconditional_recursion.rs:110:9
| |
LL | other != other LL | other != other
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `!=` error: equal expressions as operands to `!=`
--> tests/ui/unconditional_recursion.rs:106:9 --> tests/ui/unconditional_recursion.rs:110:9
| |
LL | other != other LL | other != other
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
@ -225,7 +199,7 @@ LL | other != other
= note: `#[deny(clippy::eq_op)]` on by default = note: `#[deny(clippy::eq_op)]` on by default
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:108:5 --> tests/ui/unconditional_recursion.rs:112:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -234,19 +208,19 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:110:9 --> tests/ui/unconditional_recursion.rs:114:9
| |
LL | other == other LL | other == other
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> tests/ui/unconditional_recursion.rs:110:9 --> tests/ui/unconditional_recursion.rs:114:9
| |
LL | other == other LL | other == other
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:117:5 --> tests/ui/unconditional_recursion.rs:121:5
| |
LL | / fn ne(&self, _other: &Self) -> bool { LL | / fn ne(&self, _other: &Self) -> bool {
LL | | LL | |
@ -255,19 +229,19 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:119:9 --> tests/ui/unconditional_recursion.rs:123:9
| |
LL | self != self LL | self != self
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `!=` error: equal expressions as operands to `!=`
--> tests/ui/unconditional_recursion.rs:119:9 --> tests/ui/unconditional_recursion.rs:123:9
| |
LL | self != self LL | self != self
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:121:5 --> tests/ui/unconditional_recursion.rs:125:5
| |
LL | / fn eq(&self, _other: &Self) -> bool { LL | / fn eq(&self, _other: &Self) -> bool {
LL | | LL | |
@ -276,19 +250,19 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:123:9 --> tests/ui/unconditional_recursion.rs:127:9
| |
LL | self == self LL | self == self
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: equal expressions as operands to `==` error: equal expressions as operands to `==`
--> tests/ui/unconditional_recursion.rs:123:9 --> tests/ui/unconditional_recursion.rs:127:9
| |
LL | self == self LL | self == self
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:149:13 --> tests/ui/unconditional_recursion.rs:153:13
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -300,7 +274,7 @@ LL | impl_partial_eq!(S5);
| -------------------- in this macro invocation | -------------------- in this macro invocation
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:151:17 --> tests/ui/unconditional_recursion.rs:155:17
| |
LL | self == other LL | self == other
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
@ -310,7 +284,7 @@ LL | impl_partial_eq!(S5);
= note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:178:5 --> tests/ui/unconditional_recursion.rs:182:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -321,13 +295,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:182:9 --> tests/ui/unconditional_recursion.rs:186:9
| |
LL | mine == theirs LL | mine == theirs
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:247:5 --> tests/ui/unconditional_recursion.rs:251:5
| |
LL | / fn new() -> Self { LL | / fn new() -> Self {
LL | | LL | |
@ -336,13 +310,13 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:249:9 --> tests/ui/unconditional_recursion.rs:253:9
| |
LL | Self::default() LL | Self::default()
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: function cannot return without recursing error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:286:5 --> tests/ui/unconditional_recursion.rs:290:5
| |
LL | / fn eq(&self, other: &Self) -> bool { LL | / fn eq(&self, other: &Self) -> bool {
LL | | LL | |
@ -353,10 +327,38 @@ LL | | }
| |_____^ | |_____^
| |
note: recursive call site note: recursive call site
--> tests/ui/unconditional_recursion.rs:290:9 --> tests/ui/unconditional_recursion.rs:294:9
| |
LL | mine.eq(theirs) LL | mine.eq(theirs)
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:361:5
|
LL | / fn from(f: BadFromTy1<'a>) -> Self {
LL | | f.into()
LL | | }
| |_____^
|
note: recursive call site
--> tests/ui/unconditional_recursion.rs:362:9
|
LL | f.into()
| ^^^^^^^^
error: function cannot return without recursing
--> tests/ui/unconditional_recursion.rs:370:5
|
LL | / fn from(f: BadFromTy2<'a>) -> Self {
LL | | Into::into(f)
LL | | }
| |_____^
|
note: recursive call site
--> tests/ui/unconditional_recursion.rs:371:9
|
LL | Into::into(f)
| ^^^^^^^^^^^^^
error: aborting due to 27 previous errors error: aborting due to 27 previous errors