diff --git a/Cargo.lock b/Cargo.lock index 01300d56cff9c..9116adf151dc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3548,6 +3548,7 @@ dependencies = [ "rustc_lexer", "rustc_macros", "rustc_parse", + "rustc_parse_format", "rustc_session", "rustc_span", "rustc_target", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 79193f394fe43..b60f6b41b7b46 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } +rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0d328d5cc6a70..123c6f7d9d10e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -53,6 +53,7 @@ pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod no_link; pub(crate) mod non_exhaustive; +pub(crate) mod on_move; pub(crate) mod path; pub(crate) mod pin_v2; pub(crate) mod proc_macro_attrs; diff --git a/compiler/rustc_attr_parsing/src/attributes/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/on_move.rs new file mode 100644 index 0000000000000..061b1a683e704 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/on_move.rs @@ -0,0 +1,106 @@ +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; + }; + 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 e500be68e2410..b9036e1bfb8e9 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -59,6 +59,7 @@ use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::no_link::NoLinkParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; +use crate::attributes::on_move::OnMoveParser; use crate::attributes::path::PathParser as PathAttributeParser; use crate::attributes::pin_v2::PinV2Parser; use crate::attributes::proc_macro_attrs::{ @@ -225,6 +226,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 caf6a86af098a..22609e929256c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -9,9 +9,12 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; 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 +141,19 @@ 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.get_all_attrs(item_def.did()), AttributeKind::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 8a7dee15d4f48..80ae02d79fe44 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::*; @@ -568,6 +569,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 | @@ -984,6 +1012,9 @@ pub enum AttributeKind { /// Represents `#[rustc_objc_selector]` ObjcSelector { methname: Symbol, span: Span }, + /// Represents `#[diagnostic::on_move]` + OnMove(Box), + /// 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 356575416aded..e7ef6d84061c4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -89,6 +89,7 @@ impl AttributeKind { NonExhaustive(..) => Yes, // Needed for rustdoc ObjcClass { .. } => No, ObjcSelector { .. } => No, + OnMove { .. } => Yes, Optimize(..) => No, PanicRuntime => No, ParenSugar(..) => No, diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 867b937d4090d..691352ffa1263 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -558,6 +558,12 @@ lint_macro_expr_fragment_specifier_2024_migration = lint_malformed_attribute = malformed lint attribute input +lint_malformed_on_move_attr = unknown or malformed `on_move` attribute + .help = only `message` and `label` are allowed as options + .label = invalid option found here + +lint_malformed_on_move_format_literals = unknown parameter `{$name}` + .help = expect {"`{Self}`"} as format argument lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated .function_label = this function returns `()`, which is likely not what you wanted diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 377b3c439453e..35fda7b3e1601 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -428,5 +428,11 @@ pub fn decorate_attribute_lint( sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), } .decorate_lint(diag), + + &AttributeLintKind::OnMoveMalformedAttr => lints::OnMoveMalformedAttr.decorate_lint(diag), + + &AttributeLintKind::OnMoveMalformedFormatLiterals { name } => { + lints::OnMoveMalformedFormatLiterals { name }.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a20d90e1227e9..8ae10f8115be2 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3325,3 +3325,15 @@ pub(crate) struct UnknownCrateTypesSuggestion { pub span: Span, pub snippet: Symbol, } + +#[derive(LintDiagnostic)] +#[diag(lint_malformed_on_move_attr)] +#[help] +pub(crate) struct OnMoveMalformedAttr; + +#[derive(LintDiagnostic)] +#[diag(lint_malformed_on_move_format_literals)] +#[help] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c78287b7784f..d06d071f6b9b4 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -826,6 +826,10 @@ pub enum AttributeLintKind { span: Span, suggested: Option, }, + OnMoveMalformedAttr, + OnMoveMalformedFormatLiterals { + name: Symbol, + }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 395d940ddae62..0449de599c046 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -91,6 +91,9 @@ passes_diagnostic_diagnostic_on_const_only_for_trait_impls = `#[diagnostic::on_const]` can only be applied to trait impls .label = not a trait impl +passes_diagnostic_diagnostic_on_move_only_for_adt = + `#[diagnostic::on_move]` can only be applied to enums, structs or unions + passes_diagnostic_diagnostic_on_unimplemented_only_for_traits = `#[diagnostic::on_unimplemented]` can only be applied to trait definitions diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cdd141f9233e8..a5d83973a9a51 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -67,6 +67,10 @@ struct DiagnosticOnConstOnlyForTraitImpls { item_span: Span, } +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_move_only_for_adt)] +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, @@ -224,6 +228,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_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::AllowIncoherentImpl(..) @@ -643,6 +650,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); } + /// 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 8f05ad7924e33..92a1a9a3df446 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -691,7 +691,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 5767444025ec2..f9da6d0ee0ab3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1620,6 +1620,7 @@ symbols! { omit_gdb_pretty_printer_section, on, on_const, + on_move, on_unimplemented, opaque, opaque_module_name_placeholder: "", 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..95150a50346d5 --- /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.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.rs new file mode 100644 index 0000000000000..a7f8f3b0f600e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.rs @@ -0,0 +1,20 @@ +#[diagnostic::on_move( + message = Foo, + //~^ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression + label = "Bar", +)] +#[diagnostic::on_move( + message = "Foo" + label = "Bar", + //~^ERROR expected `,`, found `label` +)] +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.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.stderr new file mode 100644 index 0000000000000..ea20aa4d38746 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.stderr @@ -0,0 +1,48 @@ +error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression + --> $DIR/report_warning_on_malformed_options.rs:2:15 + | +LL | message = Foo, + | ^^^ expressions are not allowed here + | +help: surround the identifier with quotation marks to make it into a string literal + | +LL | message = "Foo", + | + + + +error: expected `,`, found `label` + --> $DIR/report_warning_on_malformed_options.rs:8:5 + | +LL | message = "Foo" + | - expected `,` +LL | label = "Bar", + | ^^^^^ unexpected token + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options.rs:18: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.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_malformed_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 3 previous errors + +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`.