mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 16:54:01 +00:00
Rollup merge of #106896 - Ezrashaw:str-cast-bool-emptyness, r=compiler-errors
suggest `is_empty` for collections when casting to `bool` Fixes #106883 Matches on slices, `String` and `str`. It would be nice to do this with something like `Deref<Target=str>` as well, but AFAIK it's not possible in this part of the compiler.
This commit is contained in:
commit
5610231454
@ -31,7 +31,9 @@
|
||||
use super::FnCtxt;
|
||||
|
||||
use crate::type_error_struct;
|
||||
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_errors::{
|
||||
struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_macros::{TypeFoldable, TypeVisitable};
|
||||
use rustc_middle::mir::Mutability;
|
||||
@ -270,6 +272,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
self.try_suggest_collection_to_bool(fcx, &mut err);
|
||||
|
||||
err.emit();
|
||||
}
|
||||
CastError::NeedViaInt => {
|
||||
@ -517,6 +522,9 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
} else {
|
||||
err.span_label(self.span, "invalid cast");
|
||||
}
|
||||
|
||||
self.try_suggest_collection_to_bool(fcx, &mut err);
|
||||
|
||||
err.emit();
|
||||
}
|
||||
CastError::SizedUnsizedCast => {
|
||||
@ -1080,4 +1088,40 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Attempt to suggest using `.is_empty` when trying to cast from a
|
||||
/// collection type to a boolean.
|
||||
fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
|
||||
if self.cast_ty.is_bool() {
|
||||
let derefed = fcx
|
||||
.autoderef(self.expr_span, self.expr_ty)
|
||||
.silence_errors()
|
||||
.find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
|
||||
|
||||
if let Some((deref_ty, _)) = derefed {
|
||||
// Give a note about what the expr derefs to.
|
||||
if deref_ty != self.expr_ty.peel_refs() {
|
||||
err.span_note(
|
||||
self.expr_span,
|
||||
format!(
|
||||
"this expression `Deref`s to `{}` which implements `is_empty`",
|
||||
fcx.ty_to_string(deref_ty)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Create a multipart suggestion: add `!` and `.is_empty()` in
|
||||
// place of the cast.
|
||||
let suggestion = vec![
|
||||
(self.expr_span.shrink_to_lo(), "!".to_string()),
|
||||
(self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
|
||||
];
|
||||
|
||||
err.multipart_suggestion_verbose(format!(
|
||||
"consider using the `is_empty` method on `{}` to determine if it contains anything",
|
||||
fcx.ty_to_string(self.expr_ty),
|
||||
), suggestion, Applicability::MaybeIncorrect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,12 @@ fn main() {
|
||||
let u = 5 as bool; //~ ERROR cannot cast as `bool`
|
||||
//~| HELP compare with zero instead
|
||||
//~| SUGGESTION 5 != 0
|
||||
|
||||
let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
|
||||
//~| HELP compare with zero instead
|
||||
//~| SUGGESTION (1 + 2) != 0
|
||||
let v = "hello" as bool; //~ ERROR casting `&'static str` as `bool` is invalid
|
||||
|
||||
let v = "hello" as bool;
|
||||
//~^ ERROR casting `&'static str` as `bool` is invalid
|
||||
//~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
|
||||
}
|
||||
|
@ -5,16 +5,21 @@ LL | let u = 5 as bool;
|
||||
| ^^^^^^^^^ help: compare with zero instead: `5 != 0`
|
||||
|
||||
error[E0054]: cannot cast as `bool`
|
||||
--> $DIR/cast-as-bool.rs:5:13
|
||||
--> $DIR/cast-as-bool.rs:6:13
|
||||
|
|
||||
LL | let t = (1 + 2) as bool;
|
||||
| ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
|
||||
|
||||
error[E0606]: casting `&'static str` as `bool` is invalid
|
||||
--> $DIR/cast-as-bool.rs:8:13
|
||||
--> $DIR/cast-as-bool.rs:10:13
|
||||
|
|
||||
LL | let v = "hello" as bool;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
|
||||
|
|
||||
LL | let v = !"hello".is_empty();
|
||||
| + ~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
27
tests/ui/cast/issue-106883-is-empty.rs
Normal file
27
tests/ui/cast/issue-106883-is-empty.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Deref for Foo {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = "foo" as bool;
|
||||
//~^ ERROR casting `&'static str` as `bool` is invalid [E0606]
|
||||
|
||||
let _ = String::from("foo") as bool;
|
||||
//~^ ERROR non-primitive cast: `String` as `bool` [E0605]
|
||||
|
||||
let _ = Foo as bool;
|
||||
//~^ ERROR non-primitive cast: `Foo` as `bool` [E0605]
|
||||
}
|
||||
|
||||
fn _slice(bar: &[i32]) -> bool {
|
||||
bar as bool
|
||||
//~^ ERROR casting `&[i32]` as `bool` is invalid [E0606]
|
||||
}
|
58
tests/ui/cast/issue-106883-is-empty.stderr
Normal file
58
tests/ui/cast/issue-106883-is-empty.stderr
Normal file
@ -0,0 +1,58 @@
|
||||
error[E0606]: casting `&'static str` as `bool` is invalid
|
||||
--> $DIR/issue-106883-is-empty.rs:14:13
|
||||
|
|
||||
LL | let _ = "foo" as bool;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
|
||||
|
|
||||
LL | let _ = !"foo".is_empty();
|
||||
| + ~~~~~~~~~~~
|
||||
|
||||
error[E0605]: non-primitive cast: `String` as `bool`
|
||||
--> $DIR/issue-106883-is-empty.rs:17:13
|
||||
|
|
||||
LL | let _ = String::from("foo") as bool;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
||||
|
|
||||
note: this expression `Deref`s to `str` which implements `is_empty`
|
||||
--> $DIR/issue-106883-is-empty.rs:17:13
|
||||
|
|
||||
LL | let _ = String::from("foo") as bool;
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
help: consider using the `is_empty` method on `String` to determine if it contains anything
|
||||
|
|
||||
LL | let _ = !String::from("foo").is_empty();
|
||||
| + ~~~~~~~~~~~
|
||||
|
||||
error[E0605]: non-primitive cast: `Foo` as `bool`
|
||||
--> $DIR/issue-106883-is-empty.rs:20:13
|
||||
|
|
||||
LL | let _ = Foo as bool;
|
||||
| ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
|
||||
|
|
||||
note: this expression `Deref`s to `[u8]` which implements `is_empty`
|
||||
--> $DIR/issue-106883-is-empty.rs:20:13
|
||||
|
|
||||
LL | let _ = Foo as bool;
|
||||
| ^^^
|
||||
help: consider using the `is_empty` method on `Foo` to determine if it contains anything
|
||||
|
|
||||
LL | let _ = !Foo.is_empty();
|
||||
| + ~~~~~~~~~~~
|
||||
|
||||
error[E0606]: casting `&[i32]` as `bool` is invalid
|
||||
--> $DIR/issue-106883-is-empty.rs:25:5
|
||||
|
|
||||
LL | bar as bool
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything
|
||||
|
|
||||
LL | !bar.is_empty()
|
||||
| + ~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0605, E0606.
|
||||
For more information about an error, try `rustc --explain E0605`.
|
Loading…
Reference in New Issue
Block a user