about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2020-05-16 02:37:21 +0200
committerGitHub <noreply@github.com>2020-05-16 02:37:21 +0200
commitbadcf267df6ecfc62a765eea0e03d1ccf0cd3d02 (patch)
tree6239f49a1e1dd92ddd949e985ac77313e8a3024c /src
parent154db50d86432e7ddc7f292b161f9a52237a129e (diff)
parent10d7da4e0b07f469c9d2b8215b563528adfc7e99 (diff)
downloadrust-badcf267df6ecfc62a765eea0e03d1ccf0cd3d02.tar.gz
rust-badcf267df6ecfc62a765eea0e03d1ccf0cd3d02.zip
Rollup merge of #71948 - csmoe:issue-61076, r=oli-obk
Suggest to await future before ? operator

Closes https://github.com/rust-lang/rust/issues/71811
cc #61076
Diffstat (limited to 'src')
-rw-r--r--src/librustc_middle/hir/map/mod.rs6
-rw-r--r--src/librustc_middle/query/mod.rs6
-rw-r--r--src/librustc_middle/ty/query/keys.rs12
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/mod.rs1
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs101
-rw-r--r--src/librustc_trait_selection/traits/mod.rs42
-rw-r--r--src/librustc_typeck/check/mod.rs24
-rw-r--r--src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr5
-rw-r--r--src/test/ui/async-await/issue-61076.rs32
-rw-r--r--src/test/ui/async-await/issue-61076.stderr27
-rw-r--r--src/test/ui/async-await/try-on-option-in-async.rs3
-rw-r--r--src/test/ui/async-await/try-on-option-in-async.stderr6
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs18
13 files changed, 246 insertions, 37 deletions
diff --git a/src/librustc_middle/hir/map/mod.rs b/src/librustc_middle/hir/map/mod.rs
index de0373c1384..b823516d64f 100644
--- a/src/librustc_middle/hir/map/mod.rs
+++ b/src/librustc_middle/hir/map/mod.rs
@@ -390,11 +390,7 @@ impl<'hir> Map<'hir> {
     /// Given a `HirId`, returns the `BodyId` associated with it,
     /// if the node is a body owner, otherwise returns `None`.
     pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option<BodyId> {
-        if let Some(node) = self.find(hir_id) {
-            associated_body(node)
-        } else {
-            bug!("no entry for id `{}`", hir_id)
-        }
+        self.find(hir_id).map(associated_body).flatten()
     }
 
     /// Given a body owner's id, returns the `BodyId` associated with it.
diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs
index 2ceba519494..13cf9a934b7 100644
--- a/src/librustc_middle/query/mod.rs
+++ b/src/librustc_middle/query/mod.rs
@@ -1164,6 +1164,12 @@ rustc_queries! {
             desc { "evaluating trait selection obligation `{}`", goal.value }
         }
 
+        query type_implements_trait(
+            key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
+        ) -> bool {
+            desc { "evaluating `type_implements_trait` `{:?}`", key }
+        }
+
         /// Do not call this query directly: part of the `Eq` type-op
         query type_op_ascribe_user_type(
             goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx>
diff --git a/src/librustc_middle/ty/query/keys.rs b/src/librustc_middle/ty/query/keys.rs
index 239691dbd17..4acf766f033 100644
--- a/src/librustc_middle/ty/query/keys.rs
+++ b/src/librustc_middle/ty/query/keys.rs
@@ -295,3 +295,15 @@ impl Key for (Symbol, u32, u32) {
         DUMMY_SP
     }
 }
+
+impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) {
+    type CacheSelector = DefaultCacheSelector;
+
+    fn query_crate(&self) -> CrateNum {
+        LOCAL_CRATE
+    }
+
+    fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
+        DUMMY_SP
+    }
+}
diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs
index 5a63a693652..6bd4455e1e3 100644
--- a/src/librustc_trait_selection/traits/error_reporting/mod.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs
@@ -402,6 +402,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
                         self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
                         self.note_version_mismatch(&mut err, &trait_ref);
+                        self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span);
                         if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) {
                             err.emit();
                             return;
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index 5c4ba40bf38..503daf56b49 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -1,14 +1,17 @@
 use super::{
     EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
+    SelectionContext,
 };
 
 use crate::infer::InferCtxt;
+use crate::traits::normalize_projection_type;
 
 use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
+use rustc_hir::lang_items;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
 use rustc_middle::ty::TypeckTables;
 use rustc_middle::ty::{
@@ -150,6 +153,15 @@ pub trait InferCtxtExt<'tcx> {
         T: fmt::Display;
 
     fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>);
+
+    /// Suggest to await before try: future? => future.await?
+    fn suggest_await_before_try(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
+        span: Span,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
@@ -1822,6 +1834,95 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             suggested_limit, self.tcx.crate_name,
         ));
     }
+
+    fn suggest_await_before_try(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        obligation: &PredicateObligation<'tcx>,
+        trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
+        span: Span,
+    ) {
+        debug!(
+            "suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}",
+            obligation,
+            span,
+            trait_ref,
+            trait_ref.self_ty()
+        );
+        let body_hir_id = obligation.cause.body_id;
+        let item_id = self.tcx.hir().get_parent_node(body_hir_id);
+
+        if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) {
+            let body = self.tcx.hir().body(body_id);
+            if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+                let future_trait =
+                    self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
+
+                let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty());
+
+                let impls_future = self.tcx.type_implements_trait((
+                    future_trait,
+                    self_ty,
+                    ty::List::empty(),
+                    obligation.param_env,
+                ));
+
+                let item_def_id = self
+                    .tcx
+                    .associated_items(future_trait)
+                    .in_definition_order()
+                    .next()
+                    .unwrap()
+                    .def_id;
+                // `<T as Future>::Output`
+                let projection_ty = ty::ProjectionTy {
+                    // `T`
+                    substs: self.tcx.mk_substs_trait(
+                        trait_ref.self_ty(),
+                        self.fresh_substs_for_item(span, item_def_id),
+                    ),
+                    // `Future::Output`
+                    item_def_id,
+                };
+
+                let mut selcx = SelectionContext::new(self);
+
+                let mut obligations = vec![];
+                let normalized_ty = normalize_projection_type(
+                    &mut selcx,
+                    obligation.param_env,
+                    projection_ty,
+                    obligation.cause.clone(),
+                    0,
+                    &mut obligations,
+                );
+
+                debug!(
+                    "suggest_await_befor_try: normalized_projection_type {:?}",
+                    self.resolve_vars_if_possible(&normalized_ty)
+                );
+                let try_obligation = self.mk_obligation_for_def_id(
+                    trait_ref.def_id(),
+                    normalized_ty,
+                    obligation.cause.clone(),
+                    obligation.param_env,
+                );
+                debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation);
+                if self.predicate_may_hold(&try_obligation) && impls_future {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                        if snippet.ends_with('?') {
+                            err.span_suggestion(
+                                span,
+                                "consider using `.await` here",
+                                format!("{}.await?", snippet.trim_end_matches('?')),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
 
 /// Collect all the returned expressions within the input expression.
diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs
index 778430fc2ca..9592f93ce2e 100644
--- a/src/librustc_trait_selection/traits/mod.rs
+++ b/src/librustc_trait_selection/traits/mod.rs
@@ -31,7 +31,9 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::middle::region;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
-use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness};
+use rustc_middle::ty::{
+    self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness,
+};
 use rustc_span::Span;
 
 use std::fmt::Debug;
@@ -523,6 +525,43 @@ fn vtable_methods<'tcx>(
     }))
 }
 
+/// Check whether a `ty` implements given trait(trait_def_id).
+///
+/// NOTE: Always return `false` for a type which needs inference.
+fn type_implements_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    key: (
+        DefId,    // trait_def_id,
+        Ty<'tcx>, // type
+        SubstsRef<'tcx>,
+        ParamEnv<'tcx>,
+    ),
+) -> bool {
+    let (trait_def_id, ty, params, param_env) = key;
+
+    debug!(
+        "type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
+        trait_def_id, ty, params, param_env
+    );
+
+    // Do not check on infer_types to avoid panic in evaluate_obligation.
+    if ty.has_infer_types() {
+        return false;
+    }
+
+    let ty = tcx.erase_regions(&ty);
+
+    let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
+
+    let obligation = Obligation {
+        cause: ObligationCause::dummy(),
+        param_env,
+        recursion_depth: 0,
+        predicate: trait_ref.without_const().to_predicate(),
+    };
+    tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+}
+
 pub fn provide(providers: &mut ty::query::Providers<'_>) {
     object_safety::provide(providers);
     *providers = ty::query::Providers {
@@ -531,6 +570,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
         codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
         vtable_methods,
         substitute_normalize_and_test_predicates,
+        type_implements_trait,
         ..*providers
     };
 }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 79dde84b8b1..226c187e3cf 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5282,6 +5282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) {
+        debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
         // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
         // body isn't `async`.
         let item_id = self.tcx().hir().get_parent_node(self.body_id);
@@ -5299,22 +5300,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .next()
                     .unwrap()
                     .def_id;
+                // `<T as Future>::Output`
+                let projection_ty = ty::ProjectionTy {
+                    // `T`
+                    substs: self
+                        .tcx
+                        .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
+                    // `Future::Output`
+                    item_def_id,
+                };
+
                 let predicate =
                     ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
-                        // `<T as Future>::Output`
-                        projection_ty: ty::ProjectionTy {
-                            // `T`
-                            substs: self.tcx.mk_substs_trait(
-                                found,
-                                self.fresh_substs_for_item(sp, item_def_id),
-                            ),
-                            // `Future::Output`
-                            item_def_id,
-                        },
+                        projection_ty,
                         ty: expected,
                     }));
                 let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+
                 debug!("suggest_missing_await: trying obligation {:?}", obligation);
+
                 if self.infcx.predicate_may_hold(&obligation) {
                     debug!("suggest_missing_await: obligation held: {:?}", obligation);
                     if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
diff --git a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
index 61f2570b2ff..96158fc0e04 100644
--- a/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
+++ b/src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr
@@ -237,7 +237,10 @@ error[E0277]: the `?` operator can only be applied to values that implement `std
   --> $DIR/incorrect-syntax-suggestions.rs:16:19
    |
 LL |     let _ = await bar()?;
-   |                   ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future`
+   |                   ^^^^^^
+   |                   |
+   |                   the `?` operator cannot be applied to type `impl std::future::Future`
+   |                   help: consider using `.await` here: `bar().await?`
    |
    = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
    = note: required by `std::ops::Try::into_result`
diff --git a/src/test/ui/async-await/issue-61076.rs b/src/test/ui/async-await/issue-61076.rs
new file mode 100644
index 00000000000..13b45df64ea
--- /dev/null
+++ b/src/test/ui/async-await/issue-61076.rs
@@ -0,0 +1,32 @@
+// edition:2018
+
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll};
+
+struct T;
+
+impl Future for T {
+    type Output = Result<(), ()>;
+
+    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
+        Poll::Pending
+    }
+}
+
+async fn foo() -> Result<(), ()> {
+    Ok(())
+}
+
+async fn bar() -> Result<(), ()> {
+    foo()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+    Ok(())
+}
+
+async fn baz() -> Result<(), ()> {
+    let t = T;
+    t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
+    Ok(())
+}
+
+fn main() {}
diff --git a/src/test/ui/async-await/issue-61076.stderr b/src/test/ui/async-await/issue-61076.stderr
new file mode 100644
index 00000000000..e71f4e7136d
--- /dev/null
+++ b/src/test/ui/async-await/issue-61076.stderr
@@ -0,0 +1,27 @@
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/issue-61076.rs:22:5
+   |
+LL |     foo()?;
+   |     ^^^^^^
+   |     |
+   |     the `?` operator cannot be applied to type `impl std::future::Future`
+   |     help: consider using `.await` here: `foo().await?`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
+   = note: required by `std::ops::Try::into_result`
+
+error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
+  --> $DIR/issue-61076.rs:28:5
+   |
+LL |     t?;
+   |     ^^
+   |     |
+   |     the `?` operator cannot be applied to type `T`
+   |     help: consider using `.await` here: `t.await?`
+   |
+   = help: the trait `std::ops::Try` is not implemented for `T`
+   = note: required by `std::ops::Try::into_result`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/async-await/try-on-option-in-async.rs b/src/test/ui/async-await/try-on-option-in-async.rs
index 51ac522017c..c520a07abc1 100644
--- a/src/test/ui/async-await/try-on-option-in-async.rs
+++ b/src/test/ui/async-await/try-on-option-in-async.rs
@@ -7,7 +7,8 @@ async fn an_async_block() -> u32 {
         let x: Option<u32> = None;
         x?; //~ ERROR the `?` operator
         22
-    }.await
+    }
+    .await
 }
 
 async fn async_closure_containing_fn() -> u32 {
diff --git a/src/test/ui/async-await/try-on-option-in-async.stderr b/src/test/ui/async-await/try-on-option-in-async.stderr
index 46f8f41076b..700296d6747 100644
--- a/src/test/ui/async-await/try-on-option-in-async.stderr
+++ b/src/test/ui/async-await/try-on-option-in-async.stderr
@@ -7,14 +7,14 @@ LL | |         let x: Option<u32> = None;
 LL | |         x?;
    | |         ^^ cannot use the `?` operator in an async block that returns `{integer}`
 LL | |         22
-LL | |     }.await
+LL | |     }
    | |_____- this function should return `Result` or `Option` to accept `?`
    |
    = help: the trait `std::ops::Try` is not implemented for `{integer}`
    = note: required by `std::ops::Try::from_error`
 
 error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
-  --> $DIR/try-on-option-in-async.rs:16:9
+  --> $DIR/try-on-option-in-async.rs:17:9
    |
 LL |       let async_closure = async || {
    |  __________________________________-
@@ -29,7 +29,7 @@ LL | |     };
    = note: required by `std::ops::Try::from_error`
 
 error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
-  --> $DIR/try-on-option-in-async.rs:25:5
+  --> $DIR/try-on-option-in-async.rs:26:5
    |
 LL |   async fn an_async_function() -> u32 {
    |  _____________________________________-
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 3b8ef18bfab..438a9f42ccd 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -40,15 +40,12 @@ use rustc_hir::{
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::map::Map;
-use rustc_middle::traits;
 use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Binder, Ty, TyCtxt, TypeFoldable};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::original_sp;
 use rustc_span::symbol::{self, kw, Symbol};
 use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
 use rustc_target::abi::Integer;
-use rustc_trait_selection::traits::predicate_for_trait_def;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 use smallvec::SmallVec;
 
@@ -326,19 +323,8 @@ pub fn implements_trait<'a, 'tcx>(
     trait_id: DefId,
     ty_params: &[GenericArg<'tcx>],
 ) -> bool {
-    let ty = cx.tcx.erase_regions(&ty);
-    let obligation = predicate_for_trait_def(
-        cx.tcx,
-        cx.param_env,
-        traits::ObligationCause::dummy(),
-        trait_id,
-        0,
-        ty,
-        ty_params,
-    );
-    cx.tcx
-        .infer_ctxt()
-        .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+    let ty_params = cx.tcx.mk_substs(ty_params.iter());
+    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
 }
 
 /// Gets the `hir::TraitRef` of the trait the given method is implemented for.