Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cca2865
sess: `-Zbranch-protection` is a target modifier
davidtwco Jan 29, 2026
965f1e7
Introduce #[diagnostic::on_move]
rperier Jan 1, 2026
eee0dc9
Rename `cycle_check` to `find_cycle`
Zoxc Mar 3, 2026
aa7d812
remove `internal_impls_macro` feature
cyrgani Mar 17, 2026
e350a56
move features into the correct section
cyrgani Feb 24, 2026
c8a2c46
bootstrap: Optionally print a backtrace if a command fails
jyn514 Mar 16, 2026
74c51ce
Don't emit `missing_doc_code_examples` on impl items
GuillaumeGomez Mar 18, 2026
6d3b780
Add more tests for rustdoc `missing_doc_code_examples` lint
GuillaumeGomez Mar 18, 2026
45b22ef
tests: Activate must_not_suspend test for MutexGuard dropped before a…
Enselic Mar 18, 2026
bef4b47
Updates derive_where and removes workaround
spirali Mar 19, 2026
74776c4
Rewrite `query_ensure_result`.
nnethercote Mar 19, 2026
cec0a68
Resolve paths for `impl` restrictions using `smart_resolve_path`
CoCo-Japan-pan Mar 19, 2026
4a60dae
Add UI tests for path resolution errors of `impl` restrictions
CoCo-Japan-pan Mar 19, 2026
082cdf7
Preserve braces around `self` in use tree pretty printing
aytey Mar 19, 2026
c777685
Fix whitespace after fragment specifiers in macro pretty printing
aytey Mar 19, 2026
e9740a4
Insert space after float literal ending with `.` in pretty printer
aytey Mar 19, 2026
caad6ee
tests: Add regression test for async closures involving HRTBs
Enselic Mar 19, 2026
e72674e
remove -Csoft-float
RalfJung Feb 22, 2026
845fd53
Rollup merge of #152909 - davidtwco:branch-protection-target-modifier…
Zalathar Mar 20, 2026
85b8c32
Rollup merge of #153556 - CoCo-Japan-pan:impl-restriction-lowering, r…
Zalathar Mar 20, 2026
319df85
Rollup merge of #154048 - GuillaumeGomez:missing_doc_code_examples-im…
Zalathar Mar 20, 2026
734cca0
Rollup merge of #150935 - rperier:provide_diagnostic_on_move_for_smar…
Zalathar Mar 20, 2026
fcc1604
Rollup merge of #152973 - RalfJung:soft-float, r=JonathanBrouwer
Zalathar Mar 20, 2026
ef9bb40
Rollup merge of #153862 - Zoxc:cycle-check-rename, r=chenyukang
Zalathar Mar 20, 2026
1f5915e
Rollup merge of #153992 - ferrocene:jyn/bootstrap-backtrace, r=jieyou…
Zalathar Mar 20, 2026
83d921d
Rollup merge of #154019 - cyrgani:feature-clean, r=joboet
Zalathar Mar 20, 2026
a194bea
Rollup merge of #154059 - Enselic:dropped-mutex-guard, r=Kivooeo
Zalathar Mar 20, 2026
3cabafe
Rollup merge of #154075 - nnethercote:rewrite-query_ensure_result, r=…
Zalathar Mar 20, 2026
ff9a99d
Rollup merge of #154082 - spirali:fix-derive-where, r=lqd
Zalathar Mar 20, 2026
870e43e
Rollup merge of #154084 - aytey:fix-use-self-braces, r=Kivooeo
Zalathar Mar 20, 2026
a98232d
Rollup merge of #154086 - aytey:fix-float-range-spacing, r=Kivooeo
Zalathar Mar 20, 2026
a00818d
Rollup merge of #154087 - aytey:fix-fragment-specifier-whitespace, r=…
Zalathar Mar 20, 2026
ffe94f0
Rollup merge of #154109 - Enselic:unifying-function-types-involving-h…
Zalathar Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1140,9 +1140,9 @@ version = "0.1.96"

[[package]]
name = "derive-where"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
dependencies = [
"proc-macro2",
"quote",
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,19 @@ fn print_crate_inner<'a>(
/// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,`
/// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]`
///
/// Returns `true` if both token trees are identifier-like tokens that would
/// merge into a single token if printed without a space between them.
/// E.g. `ident` + `where` would merge into `identwhere`.
fn idents_would_merge(tt1: &TokenTree, tt2: &TokenTree) -> bool {
fn is_ident_like(tt: &TokenTree) -> bool {
matches!(
tt,
TokenTree::Token(Token { kind: token::Ident(..) | token::NtIdent(..), .. }, _,)
)
}
is_ident_like(tt1) && is_ident_like(tt2)
}

fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool {
use Delimiter::*;
use TokenTree::{Delimited as Del, Token as Tok};
Expand Down Expand Up @@ -811,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
if let Some(next) = iter.peek() {
if spacing == Spacing::Alone && space_between(tt, next) {
self.space();
} else if spacing != Spacing::Alone && idents_would_merge(tt, next) {
// When tokens from macro `tt` captures preserve their
// original `Joint`/`JointHidden` spacing, adjacent
// identifier-like tokens can be concatenated without a
// space (e.g. `$x:identwhere`). Insert a space to
// prevent this.
self.space();
}
}
}
Expand Down
48 changes: 37 additions & 11 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,15 @@ impl<'a> State<'a> {
//
// loop { break x; }.method();
//
self.print_expr_cond_paren(
receiver,
receiver.precedence() < ExprPrecedence::Unambiguous,
fixup.leftmost_subexpression_with_dot(),
);
let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous;
self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot());

// If the receiver is an unsuffixed float literal like `0.`, insert
// a space so the `.` of the method call doesn't merge with the
// trailing dot: `0. .method()` instead of `0..method()`.
if !needs_paren && expr_ends_with_dot(receiver) {
self.word(" ");
}
self.word(".");
self.print_ident(segment.ident);
if let Some(args) = &segment.args {
Expand Down Expand Up @@ -658,11 +661,15 @@ impl<'a> State<'a> {
);
}
ast::ExprKind::Field(expr, ident) => {
let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous;
self.print_expr_cond_paren(
expr,
expr.precedence() < ExprPrecedence::Unambiguous,
needs_paren,
fixup.leftmost_subexpression_with_dot(),
);
if !needs_paren && expr_ends_with_dot(expr) {
self.word(" ");
}
self.word(".");
self.print_ident(*ident);
}
Expand All @@ -685,11 +692,15 @@ impl<'a> State<'a> {
let fake_prec = ExprPrecedence::LOr;
if let Some(e) = start {
let start_fixup = fixup.leftmost_subexpression_with_operator(true);
self.print_expr_cond_paren(
e,
start_fixup.precedence(e) < fake_prec,
start_fixup,
);
let needs_paren = start_fixup.precedence(e) < fake_prec;
self.print_expr_cond_paren(e, needs_paren, start_fixup);
// If the start expression is a float literal ending with
// `.`, we need a space before `..` or `..=` so that the
// dots don't merge. E.g. `0. ..45.` must not become
// `0...45.`.
if !needs_paren && expr_ends_with_dot(e) {
self.word(" ");
}
}
match limits {
ast::RangeLimits::HalfOpen => self.word(".."),
Expand Down Expand Up @@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String
template.push('"');
template
}

/// Returns `true` if the printed representation of this expression ends with
/// a `.` character — specifically, an unsuffixed float literal like `0.` or
/// `45.`. This is used to insert whitespace before range operators (`..`,
/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`).
fn expr_ends_with_dot(expr: &ast::Expr) -> bool {
match &expr.kind {
ast::ExprKind::Lit(token_lit) => {
token_lit.kind == token::Float
&& token_lit.suffix.is_none()
&& token_lit.symbol.as_str().ends_with('.')
}
_ => false,
}
}
8 changes: 7 additions & 1 deletion compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,13 @@ impl<'a> State<'a> {
}
if items.is_empty() {
self.word("{}");
} else if let [(item, _)] = items.as_slice() {
} else if let [(item, _)] = items.as_slice()
&& !item
.prefix
.segments
.first()
.is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower)
{
self.print_use_tree(item);
} else {
let cb = self.cbox(INDENT_UNIT);
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem

pub(crate) mod do_not_recommend;
pub(crate) mod on_const;
pub(crate) mod on_move;
pub(crate) mod on_unimplemented;

#[derive(Copy, Clone)]
Expand All @@ -32,6 +33,8 @@ pub(crate) enum Mode {
DiagnosticOnUnimplemented,
/// `#[diagnostic::on_const]`
DiagnosticOnConst,
/// `#[diagnostic::on_move]`
DiagnosticOnMove,
}

fn merge_directives<S: Stage>(
Expand Down Expand Up @@ -113,6 +116,13 @@ fn parse_directive_items<'p, S: Stage>(
span,
);
}
Mode::DiagnosticOnMove => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnMoveAttr { span },
span,
);
}
}
continue;
}}
Expand All @@ -132,7 +142,7 @@ fn parse_directive_items<'p, S: Stage>(
Mode::RustcOnUnimplemented => {
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => {
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
Expand Down
72 changes: 72 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use rustc_feature::template;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::sym;

use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::target_checking::{ALL_TARGETS, AllowedTargets};

#[derive(Default)]
pub(crate) struct OnMoveParser {
span: Option<Span>,
directive: Option<(Span, Directive)>,
}

impl OnMoveParser {
fn parse<'sess, S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, 'sess, S>,
args: &ArgParser,
mode: Mode,
) {
if !cx.features().diagnostic_on_move() {
return;
}

let span = cx.attr_span;
self.span = Some(span);
let Some(list) = args.list() else {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MissingOptionsForOnMove,
span,
);
return;
};

if list.is_empty() {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter,
list.span,
);
return;
}

if let Some(directive) = parse_directive_items(cx, mode, list.mixed(), true) {
merge_directives(cx, &mut self.directive, (span, directive));
}
}
}
impl<S: Stage> AttributeParser<S> for OnMoveParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::diagnostic, sym::on_move],
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|this, cx, args| {
this.parse(cx, args, Mode::DiagnosticOnMove);
},
)];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);

fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(span) = self.span {
Some(AttributeKind::OnMove { span, directive: self.directive.map(|d| Box::new(d.1)) })
} else {
None
}
}
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::attributes::debugger::*;
use crate::attributes::deprecation::*;
use crate::attributes::diagnostic::do_not_recommend::*;
use crate::attributes::diagnostic::on_const::*;
use crate::attributes::diagnostic::on_move::*;
use crate::attributes::diagnostic::on_unimplemented::*;
use crate::attributes::doc::*;
use crate::attributes::dummy::*;
Expand Down Expand Up @@ -149,6 +150,7 @@ attribute_parsers!(
MacroUseParser,
NakedParser,
OnConstParser,
OnMoveParser,
OnUnimplementedParser,
RustcAlignParser,
RustcAlignStaticParser,
Expand Down
25 changes: 15 additions & 10 deletions compiler/rustc_borrowck/src/borrowck_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,18 +324,23 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> {
verb: &str,
optional_adverb_for_moved: &str,
moved_path: Option<String>,
primary_message: Option<String>,
) -> 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(
Expand Down
57 changes: 50 additions & 7 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ use std::ops::ControlFlow;
use either::Either;
use hir::{ClosureKind, Path};
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};
use rustc_hir as hir;
use rustc_hir::attrs::diagnostic::FormatArgs;
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::{
Expand Down Expand Up @@ -138,6 +142,36 @@ 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, on_move_notes) = if let ty::Adt(item_def, args) =
self.body.local_decls[moved_place.local].ty.kind()
&& let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive)
{
let item_name = self.infcx.tcx.item_name(item_def.did()).to_string();
let mut generic_args: Vec<_> = self
.infcx
.tcx
.generics_of(item_def.did())
.own_params
.iter()
.filter_map(|param| Some((param.name, args[param.index as usize].to_string())))
.collect();
generic_args.push((kw::SelfUpper, item_name));

let args = FormatArgs {
this: String::new(),
trait_sugared: String::new(),
item_context: "",
generic_args,
};
(
directive.message.as_ref().map(|e| e.1.format(&args)),
directive.label.as_ref().map(|e| e.1.format(&args)),
directive.notes.iter().map(|e| e.format(&args)).collect(),
)
} else {
(None, None, ThinVec::new())
};

let mut err = self.cannot_act_on_moved_value(
span,
desired_action.as_noun(),
Expand All @@ -146,8 +180,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
moved_place,
DescribePlaceOpt { including_downcast: true, including_tuple_field: true },
),
on_move_message,
);

for note in on_move_notes {
err.note(note);
}

let reinit_spans = maybe_reinitialized_locations
.iter()
.take(3)
Expand Down Expand Up @@ -275,12 +314,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: &note_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: &note_msg,
span,
});
}
} else {
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
is_partial_move,
Expand Down
Loading
Loading