]> git.lizzy.rs Git - rust.git/commitdiff
must_not_suspend impl
authorGus Wynn <guswynn@gmail.com>
Sun, 5 Sep 2021 02:36:51 +0000 (19:36 -0700)
committerGus Wynn <guswynn@gmail.com>
Sat, 11 Sep 2021 17:45:17 +0000 (10:45 -0700)
19 files changed:
compiler/rustc_feature/src/active.rs
compiler/rustc_feature/src/builtin_attrs.rs
compiler/rustc_lint/src/lib.rs
compiler/rustc_lint_defs/src/builtin.rs
compiler/rustc_passes/src/check_attr.rs
compiler/rustc_span/src/symbol.rs
compiler/rustc_typeck/src/check/generator_interior.rs
src/test/ui/lint/must_not_suspend/boxed.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/boxed.stderr [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.stderr [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/return.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/return.stderr [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/trait.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/trait.stderr [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/unit.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/unit.stderr [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/warn.rs [new file with mode: 0644]
src/test/ui/lint/must_not_suspend/warn.stderr [new file with mode: 0644]

index a3807a2bb9fde64cb5a3a6e2574de2a9176f145f..66569270bd21ec3c4555948218b8b9d16f79f013 100644 (file)
@@ -679,6 +679,10 @@ pub fn set(&self, features: &mut Features, span: Span) {
     /// Allows `let...else` statements.
     (active, let_else, "1.56.0", Some(87335), None),
 
+    /// Allows `#[must_not_suspend]`.
+    (active, must_not_suspend, "1.56.0", Some(83310), None),
+
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
index e2aa54a59b2021401c8ab1f7fb751a34c50c1ac9..928a7eb794bd1afb647c7abe70b133ecb6860068 100644 (file)
@@ -202,6 +202,10 @@ macro_rules! experimental {
     ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
     ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
     ungated!(must_use, Normal, template!(Word, NameValueStr: "reason")),
+    gated!(
+        must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), must_not_suspend,
+        experimental!(must_not_suspend)
+    ),
     // FIXME(#14407)
     ungated!(
         deprecated, Normal,
index ef4bda666ba062c7bb033084109649cda82bab23..10285272130cc2626ae7a53537a6a1d9858b9083 100644 (file)
@@ -298,6 +298,7 @@ macro_rules! register_passes {
         UNUSED_LABELS,
         UNUSED_PARENS,
         UNUSED_BRACES,
+        MUST_NOT_SUSPEND,
         REDUNDANT_SEMICOLONS
     );
 
index 8fb678e2d20fb0313633d4b9adccfdf41ab8e4f3..386435034b6aca77e235578dc6e60d9bd004d19f 100644 (file)
     "imports that are never used"
 }
 
+declare_lint! {
+    /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+    pub MUST_NOT_SUSPEND,
+    Warn,
+    "Use of a `#[must_not_suspend]` value across a yield point",
+}
+
 declare_lint! {
     /// The `unused_extern_crates` lint guards against `extern crate` items
     /// that are never used.
         CENUM_IMPL_DROP_CAST,
         CONST_EVALUATABLE_UNCHECKED,
         INEFFECTIVE_UNSTABLE_TRAIT_IMPL,
+        MUST_NOT_SUSPEND,
         UNINHABITED_STATIC,
         FUNCTION_ITEM_REFERENCES,
         USELESS_DEPRECATED,
index fd438bdc9005ac5557c389bfa58ec7c9a5c860fe..a31f3fe281ee9918039793addf2af3ca75f17125 100644 (file)
@@ -104,6 +104,7 @@ fn check_attributes(
                 sym::default_method_body_is_const => {
                     self.check_default_method_body_is_const(attr, span, target)
                 }
+                sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
                 sym::rustc_const_unstable
                 | sym::rustc_const_stable
                 | sym::unstable
@@ -1014,6 +1015,21 @@ fn check_doc_attrs(
         is_valid
     }
 
+    /// Checks if `#[must_not_suspend]` is applied to a function. Returns `true` if valid.
+    fn check_must_not_suspend(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
+        match target {
+            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, `impl Trait`, or `dyn Trait`")
+                        .span_label(*span, "is a function")
+                        .emit();
+                false
+            }
+            _ => true,
+        }
+    }
+
     /// Checks if `#[cold]` is applied to a non-function. Returns `true` if valid.
     fn check_cold(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) {
         match target {
index 24023163cc30eb2b2ca072313f21f421f86b5764..eecbb9a9cfadcc12bf681b9623c9df6cd18f1ea8 100644 (file)
         mul,
         mul_assign,
         mul_with_overflow,
+        must_not_suspend,
         must_use,
         mut_ptr,
         mut_slice_ptr,
index 5f26e701c0ab7803e35d38802557486ad478edd9..c5efc30a7c2d492888280d30a6e97ca4aa2194c3 100644 (file)
@@ -5,6 +5,7 @@
 
 use super::FnCtxt;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
 use rustc_middle::middle::region::{self, YieldData};
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::symbol::sym;
 use rustc_span::Span;
 use smallvec::SmallVec;
+use tracing::debug;
 
 struct InteriorVisitor<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
@@ -36,6 +39,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
     fn record(
         &mut self,
         ty: Ty<'tcx>,
+        hir_id: HirId,
         scope: Option<region::Scope>,
         expr: Option<&'tcx Expr<'tcx>>,
         source_span: Span,
@@ -117,6 +121,20 @@ fn record(
             } else {
                 // Insert the type into the ordered set.
                 let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
+
+                check_must_not_suspend_ty(
+                    self.fcx,
+                    ty::ParamEnv::empty(),
+                    ty,
+                    hir_id,
+                    expr,
+                    source_span,
+                    yield_data.span,
+                    "",
+                    "",
+                    1,
+                );
+
                 self.types.insert(ty::GeneratorInteriorTypeCause {
                     span: source_span,
                     ty: &ty,
@@ -290,7 +308,7 @@ fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
         if let PatKind::Binding(..) = pat.kind {
             let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
             let ty = self.fcx.typeck_results.borrow().pat_ty(pat);
-            self.record(ty, Some(scope), None, pat.span, false);
+            self.record(ty, pat.hir_id, Some(scope), None, pat.span, false);
         }
     }
 
@@ -342,7 +360,14 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
         // If there are adjustments, then record the final type --
         // this is the actual value that is being produced.
         if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) {
-            self.record(adjusted_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
+            self.record(
+                adjusted_ty,
+                expr.hir_id,
+                scope,
+                Some(expr),
+                expr.span,
+                guard_borrowing_from_pattern,
+            );
         }
 
         // Also record the unadjusted type (which is the only type if
@@ -380,9 +405,23 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                     tcx.mk_region(ty::RegionKind::ReErased),
                     ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
                 );
-                self.record(ref_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
+                self.record(
+                    ref_ty,
+                    expr.hir_id,
+                    scope,
+                    Some(expr),
+                    expr.span,
+                    guard_borrowing_from_pattern,
+                );
             }
-            self.record(ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern);
+            self.record(
+                ty,
+                expr.hir_id,
+                scope,
+                Some(expr),
+                expr.span,
+                guard_borrowing_from_pattern,
+            );
         } else {
             self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node");
         }
@@ -409,3 +448,263 @@ fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
         }
     }
 }
+
+// Returns whether it emitted a diagnostic or not
+// Note that this fn and the proceding one are based on the code
+// for creating must_use diagnostics
+pub fn check_must_not_suspend_ty<'tcx>(
+    fcx: &FnCtxt<'_, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    hir_id: HirId,
+    expr: Option<&'tcx Expr<'tcx>>,
+    source_span: Span,
+    yield_span: Span,
+    descr_pre: &str,
+    descr_post: &str,
+    plural_len: usize,
+) -> bool {
+    if ty.is_unit()
+        || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, param_env)
+    {
+        return true;
+    }
+
+    let plural_suffix = pluralize!(plural_len);
+
+    let emitted = match *ty.kind() {
+        ty::Adt(..) if ty.is_box() => {
+            let boxed_ty = ty.boxed_ty();
+            let descr_pre = &format!("{}boxed ", descr_pre);
+            check_must_not_suspend_ty(
+                fcx,
+                param_env,
+                boxed_ty,
+                hir_id,
+                expr,
+                source_span,
+                yield_span,
+                descr_pre,
+                descr_post,
+                plural_len,
+            )
+        }
+        ty::Adt(def, _) => check_must_not_suspend_def(
+            fcx.tcx,
+            def.did,
+            hir_id,
+            source_span,
+            yield_span,
+            descr_pre,
+            descr_post,
+        ),
+        ty::Opaque(def, _) => {
+            let mut has_emitted = false;
+            for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) {
+                // We only look at the `DefId`, so it is safe to skip the binder here.
+                if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
+                    predicate.kind().skip_binder()
+                {
+                    let def_id = poly_trait_predicate.trait_ref.def_id;
+                    let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix,);
+                    if check_must_not_suspend_def(
+                        fcx.tcx,
+                        def_id,
+                        hir_id,
+                        source_span,
+                        yield_span,
+                        descr_pre,
+                        descr_post,
+                    ) {
+                        has_emitted = true;
+                        break;
+                    }
+                }
+            }
+            has_emitted
+        }
+        ty::Dynamic(binder, _) => {
+            let mut has_emitted = false;
+            for predicate in binder.iter() {
+                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+                    let def_id = trait_ref.def_id;
+                    let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post,);
+                    if check_must_not_suspend_def(
+                        fcx.tcx,
+                        def_id,
+                        hir_id,
+                        source_span,
+                        yield_span,
+                        descr_pre,
+                        descr_post,
+                    ) {
+                        has_emitted = true;
+                        break;
+                    }
+                }
+            }
+            has_emitted
+        }
+        ty::Tuple(ref tys) => {
+            let mut has_emitted = false;
+            /*
+            let spans = if let hir::ExprKind::Tup(comps) = &expr.kind {
+                debug_assert_eq!(comps.len(), tys.len());
+                comps.iter().map(|e| e.span).collect()
+            } else {
+                vec![]
+            };
+            */
+            let spans = vec![];
+            for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
+                let descr_post = &format!(" in tuple element {}", i);
+                let span = *spans.get(i).unwrap_or(&source_span);
+                if check_must_not_suspend_ty(
+                    fcx, param_env, ty, hir_id, expr, span, yield_span, descr_pre, descr_post,
+                    plural_len,
+                ) {
+                    has_emitted = true;
+                }
+            }
+            has_emitted
+        }
+        ty::Array(ty, len) => match len.try_eval_usize(fcx.tcx, param_env) {
+            // If the array is empty we don't lint, to avoid false positives
+            Some(0) | None => false,
+            // If the array is definitely non-empty, we can do `#[must_use]` checking.
+            Some(n) => {
+                let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,);
+                check_must_not_suspend_ty(
+                    fcx,
+                    param_env,
+                    ty,
+                    hir_id,
+                    expr,
+                    source_span,
+                    yield_span,
+                    descr_pre,
+                    descr_post,
+                    n as usize + 1,
+                )
+            }
+        },
+        _ => false,
+    };
+
+    // Don't move onto the "return value" path if we already sent a diagnostic
+    if emitted {
+        return true;
+    }
+
+    match expr {
+        Some(expr) => match expr.kind {
+            hir::ExprKind::Call(ref callee, _) => {
+                match callee.kind {
+                    hir::ExprKind::Path(ref qpath) => {
+                        match fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id) {
+                            Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => {
+                                check_must_not_suspend_def(
+                                    fcx.tcx,
+                                    def_id,
+                                    hir_id,
+                                    source_span,
+                                    yield_span,
+                                    "return value of ",
+                                    "",
+                                )
+                            }
+
+                            // `Res::Local` if it was a closure, for which we
+                            // do not currently support must-not-suspend linting
+                            _ => false,
+                        }
+                    }
+                    _ => false,
+                }
+            }
+            hir::ExprKind::MethodCall(..) => {
+                if let Some(def_id) = fcx.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
+                {
+                    check_must_not_suspend_def(
+                        fcx.tcx,
+                        def_id,
+                        hir_id,
+                        source_span,
+                        yield_span,
+                        "return value of ",
+                        "",
+                    )
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        },
+        None => false,
+    }
+}
+
+fn check_must_not_suspend_def(
+    tcx: TyCtxt<'_>,
+    def_id: DefId,
+    hir_id: HirId,
+    source_span: Span,
+    yield_span: Span,
+    descr_pre_path: &str,
+    descr_post_path: &str,
+) -> bool {
+    for attr in tcx.get_attrs(def_id).iter() {
+        if attr.has_name(sym::must_not_suspend) {
+            tcx.struct_span_lint_hir(
+                rustc_session::lint::builtin::MUST_NOT_SUSPEND,
+                hir_id,
+                source_span,
+                |lint| {
+                    let msg = format!(
+                        "{}`{}`{} held across a yield point, but should not be",
+                        descr_pre_path,
+                        tcx.def_path_str(def_id),
+                        descr_post_path
+                    );
+                    let mut err = lint.build(&msg);
+
+                    // Add optional reason note
+                    if let Some(note) = attr.value_str() {
+                        err.note(&note.as_str());
+                    }
+
+                    // add span pointing to the offending yield/await)
+                    err.span_label(yield_span, "The value is held across this yield point");
+
+                    // Add some quick suggestions on what to do
+                    err.span_help(
+                        source_span,
+                        "`drop` this value before the yield point, or use a block (`{ ... }`) \"
+                        to shrink its scope",
+                    );
+
+                    err.emit();
+                },
+            );
+
+            /*
+            cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
+                let msg = format!(
+                    "unused {}`{}`{} that must be used",
+                    descr_pre_path,
+                    cx.tcx.def_path_str(def_id),
+                    descr_post_path
+                );
+                let mut err = lint.build(&msg);
+                // check for #[must_use = "..."]
+                if let Some(note) = attr.value_str() {
+                    err.note(&note.as_str());
+                }
+                err.emit();
+            });
+            */
+            return true;
+        }
+    }
+    false
+}
diff --git a/src/test/ui/lint/must_not_suspend/boxed.rs b/src/test/ui/lint/must_not_suspend/boxed.rs
new file mode 100644 (file)
index 0000000..d64d07e
--- /dev/null
@@ -0,0 +1,25 @@
+// edition:2018
+#![feature(must_not_suspend)]
+#![deny(must_not_suspend)]
+
+#[must_not_suspend = "You gotta use Umm's, ya know?"]
+struct Umm {
+    i: i64
+}
+
+
+fn bar() -> Box<Umm> {
+    Box::new(Umm {
+        i: 1
+    })
+}
+
+async fn other() {}
+
+pub async fn uhoh() {
+    let _guard = bar(); //~ boxed `Umm` held across
+    other().await;
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/boxed.stderr b/src/test/ui/lint/must_not_suspend/boxed.stderr
new file mode 100644 (file)
index 0000000..c3c23db
--- /dev/null
@@ -0,0 +1,23 @@
+error: boxed `Umm` held across a yield point, but should not be
+  --> $DIR/boxed.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+LL |     other().await;
+   |     ------------- The value is held across this yield point
+   |
+note: the lint level is defined here
+  --> $DIR/boxed.rs:3:9
+   |
+LL | #![deny(must_not_suspend)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: You gotta use Umm's, ya know?
+help: `drop` this value before the yield point, or use a block (`{ ... }`) "
+                        to shrink its scope
+  --> $DIR/boxed.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.rs b/src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.rs
new file mode 100644 (file)
index 0000000..aff8ff3
--- /dev/null
@@ -0,0 +1,9 @@
+// edition:2018
+
+#[must_not_suspend = "You gotta use Umm's, ya know?"] //~ the `#[must_not_suspend]`
+struct Umm {
+    _i: i64
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.stderr b/src/test/ui/lint/must_not_suspend/feature-gate-must_not_suspend.stderr
new file mode 100644 (file)
index 0000000..ab20a8b
--- /dev/null
@@ -0,0 +1,12 @@
+error[E0658]: the `#[must_not_suspend]` attribute is an experimental feature
+  --> $DIR/feature-gate-must_not_suspend.rs:3:1
+   |
+LL | #[must_not_suspend = "You gotta use Umm's, ya know?"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #83310 <https://github.com/rust-lang/rust/issues/83310> for more information
+   = help: add `#![feature(must_not_suspend)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/lint/must_not_suspend/return.rs b/src/test/ui/lint/must_not_suspend/return.rs
new file mode 100644 (file)
index 0000000..5f80e78
--- /dev/null
@@ -0,0 +1,9 @@
+// edition:2018
+#![feature(must_not_suspend)]
+#![deny(must_not_suspend)]
+
+#[must_not_suspend] //~ attribute should be
+fn foo() -> i32 {
+    0
+}
+fn main() {}
diff --git a/src/test/ui/lint/must_not_suspend/return.stderr b/src/test/ui/lint/must_not_suspend/return.stderr
new file mode 100644 (file)
index 0000000..ff17983
--- /dev/null
@@ -0,0 +1,12 @@
+error: `must_not_suspend` attribute should be applied to a struct, enum, `impl Trait`, or `dyn Trait`
+  --> $DIR/return.rs:5:1
+   |
+LL |   #[must_not_suspend]
+   |   ^^^^^^^^^^^^^^^^^^^
+LL | / fn foo() -> i32 {
+LL | |     0
+LL | | }
+   | |_- is a function
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lint/must_not_suspend/trait.rs b/src/test/ui/lint/must_not_suspend/trait.rs
new file mode 100644 (file)
index 0000000..0438e07
--- /dev/null
@@ -0,0 +1,28 @@
+// edition:2018
+#![feature(must_not_suspend)]
+#![deny(must_not_suspend)]
+
+#[must_not_suspend]
+trait Wow {}
+
+impl Wow for i32 {}
+
+fn r#impl() -> impl Wow {
+    1
+}
+
+fn r#dyn() -> Box<dyn Wow> {
+    Box::new(1)
+}
+
+async fn other() {}
+
+pub async fn uhoh() {
+    let _guard1 = r#impl(); //~ implementer of `Wow` held across
+    let _guard2 = r#dyn(); //~ boxed `Wow` trait object held across
+
+    other().await;
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/trait.stderr b/src/test/ui/lint/must_not_suspend/trait.stderr
new file mode 100644 (file)
index 0000000..1175cbb
--- /dev/null
@@ -0,0 +1,39 @@
+error: implementer of `Wow` held across a yield point, but should not be
+  --> $DIR/trait.rs:21:9
+   |
+LL |     let _guard1 = r#impl();
+   |         ^^^^^^^
+...
+LL |     other().await;
+   |     ------------- The value is held across this yield point
+   |
+note: the lint level is defined here
+  --> $DIR/trait.rs:3:9
+   |
+LL | #![deny(must_not_suspend)]
+   |         ^^^^^^^^^^^^^^^^
+help: `drop` this value before the yield point, or use a block (`{ ... }`) "
+                        to shrink its scope
+  --> $DIR/trait.rs:21:9
+   |
+LL |     let _guard1 = r#impl();
+   |         ^^^^^^^
+
+error: boxed `Wow` trait object held across a yield point, but should not be
+  --> $DIR/trait.rs:22:9
+   |
+LL |     let _guard2 = r#dyn();
+   |         ^^^^^^^
+LL | 
+LL |     other().await;
+   |     ------------- The value is held across this yield point
+   |
+help: `drop` this value before the yield point, or use a block (`{ ... }`) "
+                        to shrink its scope
+  --> $DIR/trait.rs:22:9
+   |
+LL |     let _guard2 = r#dyn();
+   |         ^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/lint/must_not_suspend/unit.rs b/src/test/ui/lint/must_not_suspend/unit.rs
new file mode 100644 (file)
index 0000000..4e87b80
--- /dev/null
@@ -0,0 +1,25 @@
+// edition:2018
+#![feature(must_not_suspend)]
+#![deny(must_not_suspend)]
+
+#[must_not_suspend = "You gotta use Umm's, ya know?"]
+struct Umm {
+    i: i64
+}
+
+
+fn bar() -> Umm {
+    Umm {
+        i: 1
+    }
+}
+
+async fn other() {}
+
+pub async fn uhoh() {
+    let _guard = bar(); //~ `Umm` held across
+    other().await;
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/unit.stderr b/src/test/ui/lint/must_not_suspend/unit.stderr
new file mode 100644 (file)
index 0000000..cff00dd
--- /dev/null
@@ -0,0 +1,23 @@
+error: `Umm` held across a yield point, but should not be
+  --> $DIR/unit.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+LL |     other().await;
+   |     ------------- The value is held across this yield point
+   |
+note: the lint level is defined here
+  --> $DIR/unit.rs:3:9
+   |
+LL | #![deny(must_not_suspend)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: You gotta use Umm's, ya know?
+help: `drop` this value before the yield point, or use a block (`{ ... }`) "
+                        to shrink its scope
+  --> $DIR/unit.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lint/must_not_suspend/warn.rs b/src/test/ui/lint/must_not_suspend/warn.rs
new file mode 100644 (file)
index 0000000..d0d7238
--- /dev/null
@@ -0,0 +1,25 @@
+// edition:2018
+// run-pass
+#![feature(must_not_suspend)]
+
+#[must_not_suspend = "You gotta use Umm's, ya know?"]
+struct Umm {
+    _i: i64
+}
+
+
+fn bar() -> Umm {
+    Umm {
+        _i: 1
+    }
+}
+
+async fn other() {}
+
+pub async fn uhoh() {
+    let _guard = bar(); //~ `Umm` held across
+    other().await;
+}
+
+fn main() {
+}
diff --git a/src/test/ui/lint/must_not_suspend/warn.stderr b/src/test/ui/lint/must_not_suspend/warn.stderr
new file mode 100644 (file)
index 0000000..bda44d0
--- /dev/null
@@ -0,0 +1,19 @@
+warning: `Umm` held across a yield point, but should not be
+  --> $DIR/warn.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+LL |     other().await;
+   |     ------------- The value is held across this yield point
+   |
+   = note: `#[warn(must_not_suspend)]` on by default
+   = note: You gotta use Umm's, ya know?
+help: `drop` this value before the yield point, or use a block (`{ ... }`) "
+                        to shrink its scope
+  --> $DIR/warn.rs:20:9
+   |
+LL |     let _guard = bar();
+   |         ^^^^^^
+
+warning: 1 warning emitted
+