suggest await on unexpected types

This commit is contained in:
csmoe 2020-08-20 18:42:08 +08:00
parent 2271b081eb
commit 8ee206a80d
6 changed files with 126 additions and 32 deletions

View File

@ -50,6 +50,7 @@
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use crate::infer;
use crate::infer::OriginalQueryValues;
use crate::traits::error_reporting::report_object_safety_error;
use crate::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@ -60,8 +61,10 @@
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{Item, ItemKind, Node};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::ParamEnvAnd;
use rustc_middle::ty::{
self,
subst::{Subst, SubstsRef},
@ -1529,6 +1532,7 @@ enum Mismatch<'a> {
};
if let Some(exp_found) = exp_found {
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
}
// In some (most?) cases cause.body_id points to actual body, but in some cases
@ -1547,6 +1551,72 @@ enum Mismatch<'a> {
self.note_error_origin(diag, cause, exp_found);
}
fn suggest_await_on_expect_found(
&self,
cause: &ObligationCause<'tcx>,
exp_span: Span,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut DiagnosticBuilder<'tcx>,
) {
debug!(
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
exp_span, exp_found.expected, exp_found.found
);
if let ty::Opaque(def_id, _) = exp_found.expected.kind {
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
// Future::Output
let item_def_id = self
.tcx
.associated_items(future_trait)
.in_definition_order()
.next()
.unwrap()
.def_id;
let mut projection_ty = None;
for (predicate, _) in self.tcx.predicates_of(def_id).predicates {
if let ty::PredicateAtom::Projection(projection_predicate) =
predicate.skip_binders()
{
if item_def_id == projection_predicate.projection_ty.item_def_id {
projection_ty = Some(projection_predicate.projection_ty);
break;
}
}
}
if let Some(projection_ty) = projection_ty {
let projection_query = self.canonicalize_query(
&ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
&mut OriginalQueryValues::default(),
);
if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
let normalized_ty = resp.value.value.normalized_ty;
debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
if ty::TyS::same_type(normalized_ty, exp_found.found) {
let span = if let ObligationCauseCode::Pattern {
span,
origin_expr: _,
root_ty: _,
} = cause.code
{
// scrutinee's span
span.unwrap_or(exp_span)
} else {
exp_span
};
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"consider awaiting on the future",
".await".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
}
}
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
/// suggests it.
fn suggest_as_ref_where_appropriate(

View File

@ -28,7 +28,7 @@ pub fn check_match(
};
// Type check the descriminant and get its type.
let scrut_ty = if force_scrutinee_bool {
let scrutinee_ty = if force_scrutinee_bool {
// Here we want to ensure:
//
// 1. That default match bindings are *not* accepted in the condition of an
@ -55,7 +55,7 @@ pub fn check_match(
// #55810: Type check patterns first so we get types for all bindings.
for arm in arms {
self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true);
}
// Now typecheck the blocks.

View File

@ -1518,7 +1518,7 @@ fn suggest_await_on_field_access(
def_id: DefId,
) {
let param_env = self.tcx().param_env(def_id);
let future_trait = self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
// Future::Output
let item_def_id =
self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
@ -1554,15 +1554,12 @@ fn suggest_await_on_field_access(
);
if let ty::Adt(def, _) = normalized_ty.kind {
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
let suggestion = format!("{}.await.{}", base, field_ident);
err.span_suggestion(
expr.span,
"consider await before field access",
suggestion,
Applicability::MaybeIncorrect,
);
}
err.span_suggestion_verbose(
base.span.shrink_to_hi(),
"consider awaiting before field access",
".await".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}

View File

@ -911,15 +911,13 @@ fn suggest_await_before_method(
);
let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
if let Ok(sp) = self.tcx.sess.source_map().span_to_snippet(span) {
if method_exists {
err.span_suggestion(
span,
"consider await before this method call",
format!("await.{}", sp),
Applicability::MaybeIncorrect,
);
}
if method_exists {
err.span_suggestion_verbose(
span.shrink_to_lo(),
"consider awaiting before this method call",
"await.".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}

View File

@ -64,5 +64,10 @@ async fn baz() -> Result<(), ()> {
Ok(())
}
async fn match_() {
match tuple() {
Tuple(_) => {} //~ ERROR mismatched types
}
}
fn main() {}

View File

@ -26,28 +26,52 @@ error[E0609]: no field `0` on type `impl std::future::Future`
--> $DIR/issue-61076.rs:58:26
|
LL | let _: i32 = tuple().0;
| --------^
| |
| help: consider await before field access: `tuple().await.0`
| ^
|
help: consider awaiting before field access
|
LL | let _: i32 = tuple().await.0;
| ^^^^^^
error[E0609]: no field `a` on type `impl std::future::Future`
--> $DIR/issue-61076.rs:60:28
|
LL | let _: i32 = struct_().a;
| ----------^
| |
| help: consider await before field access: `struct_().await.a`
| ^
|
help: consider awaiting before field access
|
LL | let _: i32 = struct_().await.a;
| ^^^^^^
error[E0599]: no method named `method` found for opaque type `impl std::future::Future` in the current scope
--> $DIR/issue-61076.rs:62:15
|
LL | struct_().method();
| ^^^^^^ method not found in `impl std::future::Future`
|
help: consider awaiting before this method call
|
LL | struct_().await.method();
| ^^^^^^
| |
| method not found in `impl std::future::Future`
| help: consider await before this method call: `await.method`
error: aborting due to 5 previous errors
error[E0308]: mismatched types
--> $DIR/issue-61076.rs:69:9
|
LL | async fn tuple() -> Tuple {
| ----- the `Output` of this `async fn`'s expected opaque type
...
LL | Tuple(_) => {}
| ^^^^^^^^ expected opaque type, found struct `Tuple`
|
= note: expected opaque type `impl std::future::Future`
found struct `Tuple`
help: consider awaiting on the future
|
LL | match tuple().await {
| ^^^^^^
Some errors have detailed explanations: E0277, E0599, E0609.
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0277, E0308, E0599, E0609.
For more information about an error, try `rustc --explain E0277`.