Skip to content

Commit 92ced4a

Browse files
committed
suggest is_empty for collections when casting to bool
1 parent b8f9cb3 commit 92ced4a

File tree

5 files changed

+142
-4
lines changed

5 files changed

+142
-4
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+45-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
use super::FnCtxt;
3232

3333
use crate::type_error_struct;
34-
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
34+
use rustc_errors::{
35+
struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
36+
};
3537
use rustc_hir as hir;
3638
use rustc_macros::{TypeFoldable, TypeVisitable};
3739
use rustc_middle::mir::Mutability;
@@ -270,6 +272,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
270272
}
271273
));
272274
}
275+
276+
self.try_suggest_collection_to_bool(fcx, &mut err);
277+
273278
err.emit();
274279
}
275280
CastError::NeedViaInt => {
@@ -517,6 +522,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
517522
} else {
518523
err.span_label(self.span, "invalid cast");
519524
}
525+
526+
self.try_suggest_collection_to_bool(fcx, &mut err);
527+
520528
err.emit();
521529
}
522530
CastError::SizedUnsizedCast => {
@@ -1080,4 +1088,40 @@ impl<'a, 'tcx> CastCheck<'tcx> {
10801088
},
10811089
);
10821090
}
1091+
1092+
/// Attempt to suggest using `.is_empty` when trying to cast from a
1093+
/// collection type to a boolean.
1094+
fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
1095+
if self.cast_ty.is_bool() {
1096+
let derefed = fcx
1097+
.autoderef(self.expr_span, self.expr_ty)
1098+
.silence_errors()
1099+
.find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
1100+
1101+
if let Some((deref_ty, _)) = derefed {
1102+
// Give a note about what the expr derefs to.
1103+
if deref_ty != self.expr_ty.peel_refs() {
1104+
err.span_note(
1105+
self.expr_span,
1106+
format!(
1107+
"this expression `Deref`s to `{}` which implements `is_empty`",
1108+
fcx.ty_to_string(deref_ty)
1109+
),
1110+
);
1111+
}
1112+
1113+
// Create a multipart suggestion: add `!` and `.is_empty()` in
1114+
// place of the cast.
1115+
let suggestion = vec![
1116+
(self.expr_span.shrink_to_lo(), "!".to_string()),
1117+
(self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
1118+
];
1119+
1120+
err.multipart_suggestion_verbose(format!(
1121+
"consider using the `is_empty` method on `{}` to determine if it contains anything",
1122+
fcx.ty_to_string(self.expr_ty),
1123+
), suggestion, Applicability::MaybeIncorrect);
1124+
}
1125+
}
1126+
}
10831127
}

tests/ui/cast/cast-as-bool.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ fn main() {
22
let u = 5 as bool; //~ ERROR cannot cast as `bool`
33
//~| HELP compare with zero instead
44
//~| SUGGESTION 5 != 0
5+
56
let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
67
//~| HELP compare with zero instead
78
//~| SUGGESTION (1 + 2) != 0
8-
let v = "hello" as bool; //~ ERROR casting `&'static str` as `bool` is invalid
9+
10+
let v = "hello" as bool;
11+
//~^ ERROR casting `&'static str` as `bool` is invalid
12+
//~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
913
}

tests/ui/cast/cast-as-bool.stderr

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@ LL | let u = 5 as bool;
55
| ^^^^^^^^^ help: compare with zero instead: `5 != 0`
66

77
error[E0054]: cannot cast as `bool`
8-
--> $DIR/cast-as-bool.rs:5:13
8+
--> $DIR/cast-as-bool.rs:6:13
99
|
1010
LL | let t = (1 + 2) as bool;
1111
| ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
1212

1313
error[E0606]: casting `&'static str` as `bool` is invalid
14-
--> $DIR/cast-as-bool.rs:8:13
14+
--> $DIR/cast-as-bool.rs:10:13
1515
|
1616
LL | let v = "hello" as bool;
1717
| ^^^^^^^^^^^^^^^
18+
|
19+
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
20+
|
21+
LL | let v = !"hello".is_empty();
22+
| + ~~~~~~~~~~~
1823

1924
error: aborting due to 3 previous errors
2025

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::ops::Deref;
2+
3+
struct Foo;
4+
5+
impl Deref for Foo {
6+
type Target = [u8];
7+
8+
fn deref(&self) -> &Self::Target {
9+
&[]
10+
}
11+
}
12+
13+
fn main() {
14+
let _ = "foo" as bool;
15+
//~^ ERROR casting `&'static str` as `bool` is invalid [E0606]
16+
17+
let _ = String::from("foo") as bool;
18+
//~^ ERROR non-primitive cast: `String` as `bool` [E0605]
19+
20+
let _ = Foo as bool;
21+
//~^ ERROR non-primitive cast: `Foo` as `bool` [E0605]
22+
}
23+
24+
fn _slice(bar: &[i32]) -> bool {
25+
bar as bool
26+
//~^ ERROR casting `&[i32]` as `bool` is invalid [E0606]
27+
}
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error[E0606]: casting `&'static str` as `bool` is invalid
2+
--> $DIR/issue-106883-is-empty.rs:14:13
3+
|
4+
LL | let _ = "foo" as bool;
5+
| ^^^^^^^^^^^^^
6+
|
7+
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
8+
|
9+
LL | let _ = !"foo".is_empty();
10+
| + ~~~~~~~~~~~
11+
12+
error[E0605]: non-primitive cast: `String` as `bool`
13+
--> $DIR/issue-106883-is-empty.rs:17:13
14+
|
15+
LL | let _ = String::from("foo") as bool;
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
17+
|
18+
note: this expression `Deref`s to `str` which implements `is_empty`
19+
--> $DIR/issue-106883-is-empty.rs:17:13
20+
|
21+
LL | let _ = String::from("foo") as bool;
22+
| ^^^^^^^^^^^^^^^^^^^
23+
help: consider using the `is_empty` method on `String` to determine if it contains anything
24+
|
25+
LL | let _ = !String::from("foo").is_empty();
26+
| + ~~~~~~~~~~~
27+
28+
error[E0605]: non-primitive cast: `Foo` as `bool`
29+
--> $DIR/issue-106883-is-empty.rs:20:13
30+
|
31+
LL | let _ = Foo as bool;
32+
| ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
33+
|
34+
note: this expression `Deref`s to `[u8]` which implements `is_empty`
35+
--> $DIR/issue-106883-is-empty.rs:20:13
36+
|
37+
LL | let _ = Foo as bool;
38+
| ^^^
39+
help: consider using the `is_empty` method on `Foo` to determine if it contains anything
40+
|
41+
LL | let _ = !Foo.is_empty();
42+
| + ~~~~~~~~~~~
43+
44+
error[E0606]: casting `&[i32]` as `bool` is invalid
45+
--> $DIR/issue-106883-is-empty.rs:25:5
46+
|
47+
LL | bar as bool
48+
| ^^^^^^^^^^^
49+
|
50+
help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything
51+
|
52+
LL | !bar.is_empty()
53+
| + ~~~~~~~~~~~
54+
55+
error: aborting due to 4 previous errors
56+
57+
Some errors have detailed explanations: E0605, E0606.
58+
For more information about an error, try `rustc --explain E0605`.

0 commit comments

Comments
 (0)