Skip to content

Add a MustUse trait to complement #[must_use] #71816

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/libcore/macros/mod.rs
Original file line number Diff line number Diff line change
@@ -1446,4 +1446,13 @@ pub(crate) mod builtin {
pub macro RustcEncodable($item:item) {
/* compiler built-in */
}

/// Indicates that a value must be explicitly consumed by the user.
#[cfg(not(bootstrap))]
#[rustc_builtin_macro]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable(rustc_attrs, must_use_trait)]
pub macro must_use($item:item) {
/* compiler built-in */
}
}
10 changes: 10 additions & 0 deletions src/libcore/marker.rs
Original file line number Diff line number Diff line change
@@ -757,6 +757,16 @@ impl<T: ?Sized> Unpin for *const T {}
#[stable(feature = "pin_raw", since = "1.38.0")]
impl<T: ?Sized> Unpin for *mut T {}

/// Indicates that a type must be consumed explicitly.
///
/// cf. `#[must_use]`
#[unstable(feature = "must_use_trait", issue = "none")]
#[cfg_attr(not(bootstrap), lang = "must_use")]
pub trait MustUse {
/// An explanation for why values of this type must be used.
const REASON: &'static str = "";
}

/// Implementations of `Copy` for primitive types.
///
/// Implementations that cannot be described in Rust
5 changes: 5 additions & 0 deletions src/libcore/prelude/v1.rs
Original file line number Diff line number Diff line change
@@ -76,3 +76,8 @@ pub use crate::macros::builtin::{
)]
#[doc(no_inline)]
pub use crate::macros::builtin::cfg_accessible;

#[cfg(not(bootstrap))]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[doc(no_inline)]
pub use crate::macros::builtin::must_use;
2 changes: 2 additions & 0 deletions src/librustc_builtin_macros/lib.rs
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ mod global_allocator;
mod global_asm;
mod llvm_asm;
mod log_syntax;
mod must_use;
mod source_util;
mod test;
mod trace_macros;
@@ -92,6 +93,7 @@ pub fn register_builtin_macros(resolver: &mut dyn Resolver, edition: Edition) {
global_allocator: global_allocator::expand,
test: test::expand_test,
test_case: test::expand_test_case,
must_use: must_use::expand,
}

register_derive! {
186 changes: 186 additions & 0 deletions src/librustc_builtin_macros/must_use.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
use ast::{AttrStyle, Ident, MacArgs};
use rustc_ast::{ast, ptr::P, tokenstream::TokenStream};
use rustc_attr::{mk_attr, HasAttrs};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_feature::BUILTIN_ATTRIBUTE_MAP;
use rustc_parse::validate_attr;
use rustc_span::{sym, symbol::kw, Span};
use std::iter;

pub fn expand(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta: &ast::MetaItem,
mut item: Annotatable,
) -> Vec<Annotatable> {
// Validate input against the `#[rustc_must_use]` template.
let (_, _, template, _) = &BUILTIN_ATTRIBUTE_MAP[&sym::rustc_must_use];
let attr = ecx.attribute(meta.clone());
validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, sym::must_use, template.clone());

let reason = meta.name_value_literal();
let mac_args = match reason {
None => MacArgs::Empty,
Some(lit) => MacArgs::Eq(span, TokenStream::new(vec![lit.token_tree().into()])),
};

// def-site context makes rustc accept the unstable `rustc_must_use` and `MustUse` trait.
let def_span = ecx.with_def_site_ctxt(item.span());

// Put a `#[rustc_must_use]` on the item in any case. This allows rustdoc to pick it up and
// render it.
item.visit_attrs(|attrs| {
attrs.push(mk_attr(
AttrStyle::Outer,
ast::Path::from_ident(Ident::with_dummy_span(sym::rustc_must_use)),
mac_args,
def_span,
));
});

// This macro desugars `#[must_use]` on types to `impl`s of the `MustUse` trait.
// Any other uses are forwarded to the `#[rustc_must_use]` macro.
if let Annotatable::Item(ast_item) = &item {
match &ast_item.kind {
ast::ItemKind::Enum(_, generics)
| ast::ItemKind::Struct(_, generics)
| ast::ItemKind::Union(_, generics) => {
// Generate a derive-style impl for a concrete type.
let impl_items = if let Some(reason) = reason {
let item = ast::AssocItem {
attrs: vec![],
id: ast::DUMMY_NODE_ID,
span: def_span,
vis: ast::Visibility {
node: ast::VisibilityKind::Inherited,
span: def_span,
},
ident: Ident::new(sym::REASON, def_span),
kind: ast::AssocItemKind::Const(
ast::Defaultness::Final,
ecx.ty(
def_span,
ast::TyKind::Rptr(
Some(ecx.lifetime(
def_span,
Ident::new(kw::StaticLifetime, def_span),
)),
ecx.ty_mt(
ecx.ty_ident(def_span, Ident::new(sym::str, def_span)),
ast::Mutability::Not,
),
),
),
Some(ecx.expr_lit(def_span, reason.kind.clone())),
),
tokens: None,
};

vec![P(item)]
} else {
vec![]
};

let mut impl_generics = generics.clone();
for param in impl_generics.params.iter_mut() {
match &mut param.kind {
ast::GenericParamKind::Type { default } => {
// Delete defaults as they're not usable in impls.
*default = None;
}

ast::GenericParamKind::Lifetime
| ast::GenericParamKind::Const { ty: _ } => {}
}
}
let new_impl = ast::ItemKind::Impl {
unsafety: ast::Unsafe::No,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
constness: ast::Const::No,
generics: impl_generics,
of_trait: Some(ecx.trait_ref(ecx.path(
def_span,
vec![
Ident::new(kw::DollarCrate, def_span),
Ident::new(sym::marker, def_span),
Ident::new(sym::MustUse, def_span),
],
))),
self_ty: ecx.ty_path(
ecx.path_all(
def_span,
false,
vec![ast_item.ident],
generics
.params
.iter()
.map(|param| match &param.kind {
ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
ecx.lifetime(def_span, param.ident),
),
ast::GenericParamKind::Type { default: _ } => {
ast::GenericArg::Type(ecx.ty_ident(def_span, param.ident))
}
ast::GenericParamKind::Const { ty: _ } => {
ast::GenericArg::Const(
ecx.const_ident(def_span, param.ident),
)
}
})
.collect(),
),
),
items: impl_items,
};

// Copy some important attributes from the original item, just like the built-in
// derives.
let attrs = ast_item
.attrs
.iter()
.filter(|a| {
[sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
.contains(&a.name_or_empty())
})
.cloned()
.chain(iter::once(
ecx.attribute(ecx.meta_word(def_span, sym::automatically_derived)),
))
.collect();

let new_impl =
Annotatable::Item(ecx.item(def_span, Ident::invalid(), attrs, new_impl));

return vec![item, new_impl];
}

ast::ItemKind::Trait(_, _, _, _, _) => {
// Generate a blanket `impl<T: Trait> MustUse for T` impl.
// FIXME(jschievink): This currently doesn't work due to overlapping impls. Even
// with specialization, you couldn't implement 2 `#[must_use]` traits for the same
// type.
// Technically, we could permit overlapping impls here, since it cannot lead to
// unsoundness. At worst, the lint will only print one of the applicable messages
// (though it could attempt to collect messages from all impls that are known to
// apply).
}

ast::ItemKind::TraitAlias(_, _)
| ast::ItemKind::Impl { .. }
| ast::ItemKind::MacCall(..)
| ast::ItemKind::MacroDef(..)
| ast::ItemKind::ExternCrate(..)
| ast::ItemKind::Use(..)
| ast::ItemKind::Static(..)
| ast::ItemKind::Const(..)
| ast::ItemKind::Fn(..)
| ast::ItemKind::Mod(..)
| ast::ItemKind::ForeignMod(..)
| ast::ItemKind::GlobalAsm(..)
| ast::ItemKind::TyAlias(..) => {}
}
}

vec![item]
}
1 change: 1 addition & 0 deletions src/librustc_expand/expand.rs
Original file line number Diff line number Diff line change
@@ -1084,6 +1084,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
if !self.cx.ecfg.custom_inner_attributes()
&& attr.style == ast::AttrStyle::Inner
&& !attr.has_name(sym::test)
&& !attr.has_name(sym::must_use)
{
feature_err(
&self.cx.parse_sess,
2 changes: 1 addition & 1 deletion src/librustc_feature/builtin_attrs.rs
Original file line number Diff line number Diff line change
@@ -202,7 +202,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
ungated!(must_use, Whitelisted, template!(Word, NameValueStr: "reason")),
// FIXME(#14407)
ungated!(
deprecated, Normal,
@@ -440,6 +439,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Internal attributes, Diagnostics related:
// ==========================================================================

rustc_attr!(rustc_must_use, Whitelisted, template!(Word, NameValueStr: "reason"), IMPL_DETAIL),
rustc_attr!(
rustc_on_unimplemented, Whitelisted,
template!(
2 changes: 2 additions & 0 deletions src/librustc_hir/lang_items.rs
Original file line number Diff line number Diff line change
@@ -256,4 +256,6 @@ language_item_table! {
AlignOffsetLangItem, "align_offset", align_offset_fn, Target::Fn;

TerminationTraitLangItem, "termination", termination, Target::Trait;

MustUseTraitLangItem, "must_use", must_use_trait, Target::Trait;
}
114 changes: 111 additions & 3 deletions src/librustc_lint/unused.rs
Original file line number Diff line number Diff line change
@@ -11,14 +11,26 @@ use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::adjustment;
use rustc_middle::ty::{self, Ty};
use rustc_middle::{
mir::interpret::ConstValue,
traits::{specialization_graph, ObligationCause, Vtable},
ty::{self, Ty},
};
use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
use rustc_span::symbol::Symbol;
use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span, DUMMY_SP};
use rustc_trait_selection::{
infer::TyCtxtInferExt,
traits::{FulfillmentContext, Obligation, SelectionContext, TraitEngine},
};
use ty::{AssocKind, Binder, TraitPredicate};

use ast::Ident;
use log::debug;
use std::str;

declare_lint! {
pub UNUSED_MUST_USE,
@@ -134,6 +146,103 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
return true;
}

let must_use_trait = match cx.tcx.lang_items().require(LangItem::MustUseTraitLangItem) {
Ok(def_id) => def_id,
Err(s) => {
cx.tcx.sess.fatal(&s);
}
};
let did = cx.tcx.hir().get_parent_did(expr.hir_id);
let param_env = cx.tcx.param_env(did);
let must_use_reason = cx.tcx.infer_ctxt().enter(|infcx| {
// If `MustUse` is implemented, we want to know the `REASON` value, so we have to
// use the selection context directly and obtain a `VtableImpl`.
let substs = infcx.tcx.mk_substs_trait(ty, &[]);
let trait_ref = ty::TraitRef { def_id: must_use_trait, substs };
let pred = TraitPredicate { trait_ref };
let obl = Obligation::new(ObligationCause::dummy(), param_env, Binder::bind(pred));

let mut selcx = SelectionContext::new(&infcx);
let vtable = if let Ok(Some(vtable)) = selcx.select(&obl) {
vtable
} else {
// Not implemented.
return None;
};

// There is an impl that *might* apply. Make sure it does.
let mut fulfillcx = FulfillmentContext::new();
fulfillcx.register_bound(
&infcx,
param_env,
ty,
must_use_trait,
ObligationCause::dummy(),
);
if !fulfillcx.select_all_or_error(&infcx).is_ok() {
// It does not hold.
return None;
}

let data = match vtable {
// When a concrete impl is known to apply, use its `REASON` value (or any
// parent's in the specialization graph).
Vtable::VtableImpl(data) => data,

// We might get a `VtableParam` if we have a `T: MustUse` bound. We do lint in
// this case, but cannot know the message.
Vtable::VtableParam(_) => return Some(String::new()),

// `MustUse` isn't object safe, but could be if assoc. consts were object safe.
// In case that ever happens, don't lint on `dyn MustUse`.
Vtable::VtableObject(_) => return None,

_ => {
bug!("unexpected vtable for MustUse selection: {:?}", vtable);
}
};

let reason_const = if let Ok(ancestors) =
specialization_graph::ancestors(infcx.tcx, must_use_trait, data.impl_def_id)
{
ancestors
.leaf_def(infcx.tcx, Ident::with_dummy_span(sym::REASON), AssocKind::Const)
.expect("could not find MustUse::REASON")
.item
} else {
// Error building the specialization graph. Don't warn.
return None;
};

let const_val = if let Ok(const_val) =
infcx.const_eval_resolve(param_env, reason_const.def_id, substs, None, None)
{
const_val
} else {
return None;
};

if let ConstValue::Slice { data, start, end } = const_val {
let bytes = data.inspect_with_undef_and_ptr_outside_interpreter(start..end);
Some(String::from_utf8(bytes.to_vec()).unwrap())
} else {
bug!("unexpected ConstValue for MustUse::REASON: {:?}", const_val);
}
});

if let Some(reason) = must_use_reason {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let msg =
format!("unused {}`{}`{} that must be used", descr_pre, ty, descr_post);
let mut err = lint.build(&msg);
if !reason.is_empty() {
err.note(&reason);
}
err.emit();
});
return true;
}

let plural_suffix = pluralize!(plural_len);

match ty.kind {
@@ -142,7 +251,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
let descr_pre = &format!("{}boxed ", descr_pre);
check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural_len)
}
ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post),
ty::Opaque(def, _) => {
let mut has_emitted = false;
for (predicate, _) in cx.tcx.predicates_of(def).predicates {
@@ -218,7 +326,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
descr_post_path: &str,
) -> bool {
for attr in cx.tcx.get_attrs(def_id).iter() {
if attr.check_name(sym::must_use) {
if attr.check_name(sym::rustc_must_use) {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
let msg = format!(
"unused {}`{}`{} that must be used",
4 changes: 2 additions & 2 deletions src/librustc_middle/middle/stability.rs
Original file line number Diff line number Diff line change
@@ -201,7 +201,7 @@ pub fn early_report_deprecation(
lint: &'static Lint,
span: Span,
) {
if span.in_derive_expansion() {
if span.in_expansion_ignoring_deprecation() {
return;
}

@@ -217,7 +217,7 @@ fn late_report_deprecation(
span: Span,
hir_id: HirId,
) {
if span.in_derive_expansion() {
if span.in_expansion_ignoring_deprecation() {
return;
}

2 changes: 1 addition & 1 deletion src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
@@ -293,7 +293,7 @@ impl<'a> base::Resolver for Resolver<'a> {
invoc_id.set_expn_data(ext.expn_data(parent_scope.expansion, span, fast_print_path(path)));

if let Res::Def(_, def_id) = res {
if after_derive {
if after_derive && !ext.is_builtin {
self.session.span_err(span, "macro attributes must be placed before `#[derive]`");
}
self.macro_defs.insert(invoc_id, def_id);
14 changes: 11 additions & 3 deletions src/librustc_span/lib.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
#![feature(nll)]
#![feature(optin_builtin_traits)]
#![feature(specialization)]
#![feature(or_patterns)]

use rustc_data_structures::AtomicRef;
use rustc_macros::HashStable_Generic;
@@ -286,9 +287,16 @@ impl Span {
self.ctxt() != SyntaxContext::root()
}

/// Returns `true` if `span` originates in a derive-macro's expansion.
pub fn in_derive_expansion(self) -> bool {
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
/// Returns `true` if `span` originates in a macro expansion that should ignore `#[deprecated]`
/// items.
///
/// This is used to avoid undesired warnings when a type is marked as `#[deprecated]`.
pub fn in_expansion_ignoring_deprecation(self) -> bool {
match self.ctxt().outer_expn_data().kind {
ExpnKind::Macro(MacroKind::Derive, _) => true,
ExpnKind::Macro(MacroKind::Attr, sym::must_use) => true,
_ => false,
}
}

#[inline]
3 changes: 3 additions & 0 deletions src/librustc_span/symbol.rs
Original file line number Diff line number Diff line change
@@ -470,6 +470,7 @@ symbols! {
movbe_target_feature,
mul_with_overflow,
must_use,
MustUse,
naked,
naked_functions,
name,
@@ -588,6 +589,7 @@ symbols! {
Rc,
Ready,
reason,
REASON,
recursion_limit,
reexport_test_harness_main,
reflect,
@@ -647,6 +649,7 @@ symbols! {
rustc_layout_scalar_valid_range_start,
rustc_macro_transparency,
rustc_mir,
rustc_must_use,
rustc_nonnull_optimization_guaranteed,
rustc_object_lifetime_default,
rustc_on_unimplemented,
12 changes: 12 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -506,6 +506,18 @@ impl Attributes {
let other_attrs = attrs
.iter()
.filter_map(|attr| {
// We want to render `#[must_use]`, but that's a macro that expands to
// `#[rustc_must_use]` (and possibly a `MustUse` impl), so turn that back into
// `#[must_use]` here.
if attr.name_or_empty().as_str() == "rustc_must_use" {
let mut attr = attr.clone();
if let ast::AttrKind::Normal(item) = &mut attr.kind {
item.path = ast::Path::from_ident(Ident::new(sym::must_use, DUMMY_SP));
}

return Some(attr);
}

if let Some(value) = attr.doc_str() {
let (value, mk_fragment): (_, fn(_, _, _) -> _) = if attr.is_doc_comment() {
(strip_doc_comment_decoration(&value.as_str()), DocFragment::SugaredDoc)
5 changes: 5 additions & 0 deletions src/libstd/prelude/v1.rs
Original file line number Diff line number Diff line change
@@ -62,6 +62,11 @@ pub use core::prelude::v1::{
#[doc(hidden)]
pub use core::prelude::v1::cfg_accessible;

#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
#[cfg(not(bootstrap))]
#[doc(hidden)]
pub use core::prelude::v1::must_use;

// The file so far is equivalent to src/libcore/prelude/v1.rs,
// and below to src/liballoc/prelude.rs.
// Those files are duplicated rather than using glob imports
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ trait Sized {}
trait Copy {}
#[lang = "freeze"]
trait Freeze {}
#[lang = "must_use"]
trait MustUse {
const REASON: &'static str = "";
}

impl<T: ?Sized> Copy for *mut T {}

Original file line number Diff line number Diff line change
@@ -77,7 +77,6 @@
#![link_name = "1900"]
#![link_section = "1800"]
// see issue-43106-gating-of-rustc_deprecated.rs
#![must_use]
// see issue-43106-gating-of-stable.rs
// see issue-43106-gating-of-unstable.rs
// see issue-43106-gating-of-deprecated.rs
374 changes: 187 additions & 187 deletions src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/test/ui/feature-gate/issue-43106-gating-of-must_use.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// The non-crate level cases are in issue-43106-gating-of-builtin-attrs.rs.

#![must_use = "4200"]
//~^ ERROR cannot determine resolution for the attribute macro `must_use`

fn main() {}
10 changes: 10 additions & 0 deletions src/test/ui/feature-gate/issue-43106-gating-of-must_use.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: cannot determine resolution for the attribute macro `must_use`
--> $DIR/issue-43106-gating-of-must_use.rs:3:4
|
LL | #![must_use = "4200"]
| ^^^^^^^^
|
= note: import resolution is stuck, try simplifying macro imports

error: aborting due to previous error

32 changes: 32 additions & 0 deletions src/test/ui/lint/must-use-trait-blanket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Tests the `MustUse` trait implemented with a conditional blanket impl.
#![feature(must_use_trait)]
#![deny(unused_must_use)] //~ NOTE the lint level is defined here

use std::marker::MustUse;

struct Box<T>(T);

impl<T> Box<T> {
fn new(t: T) -> Self {
Box(t)
}
}

impl<T: MustUse> MustUse for Box<T> {
const REASON: &'static str = T::REASON;
}

struct St;

impl MustUse for St {
const REASON: &'static str = "because I said so";
}

fn main() {
Box::new(St);
//~^ ERROR unused `Box<St>` that must be used
//~| NOTE because I said so

Box::new(());
}
15 changes: 15 additions & 0 deletions src/test/ui/lint/must-use-trait-blanket.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: unused `Box<St>` that must be used
--> $DIR/must-use-trait-blanket.rs:27:5
|
LL | Box::new(St);
| ^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/must-use-trait-blanket.rs:4:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
= note: because I said so

error: aborting due to previous error

18 changes: 18 additions & 0 deletions src/test/ui/lint/must-use-trait-in-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! Tests that `T: MustUse` bounds do emit the lint.
//!
//! This is rather useless, but at least shouldn't ICE. Maybe it should be forbidden to use
//! `MustUse` in bounds.
#![feature(must_use_trait)]
#![deny(unused_must_use)] //~ NOTE the lint level is defined here

use std::marker::MustUse;

trait Tr: MustUse {}

fn f<T: MustUse>(t: T) {
t;
//~^ ERROR unused `T` that must be used
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/lint/must-use-trait-in-bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unused `T` that must be used
--> $DIR/must-use-trait-in-bounds.rs:14:5
|
LL | t;
| ^^
|
note: the lint level is defined here
--> $DIR/must-use-trait-in-bounds.rs:7:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^

error: aborting due to previous error

22 changes: 22 additions & 0 deletions src/test/ui/lint/must-use-trait-message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![feature(must_use_trait)]
#![deny(unused_must_use)] //~ NOTE the lint level is defined here

use std::marker::MustUse;

struct St;

impl St {
fn new() -> Self {
St
}
}

impl MustUse for St {
const REASON: &'static str = "because I said so";
}

fn main() {
St::new();
//~^ ERROR unused `St` that must be used
//~| NOTE because I said so
}
15 changes: 15 additions & 0 deletions src/test/ui/lint/must-use-trait-message.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: unused `St` that must be used
--> $DIR/must-use-trait-message.rs:19:5
|
LL | St::new();
| ^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/must-use-trait-message.rs:2:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
= note: because I said so

error: aborting due to previous error

18 changes: 18 additions & 0 deletions src/test/ui/lint/must-use-trait-simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![feature(must_use_trait)]
#![deny(unused_must_use)]

use std::marker::MustUse;

struct St;

impl St {
fn new() -> Self {
St
}
}

impl MustUse for St {}

fn main() {
St::new(); //~ ERROR unused `St` that must be used
}
14 changes: 14 additions & 0 deletions src/test/ui/lint/must-use-trait-simple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unused `St` that must be used
--> $DIR/must-use-trait-simple.rs:17:5
|
LL | St::new();
| ^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/must-use-trait-simple.rs:2:9
|
LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^

error: aborting due to previous error

14 changes: 14 additions & 0 deletions src/test/ui/lint/must_use-default-ty-param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Tests that the lowering of `#[must_use]` compiles in presence of defaulted type parameters.
// check-pass

#![deny(unused_must_use)]

trait Tr {
type Assoc;
}

#[must_use]
struct Generic<T: Tr, U = <T as Tr>::Assoc>(T, U);

fn main() {}
11 changes: 11 additions & 0 deletions src/test/ui/lint/must_use-forbid-deprecated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Tests that the lowering of `#[must_use]` compiles in presence of `#![forbid(deprecated)]`.
// check-pass

#![deny(unused_must_use)]
#![forbid(deprecated)]

#[must_use]
struct S;

fn main() {}
10 changes: 5 additions & 5 deletions src/test/ui/lint/must_use-tuple.rs
Original file line number Diff line number Diff line change
@@ -5,13 +5,13 @@ fn foo() -> (Result<(), ()>, ()) {
}

fn main() {
(Ok::<(), ()>(()),); //~ ERROR unused `std::result::Result`
(Ok::<(), ()>(()),); //~ ERROR unused `std::result::Result

(Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
//~^ ERROR unused `std::result::Result`
//~^^ ERROR unused `std::result::Result`
//~^ ERROR unused `std::result::Result
//~| ERROR unused `std::result::Result

foo(); //~ ERROR unused `std::result::Result`
foo(); //~ ERROR unused `std::result::Result

((Err::<(), ()>(()), ()), ()); //~ ERROR unused `std::result::Result`
((Err::<(), ()>(()), ()), ()); //~ ERROR unused `std::result::Result
}
10 changes: 5 additions & 5 deletions src/test/ui/lint/must_use-tuple.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: unused `std::result::Result` in tuple element 0 that must be used
error: unused `std::result::Result<(), ()>` in tuple element 0 that must be used
--> $DIR/must_use-tuple.rs:8:6
|
LL | (Ok::<(), ()>(()),);
@@ -11,31 +11,31 @@ LL | #![deny(unused_must_use)]
| ^^^^^^^^^^^^^^^
= note: this `Result` may be an `Err` variant, which should be handled

error: unused `std::result::Result` in tuple element 0 that must be used
error: unused `std::result::Result<(), ()>` in tuple element 0 that must be used
--> $DIR/must_use-tuple.rs:10:6
|
LL | (Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
| ^^^^^^^^^^^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled

error: unused `std::result::Result` in tuple element 2 that must be used
error: unused `std::result::Result<(), ()>` in tuple element 2 that must be used
--> $DIR/must_use-tuple.rs:10:27
|
LL | (Ok::<(), ()>(()), 0, Ok::<(), ()>(()), 5);
| ^^^^^^^^^^^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled

error: unused `std::result::Result` in tuple element 0 that must be used
error: unused `std::result::Result<(), ()>` in tuple element 0 that must be used
--> $DIR/must_use-tuple.rs:14:5
|
LL | foo();
| ^^^^^^
|
= note: this `Result` may be an `Err` variant, which should be handled

error: unused `std::result::Result` in tuple element 0 that must be used
error: unused `std::result::Result<(), ()>` in tuple element 0 that must be used
--> $DIR/must_use-tuple.rs:16:6
|
LL | ((Err::<(), ()>(()), ()), ());
2 changes: 1 addition & 1 deletion src/test/ui/macros/must-use-in-macro-55516.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
warning: unused `std::result::Result` that must be used
warning: unused `std::result::Result<(), std::fmt::Error>` that must be used
--> $DIR/must-use-in-macro-55516.rs:9:5
|
LL | write!(&mut example, "{}", 42);
20 changes: 10 additions & 10 deletions src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ extern "C" {
/// Bar
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -23,7 +23,7 @@ type FnType = fn(
/// Bar
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -38,7 +38,7 @@ pub fn foo(
/// Bar
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -58,7 +58,7 @@ impl SelfStruct {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -73,7 +73,7 @@ impl SelfStruct {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -94,7 +94,7 @@ impl RefStruct {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -113,7 +113,7 @@ trait RefTrait {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -128,7 +128,7 @@ trait RefTrait {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -148,7 +148,7 @@ impl RefTrait for RefStruct {
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Qux
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32,
@@ -165,7 +165,7 @@ fn main() {
/// Bar
//~^ ERROR documentation comments cannot be applied to function
#[must_use]
//~^ ERROR allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in
//~^ ERROR expected an inert attribute, found an attribute macro
/// Baz
//~^ ERROR documentation comments cannot be applied to function
#[no_mangle] b: i32
120 changes: 60 additions & 60 deletions src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr
Original file line number Diff line number Diff line change
@@ -4,60 +4,120 @@ error: expected an inert attribute, found an attribute macro
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:9:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:21:5
|
LL | #[test] a: u32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:25:5
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:36:5
|
LL | #[test] a: u32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:40:5
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:56:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:60:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:71:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:75:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:92:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:96:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:111:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:115:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:126:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:130:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:146:9
|
LL | #[test] a: i32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:150:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:163:9
|
LL | #[test] a: u32,
| ^^^^^^^

error: expected an inert attribute, found an attribute macro
--> $DIR/param-attrs-builtin-attrs.rs:167:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:3:9
|
@@ -70,12 +130,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:9:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:11:9
|
@@ -100,12 +154,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:25:5
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:27:5
|
@@ -130,12 +178,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:40:5
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:42:5
|
@@ -166,12 +208,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:60:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:62:9
|
@@ -196,12 +232,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:75:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:77:9
|
@@ -232,12 +262,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:96:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:98:9
|
@@ -268,12 +292,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:115:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:117:9
|
@@ -298,12 +316,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:130:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:132:9
|
@@ -334,12 +346,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Baz
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:150:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:152:9
|
@@ -364,12 +370,6 @@ error: documentation comments cannot be applied to function parameters
LL | /// Bar
| ^^^^^^^ doc comments are not allowed here

error: allow, cfg, cfg_attr, deny, forbid, and warn are the only allowed built-in attributes in function parameters
--> $DIR/param-attrs-builtin-attrs.rs:167:9
|
LL | #[must_use]
| ^^^^^^^^^^^

error: documentation comments cannot be applied to function parameters
--> $DIR/param-attrs-builtin-attrs.rs:169:9
|