Skip to content

Commit 8071a7d

Browse files
committed
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.
1 parent 625b63f commit 8071a7d

35 files changed

+683
-20
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,6 +3548,7 @@ dependencies = [
35483548
"rustc_lexer",
35493549
"rustc_macros",
35503550
"rustc_parse",
3551+
"rustc_parse_format",
35513552
"rustc_session",
35523553
"rustc_span",
35533554
"rustc_target",

compiler/rustc_attr_parsing/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
1515
rustc_lexer = { path = "../rustc_lexer" }
1616
rustc_macros = { path = "../rustc_macros" }
1717
rustc_parse = { path = "../rustc_parse" }
18+
rustc_parse_format = { path = "../rustc_parse_format" }
1819
rustc_session = { path = "../rustc_session" }
1920
rustc_span = { path = "../rustc_span" }
2021
rustc_target = { path = "../rustc_target" }

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub(crate) mod must_use;
5353
pub(crate) mod no_implicit_prelude;
5454
pub(crate) mod no_link;
5555
pub(crate) mod non_exhaustive;
56+
pub(crate) mod on_move;
5657
pub(crate) mod path;
5758
pub(crate) mod pin_v2;
5859
pub(crate) mod proc_macro_attrs;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use rustc_feature::{AttributeTemplate, template};
2+
use rustc_hir::attrs::{AttributeKind, OnMoveAttr};
3+
use rustc_hir::lints::AttributeLintKind;
4+
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
5+
use rustc_session::lint::builtin::{
6+
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
7+
};
8+
use rustc_span::{InnerSpan, Span, Symbol, kw, sym};
9+
use thin_vec::ThinVec;
10+
11+
use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
12+
use crate::context::{AcceptContext, Stage};
13+
use crate::parser::ArgParser;
14+
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
15+
16+
pub(crate) struct OnMoveParser;
17+
18+
impl<S: Stage> SingleAttributeParser<S> for OnMoveParser {
19+
const PATH: &[Symbol] = &[sym::diagnostic, sym::on_move];
20+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
21+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
22+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); // Check in check_attr.
23+
const TEMPLATE: AttributeTemplate = template!(List: &["message", "label"]);
24+
25+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
26+
let get_format_ranges = |cx: &mut AcceptContext<'_, '_, S>,
27+
symbol: &Symbol,
28+
span: Span|
29+
-> ThinVec<(usize, usize)> {
30+
let mut parser = Parser::new(symbol.as_str(), None, None, false, ParseMode::Diagnostic);
31+
let pieces: Vec<_> = parser.by_ref().collect();
32+
let mut spans = ThinVec::new();
33+
34+
for piece in pieces {
35+
match piece {
36+
Piece::NextArgument(arg) => match arg.position {
37+
Position::ArgumentNamed(name) if name == kw::SelfUpper.as_str() => {
38+
spans.push((arg.position_span.start - 2, arg.position_span.end));
39+
}
40+
Position::ArgumentNamed(name) => {
41+
let span = span.from_inner(InnerSpan {
42+
start: arg.position_span.start,
43+
end: arg.position_span.end,
44+
});
45+
cx.emit_lint(
46+
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
47+
AttributeLintKind::OnMoveMalformedFormatLiterals {
48+
name: Symbol::intern(name),
49+
},
50+
span,
51+
)
52+
}
53+
_ => {}
54+
},
55+
_ => continue,
56+
}
57+
}
58+
spans
59+
};
60+
let Some(list) = args.list() else {
61+
cx.expected_list(cx.attr_span, args);
62+
return None;
63+
};
64+
let mut message = None;
65+
let mut label = None;
66+
67+
for item in list.mixed() {
68+
let Some(item) = item.meta_item() else {
69+
cx.expected_specific_argument(item.span(), &[sym::message, sym::label]);
70+
return None;
71+
};
72+
73+
let Some(name_value) = item.args().name_value() else {
74+
cx.expected_name_value(cx.attr_span, item.path().word_sym());
75+
return None;
76+
};
77+
78+
let Some(value) = name_value.value_as_str() else {
79+
cx.expected_string_literal(name_value.value_span, None);
80+
return None;
81+
};
82+
83+
let value_span = name_value.value_span;
84+
if item.path().word_is(sym::message) && message.is_none() {
85+
let spans = get_format_ranges(cx, &value, value_span);
86+
message = Some(OnMoveAttr::new(value, spans));
87+
continue;
88+
} else if item.path().word_is(sym::label) && label.is_none() {
89+
let spans = get_format_ranges(cx, &value, value_span);
90+
label = Some(OnMoveAttr::new(value, spans));
91+
continue;
92+
}
93+
94+
cx.emit_lint(
95+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
96+
AttributeLintKind::OnMoveMalformedAttr,
97+
item.span(),
98+
)
99+
}
100+
Some(AttributeKind::OnMove { span: cx.attr_span, message, label })
101+
}
102+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use crate::attributes::must_use::MustUseParser;
5757
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
5858
use crate::attributes::no_link::NoLinkParser;
5959
use crate::attributes::non_exhaustive::NonExhaustiveParser;
60+
use crate::attributes::on_move::OnMoveParser;
6061
use crate::attributes::path::PathParser as PathAttributeParser;
6162
use crate::attributes::pin_v2::PinV2Parser;
6263
use crate::attributes::proc_macro_attrs::{
@@ -222,6 +223,7 @@ attribute_parsers!(
222223
Single<MustUseParser>,
223224
Single<ObjcClassParser>,
224225
Single<ObjcSelectorParser>,
226+
Single<OnMoveParser>,
225227
Single<OptimizeParser>,
226228
Single<PatchableFunctionEntryParser>,
227229
Single<PathAttributeParser>,

compiler/rustc_borrowck/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ borrowck_ty_no_impl_copy =
216216
*[false] move
217217
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait
218218
219+
borrowck_unknown_format_parameter_for_on_move_attr = unknown parameter `{$argument_name}`
220+
.help = expect {"`{Self}`"} as format argument
219221
borrowck_use_due_to_use_closure =
220222
use occurs due to use in closure
221223

compiler/rustc_borrowck/src/borrowck_errors.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -324,18 +324,23 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
324324
verb: &str,
325325
optional_adverb_for_moved: &str,
326326
moved_path: Option<String>,
327+
primary_message: Option<String>,
327328
) -> Diag<'infcx> {
328-
let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
329+
if let Some(primary_message) = primary_message {
330+
struct_span_code_err!(self.dcx(), use_span, E0382, "{}", primary_message)
331+
} else {
332+
let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
329333

330-
struct_span_code_err!(
331-
self.dcx(),
332-
use_span,
333-
E0382,
334-
"{} of {}moved value{}",
335-
verb,
336-
optional_adverb_for_moved,
337-
moved_path,
338-
)
334+
struct_span_code_err!(
335+
self.dcx(),
336+
use_span,
337+
E0382,
338+
"{} of {}moved value{}",
339+
verb,
340+
optional_adverb_for_moved,
341+
moved_path,
342+
)
343+
}
339344
}
340345

341346
pub(crate) fn cannot_borrow_path_as_mutable_because(

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ use rustc_data_structures::fx::FxIndexSet;
99
use rustc_errors::codes::*;
1010
use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
1111
use rustc_hir as hir;
12+
use rustc_hir::attrs::AttributeKind;
1213
use rustc_hir::def::{DefKind, Res};
1314
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
14-
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField};
15+
use rustc_hir::{
16+
CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr,
17+
};
1518
use rustc_middle::bug;
1619
use rustc_middle::hir::nested_filter::OnlyBodies;
1720
use rustc_middle::mir::{
@@ -138,6 +141,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
138141
let partial_str = if is_partial_move { "partial " } else { "" };
139142
let partially_str = if is_partial_move { "partially " } else { "" };
140143

144+
let (on_move_message, on_move_label) = if let ty::Adt(item_def, _) =
145+
self.body.local_decls[moved_place.local].ty.kind()
146+
&& let Some((message, label)) = find_attr!(self.infcx.tcx.get_all_attrs(item_def.did()), AttributeKind::OnMove { message, label, .. } => (message, label))
147+
{
148+
let item_name = self.infcx.tcx.item_name(item_def.did());
149+
(
150+
message.as_ref().map(|e| e.format_args_with(item_name.as_str())),
151+
label.as_ref().map(|e| e.format_args_with(item_name.as_str())),
152+
)
153+
} else {
154+
(None, None)
155+
};
156+
141157
let mut err = self.cannot_act_on_moved_value(
142158
span,
143159
desired_action.as_noun(),
@@ -146,6 +162,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
146162
moved_place,
147163
DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
148164
),
165+
on_move_message,
149166
);
150167

151168
let reinit_spans = maybe_reinitialized_locations
@@ -275,12 +292,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
275292
if needs_note {
276293
if let Some(local) = place.as_local() {
277294
let span = self.body.local_decls[local].source_info.span;
278-
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
279-
is_partial_move,
280-
ty,
281-
place: &note_msg,
282-
span,
283-
});
295+
if let Some(on_move_label) = on_move_label {
296+
err.span_label(span, on_move_label);
297+
} else {
298+
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
299+
is_partial_move,
300+
ty,
301+
place: &note_msg,
302+
span,
303+
});
304+
}
284305
} else {
285306
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
286307
is_partial_move,

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::borrow::Cow;
2+
use std::ops::Range;
23
use std::path::PathBuf;
34

45
pub use ReprAttr::*;
@@ -568,6 +569,26 @@ impl<E: rustc_span::SpanEncoder> rustc_serialize::Encodable<E> for DocAttribute
568569
}
569570
}
570571

572+
#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic, PrintAttribute)]
573+
pub struct OnMoveAttr {
574+
pub symbol: Symbol,
575+
pub format_ranges: ThinVec<(usize, usize)>,
576+
}
577+
578+
impl OnMoveAttr {
579+
pub fn new(symbol: Symbol, format_ranges: ThinVec<(usize, usize)>) -> Self {
580+
Self { symbol, format_ranges }
581+
}
582+
583+
pub fn format_args_with(&self, item_name: &str) -> String {
584+
let mut arg = self.symbol.to_string();
585+
for fmt_idx in &self.format_ranges {
586+
arg.replace_range(Range { start: fmt_idx.0, end: fmt_idx.1 }, item_name);
587+
}
588+
arg
589+
}
590+
}
591+
571592
/// How to perform collapse macros debug info
572593
/// if-ext - if macro from different crate (related to callsite code)
573594
/// | cmd \ attr | no | (unspecified) | external | yes |
@@ -870,6 +891,9 @@ pub enum AttributeKind {
870891
/// Represents `#[rustc_objc_selector]`
871892
ObjcSelector { methname: Symbol, span: Span },
872893

894+
/// Represents `#[diagnostic::on_move]`
895+
OnMove { span: Span, message: Option<OnMoveAttr>, label: Option<OnMoveAttr> },
896+
873897
/// Represents `#[optimize(size|speed)]`
874898
Optimize(OptimizeAttr, Span),
875899

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl AttributeKind {
8585
NonExhaustive(..) => Yes, // Needed for rustdoc
8686
ObjcClass { .. } => No,
8787
ObjcSelector { .. } => No,
88+
OnMove { .. } => Yes,
8889
Optimize(..) => No,
8990
ParenSugar(..) => No,
9091
PassByValue(..) => Yes,

0 commit comments

Comments
 (0)