Skip to content

Commit 734cca0

Browse files
authored
Rollup merge of #150935 - rperier:provide_diagnostic_on_move_for_smart_pointers, r=JonathanBrouwer
Introduce #[diagnostic::on_move(message)] cc #149862 This is a first proposal. I have deliberately kept it simpler than `diagnostic::on_unimplemented`. Few questions/remarks: - Do I need to move the OnMoveDirective logic into a dedicated module perhaps ? let's say into compiler/rustc_borrowck/src/diagnostics/on_move.rs - No problems to depend on crates like `rustc_ast` from the borrowck ? - Notes are not supported yet. While message and label are very static , in the sense that they are emitted in the same way from the same place in the borrowck, it is not the case for the notes. It would make the code more complex. But, I can add support for notes if it does make sense. Suggestions are welcomed !
2 parents 319df85 + 965f1e7 commit 734cca0

41 files changed

Lines changed: 975 additions & 20 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem
2222

2323
pub(crate) mod do_not_recommend;
2424
pub(crate) mod on_const;
25+
pub(crate) mod on_move;
2526
pub(crate) mod on_unimplemented;
2627

2728
#[derive(Copy, Clone)]
@@ -32,6 +33,8 @@ pub(crate) enum Mode {
3233
DiagnosticOnUnimplemented,
3334
/// `#[diagnostic::on_const]`
3435
DiagnosticOnConst,
36+
/// `#[diagnostic::on_move]`
37+
DiagnosticOnMove,
3538
}
3639

3740
fn merge_directives<S: Stage>(
@@ -113,6 +116,13 @@ fn parse_directive_items<'p, S: Stage>(
113116
span,
114117
);
115118
}
119+
Mode::DiagnosticOnMove => {
120+
cx.emit_lint(
121+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
122+
AttributeLintKind::MalformedOnMoveAttr { span },
123+
span,
124+
);
125+
}
116126
}
117127
continue;
118128
}}
@@ -132,7 +142,7 @@ fn parse_directive_items<'p, S: Stage>(
132142
Mode::RustcOnUnimplemented => {
133143
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
134144
}
135-
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => {
145+
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => {
136146
cx.emit_lint(
137147
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
138148
AttributeLintKind::IgnoredDiagnosticOption {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use rustc_feature::template;
2+
use rustc_hir::attrs::AttributeKind;
3+
use rustc_hir::lints::AttributeLintKind;
4+
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
5+
use rustc_span::sym;
6+
7+
use crate::attributes::diagnostic::*;
8+
use crate::attributes::prelude::*;
9+
use crate::context::{AcceptContext, Stage};
10+
use crate::parser::ArgParser;
11+
use crate::target_checking::{ALL_TARGETS, AllowedTargets};
12+
13+
#[derive(Default)]
14+
pub(crate) struct OnMoveParser {
15+
span: Option<Span>,
16+
directive: Option<(Span, Directive)>,
17+
}
18+
19+
impl OnMoveParser {
20+
fn parse<'sess, S: Stage>(
21+
&mut self,
22+
cx: &mut AcceptContext<'_, 'sess, S>,
23+
args: &ArgParser,
24+
mode: Mode,
25+
) {
26+
if !cx.features().diagnostic_on_move() {
27+
return;
28+
}
29+
30+
let span = cx.attr_span;
31+
self.span = Some(span);
32+
let Some(list) = args.list() else {
33+
cx.emit_lint(
34+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
35+
AttributeLintKind::MissingOptionsForOnMove,
36+
span,
37+
);
38+
return;
39+
};
40+
41+
if list.is_empty() {
42+
cx.emit_lint(
43+
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
44+
AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter,
45+
list.span,
46+
);
47+
return;
48+
}
49+
50+
if let Some(directive) = parse_directive_items(cx, mode, list.mixed(), true) {
51+
merge_directives(cx, &mut self.directive, (span, directive));
52+
}
53+
}
54+
}
55+
impl<S: Stage> AttributeParser<S> for OnMoveParser {
56+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
57+
&[sym::diagnostic, sym::on_move],
58+
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
59+
|this, cx, args| {
60+
this.parse(cx, args, Mode::DiagnosticOnMove);
61+
},
62+
)];
63+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
64+
65+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
66+
if let Some(span) = self.span {
67+
Some(AttributeKind::OnMove { span, directive: self.directive.map(|d| Box::new(d.1)) })
68+
} else {
69+
None
70+
}
71+
}
72+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::attributes::debugger::*;
2929
use crate::attributes::deprecation::*;
3030
use crate::attributes::diagnostic::do_not_recommend::*;
3131
use crate::attributes::diagnostic::on_const::*;
32+
use crate::attributes::diagnostic::on_move::*;
3233
use crate::attributes::diagnostic::on_unimplemented::*;
3334
use crate::attributes::doc::*;
3435
use crate::attributes::dummy::*;
@@ -149,6 +150,7 @@ attribute_parsers!(
149150
MacroUseParser,
150151
NakedParser,
151152
OnConstParser,
153+
OnMoveParser,
152154
OnUnimplementedParser,
153155
RustcAlignParser,
154156
RustcAlignStaticParser,

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: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ use std::ops::ControlFlow;
66
use either::Either;
77
use hir::{ClosureKind, Path};
88
use rustc_data_structures::fx::FxIndexSet;
9+
use rustc_data_structures::thin_vec::ThinVec;
910
use rustc_errors::codes::*;
1011
use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
1112
use rustc_hir as hir;
13+
use rustc_hir::attrs::diagnostic::FormatArgs;
1214
use rustc_hir::def::{DefKind, Res};
1315
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};
14-
use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField};
16+
use rustc_hir::{
17+
CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr,
18+
};
1519
use rustc_middle::bug;
1620
use rustc_middle::hir::nested_filter::OnlyBodies;
1721
use rustc_middle::mir::{
@@ -138,6 +142,36 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
138142
let partial_str = if is_partial_move { "partial " } else { "" };
139143
let partially_str = if is_partial_move { "partially " } else { "" };
140144

145+
let (on_move_message, on_move_label, on_move_notes) = if let ty::Adt(item_def, args) =
146+
self.body.local_decls[moved_place.local].ty.kind()
147+
&& let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive)
148+
{
149+
let item_name = self.infcx.tcx.item_name(item_def.did()).to_string();
150+
let mut generic_args: Vec<_> = self
151+
.infcx
152+
.tcx
153+
.generics_of(item_def.did())
154+
.own_params
155+
.iter()
156+
.filter_map(|param| Some((param.name, args[param.index as usize].to_string())))
157+
.collect();
158+
generic_args.push((kw::SelfUpper, item_name));
159+
160+
let args = FormatArgs {
161+
this: String::new(),
162+
trait_sugared: String::new(),
163+
item_context: "",
164+
generic_args,
165+
};
166+
(
167+
directive.message.as_ref().map(|e| e.1.format(&args)),
168+
directive.label.as_ref().map(|e| e.1.format(&args)),
169+
directive.notes.iter().map(|e| e.format(&args)).collect(),
170+
)
171+
} else {
172+
(None, None, ThinVec::new())
173+
};
174+
141175
let mut err = self.cannot_act_on_moved_value(
142176
span,
143177
desired_action.as_noun(),
@@ -146,8 +180,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
146180
moved_place,
147181
DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
148182
),
183+
on_move_message,
149184
);
150185

186+
for note in on_move_notes {
187+
err.note(note);
188+
}
189+
151190
let reinit_spans = maybe_reinitialized_locations
152191
.iter()
153192
.take(3)
@@ -275,12 +314,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
275314
if needs_note {
276315
if let Some(local) = place.as_local() {
277316
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-
});
317+
if let Some(on_move_label) = on_move_label {
318+
err.span_label(span, on_move_label);
319+
} else {
320+
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
321+
is_partial_move,
322+
ty,
323+
place: &note_msg,
324+
span,
325+
});
326+
}
284327
} else {
285328
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
286329
is_partial_move,

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool
15871587
match sym {
15881588
sym::on_unimplemented | sym::do_not_recommend => true,
15891589
sym::on_const => features.diagnostic_on_const(),
1590+
sym::on_move => features.diagnostic_on_move(),
15901591
_ => false,
15911592
}
15921593
}

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ declare_features! (
472472
(unstable, derive_from, "1.91.0", Some(144889)),
473473
/// Allows giving non-const impls custom diagnostic messages if attempted to be used as const
474474
(unstable, diagnostic_on_const, "1.93.0", Some(143874)),
475+
/// Allows giving on-move borrowck custom diagnostic messages for a type
476+
(unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(150935)),
475477
/// Allows `#[doc(cfg(...))]`.
476478
(unstable, doc_cfg, "1.21.0", Some(43781)),
477479
/// Allows `#[doc(masked)]`.

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1180,13 +1180,18 @@ pub enum AttributeKind {
11801180
directive: Option<Box<Directive>>,
11811181
},
11821182

1183+
/// Represents `#[diagnostic::on_move]`
1184+
OnMove {
1185+
span: Span,
1186+
directive: Option<Box<Directive>>,
1187+
},
1188+
11831189
/// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`.
11841190
OnUnimplemented {
11851191
span: Span,
11861192
/// None if the directive was malformed in some way.
11871193
directive: Option<Box<Directive>>,
11881194
},
1189-
11901195
/// Represents `#[optimize(size|speed)]`
11911196
Optimize(OptimizeAttr, Span),
11921197

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ impl AttributeKind {
7777
NoStd(..) => No,
7878
NonExhaustive(..) => Yes, // Needed for rustdoc
7979
OnConst { .. } => Yes,
80+
OnMove { .. } => Yes,
8081
OnUnimplemented { .. } => Yes,
8182
Optimize(..) => No,
8283
PanicRuntime => No,

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,18 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
498498
&AttributeLintKind::MissingOptionsForOnConst => {
499499
lints::MissingOptionsForOnConstAttr.into_diag(dcx, level)
500500
}
501+
&AttributeLintKind::MalformedOnMoveAttr { span } => {
502+
lints::MalformedOnMoveAttrLint { span }.into_diag(dcx, level)
503+
}
504+
&AttributeLintKind::OnMoveMalformedFormatLiterals { name } => {
505+
lints::OnMoveMalformedFormatLiterals { name }.into_diag(dcx, level)
506+
}
507+
&AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter => {
508+
lints::OnMoveMalformedAttrExpectedLiteralOrDelimiter.into_diag(dcx, level)
509+
}
510+
&AttributeLintKind::MissingOptionsForOnMove => {
511+
lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level)
512+
}
501513
}
502514
}
503515
}

0 commit comments

Comments
 (0)