Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
47c9070
Parse move exprs as a bottom expr instead of a prefix expr
fmease Oct 7, 2026
604a21c
Document zero sized allocations in `Box` and `Vec`
maxdexh Sep 25, 2026
311c6e3
Revert "Rollup merge of #159562 - Joel-Wwalker:154071-stdio-handle-ca…
lhecker Oct 11, 2026
9a20a07
test(parser): add baseline for missing turbofish with lifetime in str…
raushan728 Sep 12, 2026
508d2f4
test DSL eagerly handle false constraints
born-in-autumn Oct 13, 2026
ebca075
fix(parser): recover missing turbofish for lifetimes in exprs
raushan728 Oct 3, 2026
eb85bf4
[rustdoc] Improve display of `rustc_must_implement_one_of` note and g…
GuillaumeGomez Oct 13, 2026
fe97485
[rustdoc] Generate sidebar link for "required methods" even if there …
GuillaumeGomez Oct 13, 2026
9b08cd0
Windows FileExt trait doesn't need to handle ErrorKind::Interrupted; …
json420 Oct 13, 2026
fbf53be
Test the interaction between `--remap-path-prefix` and doctests
Urgau Oct 12, 2026
cad2ab9
Add regression test for `rustc_must_implement_one_of` changes
GuillaumeGomez Oct 13, 2026
f8d4856
Output remapped paths in rustdoc JSON with the `DOCUMENTATION` scope
Urgau Oct 13, 2026
c2a249d
Ignore cross-compile as doctests don't always work when cross-compiled
Urgau Oct 13, 2026
0d0f404
Add README.md and descriptions for the remap-path-prefix JSON tests
Urgau Oct 13, 2026
9c1eb80
Prefer WriteConsoleW for stdout on Windows
lhecker Oct 14, 2026
6c150fe
fix some nits
born-in-autumn Oct 14, 2026
ee99134
Rollup merge of #162749 - raushan728:issues/162656, r=nnethercote
jhpratt Oct 14, 2026
110f984
Rollup merge of #164170 - lhecker:windows-stdout-perf-is-console, r=C…
jhpratt Oct 14, 2026
a65da2e
Rollup merge of #164255 - born-in-autumn:dsl-test-handle, r=born-in-a…
jhpratt Oct 14, 2026
cc41d02
Rollup merge of #163230 - maxdexh:zero-sized-allocations-are-not-real…
jhpratt Oct 14, 2026
383d6ab
Rollup merge of #163940 - fmease:move-expr-is-bottom-not-prefix, r=Ta…
jhpratt Oct 14, 2026
501f3fb
Rollup merge of #164219 - json420:windows-FileExt-test-cleanup, r=Chr…
jhpratt Oct 14, 2026
abf20ae
Rollup merge of #164220 - Urgau:remap-path-prefix-doctest-panic, r=Gu…
jhpratt Oct 14, 2026
51048fa
Rollup merge of #164266 - GuillaumeGomez:rustc_must_implement_one_of,…
jhpratt Oct 14, 2026
414333f
Rollup merge of #164278 - Urgau:rustdoc-json-remap-path, r=GuillaumeG…
jhpratt Oct 14, 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
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4073,6 +4073,7 @@ pub struct TestBinderConstraints {

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct TestBinderBody {
pub span: Span,
pub foralls: ThinVec<TestBinderForall>,
pub exists: ThinVec<TestBinderExists>,
pub constraints: Vec<TestBinderConstraint>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
.iter()
.map(|predicate| self.lower_where_predicate(predicate, &[], &mut dedup_map)),
);
hir::TestBinderBody { foralls, exists, constraints, predicates }
let span = self.lower_span(body.span);
hir::TestBinderBody { foralls, exists, constraints, predicates, span }
}

fn lower_test_binder_forall(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4527,6 +4527,7 @@ impl FnHeader {

#[derive(Debug, Clone, Copy, StableHash)]
pub struct TestBinderBody<'hir> {
pub span: Span,
pub foralls: &'hir [TestBinderForall<'hir>],
pub exists: &'hir [TestBinderExists<'hir>],
/// Constraints to be inserted directly into constraint storage to be proven
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,7 +1600,7 @@ pub fn walk_test_binder_body<'v, V: Visitor<'v>>(
visitor: &mut V,
body: &'v TestBinderBody<'v>,
) -> V::Result {
let TestBinderBody { foralls, exists, constraints, predicates } = body;
let TestBinderBody { span: _, foralls, exists, constraints, predicates } = body;
walk_list!(visitor, visit_test_binder_forall, *foralls);
walk_list!(visitor, visit_test_binder_exists, *exists);
try_visit!(visitor.visit_test_binder_constraint(&constraints));
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2380,7 +2380,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {

#[instrument(level = "debug", skip(self))]
pub(super) fn check_test_binder_body(&self, body: TestBinderBody<'tcx>) {
let TestBinderBody { foralls, exists, constraints, predicates } = body;
let TestBinderBody { span, foralls, exists, constraints, predicates } = body;
if !predicates.is_empty() {
for (predicate, span) in predicates {
let cause = traits::ObligationCause::misc(span, self.body_def_id);
Expand All @@ -2400,6 +2400,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
Ok(()) => constraints,
Err(_guar) => ty::region_constraint::RegionConstraint::new_true(),
};
if constraints.is_false() {
self.tcx().dcx().span_err(span, "top level false constraints are not supported");
return;
}

self.infcx.register_solver_region_constraint(constraints);

Expand Down Expand Up @@ -2479,6 +2483,13 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
u,
)
.with_spans(forall.span);
if constraint.is_false() {
self.tcx().dcx().span_err(forall.span, "forall evaluated to false");
self.infcx.overwrite_solver_region_constraint(
ty::region_constraint::RegionConstraint::new_true(),
);
return;
}
if let Some(assert_on_exit) = &forall.assert_on_exit {
self.check_test_binder_region_constraints(forall.span, assert_on_exit, &constraint);
}
Expand Down Expand Up @@ -2798,6 +2809,7 @@ struct RedundantLifetimeArgsLint<'tcx> {

#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
pub(crate) struct TestBinderBody<'tcx> {
pub span: Span,
pub foralls: Vec<TestBinderForall<'tcx>>,
pub exists: Vec<TestBinderExists<'tcx>>,
/// Constraints to be inserted directly into constraint storage to be proven
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ impl<'tcx> ItemCtxt<'tcx> {
&self,
item: &hir::TestBinderBody<'tcx>,
) -> TestBinderBody<'tcx> {
let hir::TestBinderBody { foralls, exists, constraints, predicates } = item;
let hir::TestBinderBody { span, foralls, exists, constraints, predicates } = item;
let foralls = foralls.iter().map(|forall| self.lower_test_binder_forall(forall)).collect();
let exists = exists.iter().map(|exists| self.lower_test_binder_exists(exists)).collect();
let constraints = self.lower_test_binder_constraint(&constraints);
Expand All @@ -337,7 +337,7 @@ impl<'tcx> ItemCtxt<'tcx> {
clauses_of::where_predicate_clauses(self, predicate, &mut clauses);
}
let predicates = clauses.into_iter().map(|(c, span)| (c.kind(), span)).collect();
TestBinderBody { foralls, exists, constraints, predicates }
TestBinderBody { span: *span, foralls, exists, constraints, predicates }
}

#[instrument(level = "debug", skip(self), ret)]
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_parse/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,15 @@ pub(crate) struct GenericParamsWithoutAngleBrackets {
pub sugg: GenericParamsWithoutAngleBracketsSugg,
}

#[derive(Diagnostic)]
#[diag("use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments")]
pub(crate) struct MissingTurbofishForLifetime {
#[primary_span]
pub span: Span,
#[suggestion("add `::`", style = "verbose", code = "::", applicability = "machine-applicable")]
pub suggest_turbofish: Span,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
"surround the type parameters with angle brackets",
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,13 @@ impl<'a> Parser<'a> {
}
}

pub(super) fn recover_missing_turbofish_for_lifetimes(&self, lt_span: Span) {
self.dcx().emit_err(crate::diagnostics::MissingTurbofishForLifetime {
span: lt_span,
suggest_turbofish: lt_span.shrink_to_lo(),
});
}

/// When writing a turbofish with multiple type parameters missing the leading `::`, we will
/// encounter a parse error when encountering the first `,`.
pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
Expand Down
31 changes: 15 additions & 16 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,6 @@ impl<'a> Parser<'a> {
pre_span,
));
}
token::Ident(..)
if this.token.is_keyword(kw::Move)
&& this.look_ahead(1, |t| *t == token::OpenParen) =>
{
make_it!(this, attrs, |this, _| this.parse_expr_move(lo))
}
token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {
make_it!(this, attrs, |this, _| this.recover_not_expr(lo))
}
Expand Down Expand Up @@ -447,16 +441,6 @@ impl<'a> Parser<'a> {
self.parse_expr_unary(lo, UnOp::Not)
}

fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> {
self.bump();
self.psess.gated_spans.gate(sym::move_expr, move_kw);
self.expect(exp!(OpenParen))?;
let expr = self.parse_expr()?;
self.expect(exp!(CloseParen))?;
let span = move_kw.to(self.prev_token.span);
Ok((span, ExprKind::Move(expr, move_kw)))
}

fn is_mistaken_not_ident_negation(&self) -> bool {
let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {
// These tokens can start an expression after `!`, but
Expand Down Expand Up @@ -1335,6 +1319,10 @@ impl<'a> Parser<'a> {
this.parse_expr_builtin()
} else if this.check_path() {
this.parse_expr_path_start()
} else if this.token.is_keyword(kw::Move)
&& this.look_ahead(1, |t| *t == token::OpenParen)
{
this.parse_expr_move()
} else if this.check_keyword(exp!(Move))
|| this.check_keyword(exp!(Use))
|| this.check_keyword(exp!(Static))
Expand Down Expand Up @@ -2216,6 +2204,17 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None)))
}

fn parse_expr_move(&mut self) -> PResult<'a, Box<Expr>> {
let move_kw = self.token.span;
self.expect_keyword(exp!(Move))?;
self.expect(exp!(OpenParen))?;
let expr = self.parse_expr()?;
self.expect(exp!(CloseParen))?;
let span = move_kw.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::move_expr, span);
Ok(self.mk_expr(span, ExprKind::Move(expr, move_kw)))
}

/// Parses a closure expression (e.g., `move |args| expr`).
fn parse_expr_closure(&mut self) -> PResult<'a, Box<Expr>> {
let lo = self.token.span;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2717,6 +2717,7 @@ impl<'a> Parser<'a> {
let mut exists = ThinVec::new();
let mut constraints = Vec::new();
let mut predicates = Vec::new();
let mut span = self.token.span;
self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |this| {
match this.token.ident() {
Some((Ident { name: sym::predicates, .. }, IdentKind::Normal)) => {
Expand All @@ -2735,7 +2736,8 @@ impl<'a> Parser<'a> {
}
Ok(())
})?;
Ok(TestBinderBody { foralls, exists, constraints, predicates })
span = span.to(self.prev_token.span);
Ok(TestBinderBody { foralls, exists, constraints, predicates, span })
}

pub fn parse_test_binder_forall(&mut self) -> PResult<'a, TestBinderForall> {
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,20 @@ impl<'a> Parser<'a> {
is_args_start(&this.token)
};

// A lifetime followed by `>` or `,` can never start a valid expression, so `<` here
// must be the start of generic arguments with a missing `::`.
let is_missing_turbofish_for_lifetime = |this: &mut Self| {
this.token == token::Lt
&& this.may_recover()
&& style == PathStyle::Expr
&& this.look_ahead(1, |t| t.is_lifetime())
&& this.look_ahead(2, |t| matches!(t.kind, token::Gt | token::Comma))
};

Ok(
if style == PathStyle::Type && check_args_start(self)
|| style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start)
|| is_missing_turbofish_for_lifetime(self)
{
// We use `style == PathStyle::Expr` to check if this is in a recursion or not. If
// it isn't, then we reset the unmatched angle bracket count as we're about to start
Expand All @@ -314,12 +325,18 @@ impl<'a> Parser<'a> {
self.unmatched_angle_bracket_count = 0;
}

let missing_turbofish =
style == PathStyle::Expr && !self.check_path_sep_and_look_ahead(is_args_start);

// Generic arguments are found - `<`, `(`, `::<` or `::(`.
// First, eat `::` if it exists.
let _ = self.eat_path_sep();

let lo = self.token.span;
let args = if self.eat_lt() {
if missing_turbofish {
self.recover_missing_turbofish_for_lifetimes(lo);
}
// `<'a, T, A = U>`
let args = self.parse_angle_args_with_leading_angle_bracket_recovery(
style,
Expand Down
Loading
Loading