Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ parse_dotdotdot = unexpected token: `...`
parse_dotdotdot_rest_pattern = unexpected `...`
.label = not a valid pattern
.suggestion = for a rest pattern, use `..` instead of `...`
.note = C-variadic type `...` is not allowed here

parse_double_colon_in_bound = expected `:` followed by trait or lifetime
.suggestion = use single colon
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2723,7 +2723,9 @@ pub(crate) struct DotDotDotRestPattern {
#[label]
pub span: Span,
#[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
pub suggestion: Span,
pub suggestion: Option<Span>,
#[note]
pub var_args: Option<()>,
}

#[derive(Diagnostic)]
Expand Down
27 changes: 19 additions & 8 deletions compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ impl<'a> Parser<'a> {
self.bump(); // `..`
PatKind::Rest
} else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) {
self.recover_dotdotdot_rest_pat(lo)
self.recover_dotdotdot_rest_pat(lo, expected)
} else if let Some(form) = self.parse_range_end() {
self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`.
} else if self.eat(exp!(Bang)) {
Expand Down Expand Up @@ -886,16 +886,27 @@ impl<'a> Parser<'a> {

/// Recover from a typoed `...` pattern that was encountered
/// Ref: Issue #70388
fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind {
fn recover_dotdotdot_rest_pat(&mut self, lo: Span, expected: Option<Expected>) -> PatKind {
// A typoed rest pattern `...`.
self.bump(); // `...`

// The user probably mistook `...` for a rest pattern `..`.
self.dcx().emit_err(DotDotDotRestPattern {
span: lo,
suggestion: lo.with_lo(lo.hi() - BytePos(1)),
});
PatKind::Rest
if let Some(Expected::ParameterName) = expected {
// We have `...` in a closure argument, likely meant to be var-arg, which aren't
// supported in closures (#146489).
PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern {
span: lo,
suggestion: None,
var_args: Some(()),
}))
} else {
// The user probably mistook `...` for a rest pattern `..`.
self.dcx().emit_err(DotDotDotRestPattern {
span: lo,
suggestion: Some(lo.with_lo(lo.hi() - BytePos(1))),
var_args: None,
});
PatKind::Rest
}
}

/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::path::PathBuf;

use rustc_errors::codes::*;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::{self as hir, PatKind};
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
Expand Down Expand Up @@ -512,7 +512,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
type_name: ty_to_string(self, ty, def_id),
});
}
InferSourceKind::ClosureArg { insert_span, ty } => {
InferSourceKind::ClosureArg { insert_span, ty, .. } => {
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: String::new(),
Expand Down Expand Up @@ -652,6 +652,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}),
};
*err.long_ty_path() = long_ty_path;
if let InferSourceKind::ClosureArg { kind: PatKind::Err(_), .. } = kind {
// We will have already emitted an error about this pattern.
err.downgrade_to_delayed_bug();
Comment on lines +657 to +658
Copy link
Contributor

@lcnr lcnr Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, surprised we don't have a way to silence a lint by passing in an ErrorGuaranteed. We could then do

        if let InferSourceKind::ClosureArg { kind: PatKind::Err(guar), .. } = kind {
            err.silence_via_error_guaranteed(guar);
        }
}

though maybe even better... change this function to return Result<Diag, ErrorGuaranteed> and avoid creating a diagnostic at all edit: seems annoying to do 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the rationale for downgrade_to_delayed_bug itself not taking an ErrorGuarantee is because itself is kind of an error guarantee already :)

The guarantee is more critical for anything that could cause a binary to be emitted otherwise.

}
err
}
}
Expand All @@ -673,6 +677,7 @@ enum InferSourceKind<'tcx> {
ClosureArg {
insert_span: Span,
ty: Ty<'tcx>,
kind: PatKind<'tcx>,
},
GenericArg {
insert_span: Span,
Expand Down Expand Up @@ -1197,6 +1202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
kind: InferSourceKind::ClosureArg {
insert_span: param.pat.span.shrink_to_hi(),
ty: param_ty,
kind: param.pat.kind,
},
})
}
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/closures/varargs-in-closure-isnt-supported.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// var-args are not supported in closures, ensure we don't misdirect people (#146489)
#![feature(c_variadic)]

unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 {
let mut lol = |...| (); //~ ERROR: unexpected `...`
unsafe { ap.arg::<u32>() } //~^ NOTE: C-variadic type `...` is not allowed here
//~| NOTE: not a valid pattern
}

fn main() {}
10 changes: 10 additions & 0 deletions tests/ui/closures/varargs-in-closure-isnt-supported.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: unexpected `...`
--> $DIR/varargs-in-closure-isnt-supported.rs:5:20
|
LL | let mut lol = |...| ();
| ^^^ not a valid pattern
|
= note: C-variadic type `...` is not allowed here

error: aborting due to 1 previous error

Loading