Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ parse_dotdotdot = unexpected token: `...`
parse_dotdotdot_rest_pattern = unexpected `...`
.label = not a valid pattern
.suggestion = for a rest pattern, use `..` instead of `...`
.note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

parse_dotdotdot_rest_type = unexpected `...`
.note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

parse_double_colon_in_bound = expected `:` followed by trait or lifetime
.suggestion = use single colon
Expand Down
12 changes: 11 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 Expand Up @@ -3030,6 +3032,14 @@ pub(crate) struct NestedCVariadicType {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_dotdotdot_rest_type)]
#[note]
pub(crate) struct InvalidCVariadicType {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(parse_invalid_dyn_keyword)]
#[help]
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
13 changes: 11 additions & 2 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use super::{Parser, PathStyle, SeqSep, TokenType, Trailing};
use crate::errors::{
self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword,
ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg,
HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime,
NestedCVariadicType, ReturnTypesUseThinArrow,
HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut,
NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow,
};
use crate::parser::item::FrontMatterParsingMode;
use crate::parser::{FnContext, FnParseMode};
Expand Down Expand Up @@ -106,6 +106,15 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool {
impl<'a> Parser<'a> {
/// Parses a type.
pub fn parse_ty(&mut self) -> PResult<'a, Box<Ty>> {
if self.token == token::DotDotDot {
// We special case this so that we don't talk about "nested C-variadics" in types.
// We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about
// things like `Vec<...>`.
let span = self.token.span;
self.bump();
let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span }));
return Ok(self.mk_ty(span, kind));
}
// Make sure deeply nested types don't overflow the stack.
ensure_sufficient_stack(|| {
self.parse_ty_common(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use std::path::PathBuf;

use rustc_errors::codes::*;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource};
use rustc_hir::{
self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind,
};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
Expand Down Expand Up @@ -512,7 +513,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 +653,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 +678,7 @@ enum InferSourceKind<'tcx> {
ClosureArg {
insert_span: Span,
ty: Ty<'tcx>,
kind: PatKind<'tcx>,
},
GenericArg {
insert_span: Span,
Expand Down Expand Up @@ -1197,6 +1203,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
11 changes: 7 additions & 4 deletions tests/ui/c-variadic/no-closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
// Check that `...` in closures is rejected.

const F: extern "C" fn(...) = |_: ...| {};
//~^ ERROR C-variadic type `...` may not be nested inside another type
//~^ ERROR: unexpected `...`
//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

fn foo() {
let f = |...| {};
//~^ ERROR: `..` patterns are not allowed here
//~| ERROR: unexpected `...`
//~^ ERROR: unexpected `...`
//~| NOTE: not a valid pattern
//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

let f = |_: ...| {};
//~^ ERROR C-variadic type `...` may not be nested inside another type
//~^ ERROR: unexpected `...`
//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
f(1i64)
}
27 changes: 9 additions & 18 deletions tests/ui/c-variadic/no-closure.stderr
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
error[E0743]: C-variadic type `...` may not be nested inside another type
error: unexpected `...`
--> $DIR/no-closure.rs:6:35
|
LL | const F: extern "C" fn(...) = |_: ...| {};
| ^^^
|
= note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

error: unexpected `...`
--> $DIR/no-closure.rs:10:14
--> $DIR/no-closure.rs:11:14
|
LL | let f = |...| {};
| ^^^ not a valid pattern
|
help: for a rest pattern, use `..` instead of `...`
|
LL - let f = |...| {};
LL + let f = |..| {};
|
= note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

error[E0743]: C-variadic type `...` may not be nested inside another type
--> $DIR/no-closure.rs:14:17
error: unexpected `...`
--> $DIR/no-closure.rs:16:17
|
LL | let f = |_: ...| {};
| ^^^

error: `..` patterns are not allowed here
--> $DIR/no-closure.rs:10:14
|
LL | let f = |...| {};
| ^^^
|
= note: only allowed in tuple, tuple struct, and slice patterns
= note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0743`.
Loading