Skip to content

Commit 86c839f

Browse files
committed
Auto merge of #154123 - Zalathar:rollup-MUEvgV7, r=Zalathar
Rollup of 15 pull requests Successful merges: - #152909 (sess: `-Zbranch-protection` is a target modifier) - #153556 (`impl` restriction lowering) - #154048 (Don't emit rustdoc `missing_doc_code_examples` lint on impl items) - #150935 (Introduce #[diagnostic::on_move(message)]) - #152973 (remove -Csoft-float) - #153862 (Rename `cycle_check` to `find_cycle`) - #153992 (bootstrap: Optionally print a backtrace if a command fails) - #154019 (two smaller feature cleanups) - #154059 (tests: Activate `must_not_suspend` test for `MutexGuard` dropped before `await`) - #154075 (Rewrite `query_ensure_result`.) - #154082 (Updates derive_where and removes workaround) - #154084 (Preserve braces around `self` in use tree pretty printing) - #154086 (Insert space after float literal ending with `.` in pretty printer) - #154087 (Fix whitespace after fragment specifiers in macro pretty printing) - #154109 (tests: Add regression test for async closures involving HRTBs)
2 parents 76be1cc + ffe94f0 commit 86c839f

76 files changed

Lines changed: 1546 additions & 152 deletions

File tree

Some content is hidden

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

Cargo.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,9 +1140,9 @@ version = "0.1.96"
11401140

11411141
[[package]]
11421142
name = "derive-where"
1143-
version = "1.6.0"
1143+
version = "1.6.1"
11441144
source = "registry+https://github.com/rust-lang/crates.io-index"
1145-
checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
1145+
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
11461146
dependencies = [
11471147
"proc-macro2",
11481148
"quote",

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,19 @@ fn print_crate_inner<'a>(
329329
/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
330330
/// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]`
331331
///
332+
/// Returns `true` if both token trees are identifier-like tokens that would
333+
/// merge into a single token if printed without a space between them.
334+
/// E.g. `ident` + `where` would merge into `identwhere`.
335+
fn idents_would_merge(tt1: &TokenTree, tt2: &TokenTree) -> bool {
336+
fn is_ident_like(tt: &TokenTree) -> bool {
337+
matches!(
338+
tt,
339+
TokenTree::Token(Token { kind: token::Ident(..) | token::NtIdent(..), .. }, _,)
340+
)
341+
}
342+
is_ident_like(tt1) && is_ident_like(tt2)
343+
}
344+
332345
fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
333346
use Delimiter::*;
334347
use TokenTree::{Delimited as Del, Token as Tok};
@@ -811,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
811824
if let Some(next) = iter.peek() {
812825
if spacing == Spacing::Alone && space_between(tt, next) {
813826
self.space();
827+
} else if spacing != Spacing::Alone && idents_would_merge(tt, next) {
828+
// When tokens from macro `tt` captures preserve their
829+
// original `Joint`/`JointHidden` spacing, adjacent
830+
// identifier-like tokens can be concatenated without a
831+
// space (e.g. `$x:identwhere`). Insert a space to
832+
// prevent this.
833+
self.space();
814834
}
815835
}
816836
}

compiler/rustc_ast_pretty/src/pprust/state/expr.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,15 @@ impl<'a> State<'a> {
260260
//
261261
// loop { break x; }.method();
262262
//
263-
self.print_expr_cond_paren(
264-
receiver,
265-
receiver.precedence() < ExprPrecedence::Unambiguous,
266-
fixup.leftmost_subexpression_with_dot(),
267-
);
263+
let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous;
264+
self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot());
268265

266+
// If the receiver is an unsuffixed float literal like `0.`, insert
267+
// a space so the `.` of the method call doesn't merge with the
268+
// trailing dot: `0. .method()` instead of `0..method()`.
269+
if !needs_paren && expr_ends_with_dot(receiver) {
270+
self.word(" ");
271+
}
269272
self.word(".");
270273
self.print_ident(segment.ident);
271274
if let Some(args) = &segment.args {
@@ -658,11 +661,15 @@ impl<'a> State<'a> {
658661
);
659662
}
660663
ast::ExprKind::Field(expr, ident) => {
664+
let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous;
661665
self.print_expr_cond_paren(
662666
expr,
663-
expr.precedence() < ExprPrecedence::Unambiguous,
667+
needs_paren,
664668
fixup.leftmost_subexpression_with_dot(),
665669
);
670+
if !needs_paren && expr_ends_with_dot(expr) {
671+
self.word(" ");
672+
}
666673
self.word(".");
667674
self.print_ident(*ident);
668675
}
@@ -685,11 +692,15 @@ impl<'a> State<'a> {
685692
let fake_prec = ExprPrecedence::LOr;
686693
if let Some(e) = start {
687694
let start_fixup = fixup.leftmost_subexpression_with_operator(true);
688-
self.print_expr_cond_paren(
689-
e,
690-
start_fixup.precedence(e) < fake_prec,
691-
start_fixup,
692-
);
695+
let needs_paren = start_fixup.precedence(e) < fake_prec;
696+
self.print_expr_cond_paren(e, needs_paren, start_fixup);
697+
// If the start expression is a float literal ending with
698+
// `.`, we need a space before `..` or `..=` so that the
699+
// dots don't merge. E.g. `0. ..45.` must not become
700+
// `0...45.`.
701+
if !needs_paren && expr_ends_with_dot(e) {
702+
self.word(" ");
703+
}
693704
}
694705
match limits {
695706
ast::RangeLimits::HalfOpen => self.word(".."),
@@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String
10251036
template.push('"');
10261037
template
10271038
}
1039+
1040+
/// Returns `true` if the printed representation of this expression ends with
1041+
/// a `.` character — specifically, an unsuffixed float literal like `0.` or
1042+
/// `45.`. This is used to insert whitespace before range operators (`..`,
1043+
/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`).
1044+
fn expr_ends_with_dot(expr: &ast::Expr) -> bool {
1045+
match &expr.kind {
1046+
ast::ExprKind::Lit(token_lit) => {
1047+
token_lit.kind == token::Float
1048+
&& token_lit.suffix.is_none()
1049+
&& token_lit.symbol.as_str().ends_with('.')
1050+
}
1051+
_ => false,
1052+
}
1053+
}

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,13 @@ impl<'a> State<'a> {
881881
}
882882
if items.is_empty() {
883883
self.word("{}");
884-
} else if let [(item, _)] = items.as_slice() {
884+
} else if let [(item, _)] = items.as_slice()
885+
&& !item
886+
.prefix
887+
.segments
888+
.first()
889+
.is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower)
890+
{
885891
self.print_use_tree(item);
886892
} else {
887893
let cb = self.cbox(INDENT_UNIT);

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,

0 commit comments

Comments
 (0)