From 829651a47411dd60392f3e840017a5339f3ee94f Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 1 Jan 2026 18:53:27 +0100 Subject: [PATCH] Introduce #[diagnostic::on_move] This might be helpful for smart pointers to explains why they aren't Copy and what to do instead or just to let the user know that .clone() is very cheap and can be called without a performance penalty. --- Cargo.lock | 1 + compiler/rustc_attr_parsing/Cargo.toml | 1 + .../rustc_attr_parsing/src/attributes/mod.rs | 1 + .../src/attributes/on_move.rs | 106 ++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_borrowck/src/borrowck_errors.rs | 25 +++-- .../src/diagnostics/conflict_errors.rs | 35 ++++-- .../rustc_hir/src/attrs/data_structures.rs | 31 +++++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_lint/messages.ftl | 6 + compiler/rustc_lint/src/early/diagnostics.rs | 6 + compiler/rustc_lint/src/lints.rs | 12 ++ compiler/rustc_lint_defs/src/lib.rs | 4 + compiler/rustc_passes/messages.ftl | 3 + compiler/rustc_passes/src/check_attr.rs | 19 ++++ compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../on_move/on_move_simple.rs | 15 +++ .../on_move/on_move_simple.stderr | 29 +++++ .../on_move/on_move_with_format.rs | 15 +++ .../on_move/on_move_with_format.stderr | 29 +++++ .../report_warning_on_duplicated_options.rs | 20 ++++ ...eport_warning_on_duplicated_options.stderr | 50 +++++++++ .../report_warning_on_invalid_formats.rs | 16 +++ .../report_warning_on_invalid_formats.stderr | 38 +++++++ .../report_warning_on_malformed_options.rs | 20 ++++ ...report_warning_on_malformed_options.stderr | 48 ++++++++ .../report_warning_on_missing_options.rs | 12 ++ .../report_warning_on_missing_options.stderr | 43 +++++++ .../on_move/report_warning_on_non_adt.rs | 21 ++++ .../on_move/report_warning_on_non_adt.stderr | 41 +++++++ .../report_warning_on_unknown_options.rs | 17 +++ .../report_warning_on_unknown_options.stderr | 38 +++++++ 33 files changed, 690 insertions(+), 18 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/on_move.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_simple.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs create mode 100644 tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr 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`.