diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 8f114b3284485..cc270484ab63c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -22,6 +22,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem pub(crate) mod do_not_recommend; pub(crate) mod on_const; +pub(crate) mod on_move; pub(crate) mod on_unimplemented; #[derive(Copy, Clone)] diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs new file mode 100644 index 0000000000000..c8c53cfeb8cba --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -0,0 +1,116 @@ +use rustc_feature::{AttributeTemplate, template}; +use rustc_hir::attrs::{AttributeKind, OnMoveAttrArg, OnMoveAttribute}; +use rustc_hir::lints::AttributeLintKind; +use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_session::lint::builtin::{ + MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, +}; +use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; +use thin_vec::ThinVec; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::{ALL_TARGETS, AllowedTargets}; + +pub(crate) struct OnMoveParser; + +impl SingleAttributeParser for OnMoveParser { + const PATH: &[Symbol] = &[sym::diagnostic, sym::on_move]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Check in check_attr. + const TEMPLATE: AttributeTemplate = template!(List: &["message", "label"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let get_format_ranges = |cx: &mut AcceptContext<'_, '_, S>, + symbol: &Symbol, + span: Span| + -> ThinVec<(usize, usize)> { + let mut parser = Parser::new(symbol.as_str(), None, None, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); + let mut spans = ThinVec::new(); + + for piece in pieces { + match piece { + Piece::NextArgument(arg) => match arg.position { + Position::ArgumentNamed(name) if name == kw::SelfUpper.as_str() => { + spans.push((arg.position_span.start - 2, arg.position_span.end)); + } + Position::ArgumentNamed(name) => { + let span = span.from_inner(InnerSpan { + start: arg.position_span.start, + end: arg.position_span.end, + }); + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::OnMoveMalformedFormatLiterals { + name: Symbol::intern(name), + }, + span, + ) + } + _ => {} + }, + _ => continue, + } + } + spans + }; + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + if list.is_empty() { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter, + list.span, + ); + return None; + } + + let mut message = None; + let mut label = None; + + for item in list.mixed() { + let Some(item) = item.meta_item() else { + cx.expected_specific_argument(item.span(), &[sym::message, sym::label]); + return None; + }; + + let Some(name_value) = item.args().name_value() else { + cx.expected_name_value(cx.attr_span, item.path().word_sym()); + return None; + }; + + let Some(value) = name_value.value_as_str() else { + cx.expected_string_literal(name_value.value_span, None); + return None; + }; + + let value_span = name_value.value_span; + if item.path().word_is(sym::message) && message.is_none() { + let spans = get_format_ranges(cx, &value, value_span); + message = Some(OnMoveAttrArg::new(value, spans)); + continue; + } else if item.path().word_is(sym::label) && label.is_none() { + let spans = get_format_ranges(cx, &value, value_span); + label = Some(OnMoveAttrArg::new(value, spans)); + continue; + } + + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::OnMoveMalformedAttr, + item.span(), + ) + } + Some(AttributeKind::OnMove(Box::new(OnMoveAttribute { + span: cx.attr_span, + message, + label, + }))) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b82607e7c450d..61fb8e37423f8 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -28,6 +28,7 @@ use crate::attributes::debugger::*; use crate::attributes::deprecation::*; use crate::attributes::diagnostic::do_not_recommend::*; use crate::attributes::diagnostic::on_const::*; +use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; @@ -195,6 +196,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 0d3c554e41765..4fcb5f3b5a94d 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -324,18 +324,23 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { verb: &str, optional_adverb_for_moved: &str, moved_path: Option, + primary_message: Option, ) -> Diag<'infcx> { - let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); + if let Some(primary_message) = primary_message { + struct_span_code_err!(self.dcx(), use_span, E0382, "{}", primary_message) + } else { + let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); - struct_span_code_err!( - self.dcx(), - use_span, - E0382, - "{} of {}moved value{}", - verb, - optional_adverb_for_moved, - moved_path, - ) + struct_span_code_err!( + self.dcx(), + use_span, + E0382, + "{} of {}moved value{}", + verb, + optional_adverb_for_moved, + moved_path, + ) + } } pub(crate) fn cannot_borrow_path_as_mutable_because( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c2c07614bc0dd..3b27b6addd495 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -11,7 +11,9 @@ use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; -use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField}; +use rustc_hir::{ + CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::{ @@ -138,6 +140,20 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let partial_str = if is_partial_move { "partial " } else { "" }; let partially_str = if is_partial_move { "partially " } else { "" }; + let (on_move_message, on_move_label) = if let ty::Adt(item_def, _) = + self.body.local_decls[moved_place.local].ty.kind() + && let Some(attr) = + find_attr!(self.infcx.tcx, item_def.did(), OnMove(attr) => attr) + { + let item_name = self.infcx.tcx.item_name(item_def.did()); + ( + attr.message.as_ref().map(|e| e.format_args_with(item_name.as_str())), + attr.label.as_ref().map(|e| e.format_args_with(item_name.as_str())), + ) + } else { + (None, None) + }; + let mut err = self.cannot_act_on_moved_value( span, desired_action.as_noun(), @@ -146,6 +162,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { moved_place, DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ), + on_move_message, ); let reinit_spans = maybe_reinitialized_locations @@ -275,12 +292,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; - err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move, - ty, - place: ¬e_msg, - span, - }); + if let Some(on_move_label) = on_move_label { + err.span_label(span, on_move_label); + } else { + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move, + ty, + place: ¬e_msg, + span, + }); + } } else { err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { is_partial_move, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 68f5bb94c3fe8..cb1250797b9bb 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::ops::Range; use std::path::PathBuf; pub use ReprAttr::*; @@ -631,6 +632,33 @@ impl rustc_serialize::Encodable for DocAttribute } } +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)] +pub struct OnMoveAttrArg { + pub symbol: Symbol, + pub format_ranges: ThinVec<(usize, usize)>, +} + +impl OnMoveAttrArg { + pub fn new(symbol: Symbol, format_ranges: ThinVec<(usize, usize)>) -> Self { + Self { symbol, format_ranges } + } + + pub fn format_args_with(&self, item_name: &str) -> String { + let mut arg = self.symbol.to_string(); + for fmt_idx in &self.format_ranges { + arg.replace_range(Range { start: fmt_idx.0, end: fmt_idx.1 }, item_name); + } + arg + } +} + +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)] +pub struct OnMoveAttribute { + pub span: Span, + pub message: Option, + pub label: Option, +} + /// How to perform collapse macros debug info /// if-ext - if macro from different crate (related to callsite code) /// | cmd \ attr | no | (unspecified) | external | yes | @@ -1080,13 +1108,15 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::on_move]` + OnMove(Box), + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. OnUnimplemented { span: Span, /// None if the directive was malformed in some way. directive: Option>, }, - /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index c50d38b6d673a..d4789b2eee192 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,6 +77,7 @@ impl AttributeKind { NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc OnConst { .. } => Yes, + OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 5aee3f382ff3c..4fc39feb34249 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -473,5 +473,13 @@ pub fn decorate_attribute_lint( &AttributeLintKind::MissingOptionsForOnConst => { lints::MissingOptionsForOnConstAttr.decorate_lint(diag) } + &AttributeLintKind::OnMoveMalformedAttr => lints::OnMoveMalformedAttr.decorate_lint(diag), + + &AttributeLintKind::OnMoveMalformedFormatLiterals { name } => { + lints::OnMoveMalformedFormatLiterals { name }.decorate_lint(diag) + } + &AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter => { + lints::OnMoveMalformedAttrExpectedLiteralOrDelimiter.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 4b7e102e239ec..f48e014680272 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3932,3 +3932,20 @@ pub(crate) struct MalformedOnConstAttrLint { #[label("invalid option found here")] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag("unknown or malformed `on_move` attribute")] +#[help("only `message` and `label` are allowed as options")] +pub(crate) struct OnMoveMalformedAttr; + +#[derive(LintDiagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help("expect `Self` as format argument")] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag("expected a literal or missing delimiter")] +#[help("only literals are allowed as values of `message` and `label`, separated by a comma")] +pub(crate) struct OnMoveMalformedAttrExpectedLiteralOrDelimiter; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1492df50a418a..5619084fb5b20 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -855,6 +855,11 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + OnMoveMalformedAttr, + OnMoveMalformedFormatLiterals { + name: Symbol, + }, + OnMoveMalformedAttrExpectedLiteralOrDelimiter, } #[derive(Debug, Clone, HashStable_Generic)] diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 89bd39b77e64b..3169c8036e06d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -75,6 +75,10 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls { item_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")] +struct DiagnosticOnMoveOnlyForAdt; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -234,6 +238,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} + Attribute::Parsed(AttributeKind::OnMove(attr)) => { + self.check_diagnostic_on_move(attr.span, hir_id, target) + }, Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -693,6 +700,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // The traits' or the impls'? } + /// Checks if `#[diagnostic::on_move]` is applied to an ADT definition + fn check_diagnostic_on_move(&self, attr_span: Span, hir_id: HirId, target: Target) { + if !matches!(target, Target::Enum | Target::Struct | Target::Union) { + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnMoveOnlyForAdt, + ); + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 551d89ee6022a..54bca9be57650 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -707,7 +707,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } const DIAG_ATTRS: &[Symbol] = - &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const]; + &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const, sym::on_move]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3d40b7317459b..fd18f5e0abeb1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1403,6 +1403,7 @@ symbols! { omit_gdb_pretty_printer_section, on, on_const, + on_move, on_unimplemented, opaque, opaque_generic_const_args, diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs new file mode 100644 index 0000000000000..38444eb165a93 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs @@ -0,0 +1,15 @@ +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr new file mode 100644 index 0000000000000..44eb50f334b64 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr @@ -0,0 +1,29 @@ +error[E0382]: Foo + --> $DIR/on_move_simple.rs:13:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_simple.rs:8:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_simple.rs:6:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs new file mode 100644 index 0000000000000..195313dd5d061 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs @@ -0,0 +1,15 @@ +#[diagnostic::on_move( + message = "Foo for {Self}", + label = "Bar for {Self}", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo for Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr new file mode 100644 index 0000000000000..98ad58a3e5c6e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr @@ -0,0 +1,29 @@ +error[E0382]: Foo for Foo + --> $DIR/on_move_with_format.rs:13:15 + | +LL | let foo = Foo; + | --- Bar for Foo +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_with_format.rs:8:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_with_format.rs:6:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs new file mode 100644 index 0000000000000..3d4319c5bf5bc --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs @@ -0,0 +1,20 @@ +#[diagnostic::on_move( + //~^WARN unused attribute + //|= + message = "first message", + label = "first label", +)] +#[diagnostic::on_move( + message = "second message", + label = "second label", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR first message +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr new file mode 100644 index 0000000000000..1e5052e15803e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr @@ -0,0 +1,50 @@ +warning: unused attribute + --> $DIR/report_warning_on_duplicated_options.rs:1:1 + | +LL | / #[diagnostic::on_move( +LL | | +LL | | //|= +LL | | message = "first message", +LL | | label = "first label", +LL | | )] + | |__^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/report_warning_on_duplicated_options.rs:7:1 + | +LL | / #[diagnostic::on_move( +LL | | message = "second message", +LL | | label = "second label", +LL | | )] + | |__^ + = note: requested on the command line with `-W unused-attributes` + +error[E0382]: first message + --> $DIR/report_warning_on_duplicated_options.rs:18:15 + | +LL | let foo = Foo; + | --- first label +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_duplicated_options.rs:13:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_duplicated_options.rs:11:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs new file mode 100644 index 0000000000000..04ed63a5c162f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs @@ -0,0 +1,16 @@ +#[diagnostic::on_move( + message = "Foo {Baz}", + //~^WARN unknown parameter `Baz` + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr new file mode 100644 index 0000000000000..bc77f5780b2db --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr @@ -0,0 +1,38 @@ +warning: unknown parameter `Baz` + --> $DIR/report_warning_on_invalid_formats.rs:2:21 + | +LL | message = "Foo {Baz}", + | ^^^ + | + = help: expect `Self` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo {Baz} + --> $DIR/report_warning_on_invalid_formats.rs:14:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_invalid_formats.rs:9:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_invalid_formats.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs new file mode 100644 index 0000000000000..20f8bf1cd8a46 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs @@ -0,0 +1,16 @@ +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values of `message` and `label`, separated by a comma + message = "Foo" + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr new file mode 100644 index 0000000000000..2d7d3cbc7ff9a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:1:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = "Foo" +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values of `message` and `label`, separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:14:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:9:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs new file mode 100644 index 0000000000000..2a23a20e3d88e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs @@ -0,0 +1,16 @@ +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values of `message` and `label`, separated by a comma + message = Foo, + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr new file mode 100644 index 0000000000000..1a5266689d452 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_literals.rs:1:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = Foo, +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values of `message` and `label`, separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_literals.rs:14:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_literals.rs:9:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_literals.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs new file mode 100644 index 0000000000000..468e9f9123c19 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs @@ -0,0 +1,12 @@ +#[diagnostic::on_move] +//~^ERROR malformed `diagnostic::on_move` attribute input +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr new file mode 100644 index 0000000000000..506b042152763 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr @@ -0,0 +1,43 @@ +error[E0539]: malformed `diagnostic::on_move` attribute input + --> $DIR/report_warning_on_missing_options.rs:1:1 + | +LL | #[diagnostic::on_move] + | ^^^^^^^^^^^^^^^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[diagnostic::on_move(label)] + | +++++++ +LL | #[diagnostic::on_move(message)] + | +++++++++ + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_missing_options.rs:10:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_missing_options.rs:5:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_missing_options.rs:3:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0382, E0539. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs new file mode 100644 index 0000000000000..1128b6407be4f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs @@ -0,0 +1,21 @@ +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +struct Foo; + +#[diagnostic::on_move( +//~^WARN `#[diagnostic::on_move]` can only be applied to enums, structs or unions + message = "Foo", + label = "Bar", +)] +trait MyTrait {} + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr new file mode 100644 index 0000000000000..57a706e1f4b00 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr @@ -0,0 +1,41 @@ +warning: `#[diagnostic::on_move]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:7:1 + | +LL | / #[diagnostic::on_move( +LL | | +LL | | message = "Foo", +LL | | label = "Bar", +LL | | )] + | |__^ + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_non_adt.rs:19:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_non_adt.rs:14:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_non_adt.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs new file mode 100644 index 0000000000000..4aa7284155dcb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs @@ -0,0 +1,17 @@ +#[diagnostic::on_move( + message = "Foo", + label = "Bar", + baz="Baz" + //~^WARN unknown or malformed `on_move` attribute + //~|HELP only `message` and `label` are allowed as options +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr new file mode 100644 index 0000000000000..af6d794b63f7e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr @@ -0,0 +1,38 @@ +warning: unknown or malformed `on_move` attribute + --> $DIR/report_warning_on_unknown_options.rs:4:5 + | +LL | baz="Baz" + | ^^^^^^^^^ + | + = help: only `message` and `label` are allowed as options + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_unknown_options.rs:15:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_unknown_options.rs:10:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_unknown_options.rs:8:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`.