}
declare_lint! {
- /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
+ /// The `must_not_suspend` lint detects values that are marked with the `#[must_not_suspend]`
+ /// attribute being held across yield points. A "yield" point is usually a `.await` in an async
+ /// function.
+ ///
+ /// This attribute can be used to mark values that are semantically incorrect across yields
+ /// (like certain types of timers), values that have async alternatives, and values that
+ /// regularly cause problems with the `Send`-ness of async fn's returned futures (like
+ /// `MutexGuard`'s)
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// #[must_not_suspend]
+ /// struct SyncThing {}
+ ///
+ /// async fn yield() {}
+ ///
+ /// pub async fn uhoh() {
+ /// let guard = SyncThing {};
+ /// yield().await;
+ /// }
+ /// ```
pub MUST_NOT_SUSPEND,
Warn,
"Use of a `#[must_not_suspend]` value across a yield point",
/// 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 => {
+ Target::Struct | Target::Enum | Target::Union | Target::Trait => true,
+ _ => {
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")
+ .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait")
+ .span_label(*span, "is not a struct, enum, or trait")
.emit();
false
}
- _ => true,
}
}
check_must_not_suspend_ty(
self.fcx,
- ty::ParamEnv::empty(),
ty,
hir_id,
expr,
// 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>>,
descr_post: &str,
plural_len: usize,
) -> bool {
+ debug!("FOUND TYPE: {:?}", ty);
if ty.is_unit()
- || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, param_env)
+ // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env)
+ // FIXME: should this check is_ty_uninhabited_from
{
return true;
}
let descr_pre = &format!("{}boxed ", descr_pre);
check_must_not_suspend_ty(
fcx,
- param_env,
boxed_ty,
hir_id,
expr,
}
ty::Tuple(ref tys) => {
let mut has_emitted = false;
- /*
- let spans = if let hir::ExprKind::Tup(comps) = &expr.kind {
+ let spans = if let Some(hir::ExprKind::Tup(comps)) = expr.map(|e| &e.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,
+ fcx, 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) {
+ ty::Array(ty, len) => match len.try_eval_usize(fcx.tcx, fcx.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.
let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,);
check_must_not_suspend_ty(
fcx,
- param_env,
ty,
hir_id,
expr,
async fn other() {}
pub async fn uhoh() {
- let _guard = bar(); //~ boxed `Umm` held across
+ let _guard = bar(); //~ ERROR boxed `Umm` held across
other().await;
}
// edition:2018
-#[must_not_suspend = "You gotta use Umm's, ya know?"] //~ the `#[must_not_suspend]`
+#[must_not_suspend = "You gotta use Umm's, ya know?"] //~ ERROR the `#[must_not_suspend]`
struct Umm {
_i: i64
}
--- /dev/null
+// edition:2018
+#![feature(must_not_suspend)]
+#![deny(must_not_suspend)]
+
+#[must_not_suspend] //~ ERROR attribute should be
+mod inner {}
+
+fn main() {}
--- /dev/null
+error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
+ --> $DIR/other_items.rs:5:1
+ |
+LL | #[must_not_suspend]
+ | ^^^^^^^^^^^^^^^^^^^
+LL | mod inner {}
+ | ------------ is not a struct, enum, or trait
+
+error: aborting due to previous error
+
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
-#[must_not_suspend] //~ attribute should be
+#[must_not_suspend] //~ ERROR attribute should be
fn foo() -> i32 {
0
}
-error: `must_not_suspend` attribute should be applied to a struct, enum, `impl Trait`, or `dyn Trait`
+error: `must_not_suspend` attribute should be applied to a struct, enum, or trait
--> $DIR/return.rs:5:1
|
LL | #[must_not_suspend]
LL | / fn foo() -> i32 {
LL | | 0
LL | | }
- | |_- is a function
+ | |_- is not a struct, enum, or trait
error: aborting due to previous error
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
+ let _guard1 = r#impl(); //~ ERROR implementer of `Wow` held across
+ let _guard2 = r#dyn(); //~ ERROR boxed `Wow` trait object held across
other().await;
}
async fn other() {}
pub async fn uhoh() {
- let _guard = bar(); //~ `Umm` held across
+ let _guard = bar(); //~ ERROR `Umm` held across
other().await;
}
async fn other() {}
pub async fn uhoh() {
- let _guard = bar(); //~ `Umm` held across
+ let _guard = bar(); //~ WARNING `Umm` held across
other().await;
}