Skip to content
Open
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
48 changes: 48 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ declare_lint_pass! {
UNUSED_UNSAFE,
UNUSED_VARIABLES,
USELESS_DEPRECATED,
VARARGS_WITHOUT_PATTERN,
WARNINGS,
// tidy-alphabetical-end
]
Expand Down Expand Up @@ -5141,3 +5142,50 @@ declare_lint! {
"detects tail calls of functions marked with `#[track_caller]`",
@feature_gate = explicit_tail_calls;
}

declare_lint! {
/// The `varargs_without_pattern` lint detects when `...` is used as an argument to a
/// non-foreign function without any pattern being specified.
///
/// ### Example
///
/// ```rust
/// // Using `...` in non-foreign function definitions is unstable, however stability is
/// // currently only checked after attributes are expanded, so using `#[cfg(false)]` here will
/// // allow this to compile on stable Rust.
/// #[cfg(false)]
/// fn foo(...) {
///
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Patterns are currently required for all non-`...` arguments in function definitions (with
/// some exceptions in the 2015 edition). Requiring `...` arguments to have patterns in
/// non-foreign function definitions makes the language more consistent, and removes a source of
/// confusion for the unstable C variadic feature. `...` arguments without a pattern are already
/// stable and widely used in foreign function definitions; this lint only affects non-foreign
/// function definitions.
///
/// Using `...` (C varargs) in a non-foreign function definition is currently unstable. However,
/// stability checking for the `...` syntax in non-foreign function definitions is currently
/// implemented after attributes have been expanded, meaning that if the attribute removes the
/// use of the unstable syntax (e.g. `#[cfg(false)]`, or a procedural macro), the code will
/// compile on stable Rust; this is the only situation where this lint affects code that
/// compiles on stable Rust.
///
/// This is a [future-incompatible] lint to transition this to a hard error in the future.
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub VARARGS_WITHOUT_PATTERN,
Warn,
"detects usage of `...` arguments without a pattern in non-foreign items",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseError,
reference: "issue #145544 <https://github.com/rust-lang/rust/issues/145544>",
report_in_deps: false,
};
}
3 changes: 3 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,9 @@ parse_use_if_else = use an `if-else` expression instead
parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable
parse_use_let_not_var = write `let` instead of `var` to introduce a new variable
parse_varargs_without_pattern = missing pattern for `...` argument
.suggestion = name the argument, or use `_` to continue ignoring it
parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by an item
.label = the visibility
.help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3675,3 +3675,10 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
}
}
}

#[derive(LintDiagnostic)]
#[diag(parse_varargs_without_pattern)]
pub(crate) struct VarargsWithoutPattern {
#[suggestion(code = "_: ...", applicability = "machine-applicable")]
pub span: Span,
}
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ impl<'a> Parser<'a> {
AttrWrapper::empty(),
true,
false,
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true },
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
ForceCollect::No,
) {
Ok(Some(item)) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use crate::errors::{
};
use crate::parser::FnContext;
use crate::parser::attr::InnerAttrPolicy;
use crate::parser::item::IsDotDotDot;
use crate::{exp, fluent_generated as fluent};

/// Creates a placeholder argument.
Expand Down Expand Up @@ -2273,7 +2274,7 @@ impl<'a> Parser<'a> {
let maybe_emit_anon_params_note = |this: &mut Self, err: &mut Diag<'_>| {
let ed = this.token.span.with_neighbor(this.prev_token.span).edition();
if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait)
&& (fn_parse_mode.req_name)(ed)
&& (fn_parse_mode.req_name)(ed, IsDotDotDot::No)
{
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
}
Expand Down
55 changes: 42 additions & 13 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_ast::{self as ast};
use rustc_ast_pretty::pprust;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err};
use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN;
use rustc_span::edit_distance::edit_distance;
use rustc_span::edition::Edition;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, source_map, sym};
Expand Down Expand Up @@ -117,7 +118,7 @@ impl<'a> Parser<'a> {
impl<'a> Parser<'a> {
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> {
let fn_parse_mode =
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true };
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(Box::new))
}

Expand Down Expand Up @@ -977,7 +978,7 @@ impl<'a> Parser<'a> {
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
let fn_parse_mode =
FnParseMode { req_name: |_| true, context: FnContext::Impl, req_body: true };
FnParseMode { req_name: |_, _| true, context: FnContext::Impl, req_body: true };
self.parse_assoc_item(fn_parse_mode, force_collect)
}

Expand All @@ -986,7 +987,7 @@ impl<'a> Parser<'a> {
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
let fn_parse_mode = FnParseMode {
req_name: |edition| edition >= Edition::Edition2018,
req_name: |edition, _| edition >= Edition::Edition2018,
context: FnContext::Trait,
req_body: false,
};
Expand Down Expand Up @@ -1266,8 +1267,11 @@ impl<'a> Parser<'a> {
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<ForeignItem>>>> {
let fn_parse_mode =
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: false };
let fn_parse_mode = FnParseMode {
req_name: |_, is_dot_dot_dot| is_dot_dot_dot == IsDotDotDot::No,
context: FnContext::Free,
req_body: false,
};
Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match ForeignItemKind::try_from(kind) {
Expand Down Expand Up @@ -2142,7 +2146,7 @@ impl<'a> Parser<'a> {
Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited, tokens: None };
// We use `parse_fn` to get a span for the function
let fn_parse_mode =
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true };
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
match self.parse_fn(
&mut AttrVec::new(),
fn_parse_mode,
Expand Down Expand Up @@ -2375,8 +2379,16 @@ impl<'a> Parser<'a> {
/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type.
///
/// This function pointer accepts an edition, because in edition 2015, trait declarations
/// were allowed to omit parameter names. In 2018, they became required.
type ReqName = fn(Edition) -> bool;
/// were allowed to omit parameter names. In 2018, they became required. It also accepts an
/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are
/// allowed to omit the name of the `...` but regular function items are not.
type ReqName = fn(Edition, IsDotDotDot) -> bool;

#[derive(Copy, Clone, PartialEq)]
pub(crate) enum IsDotDotDot {
Yes,
No,
}

/// Parsing configuration for functions.
///
Expand Down Expand Up @@ -2409,6 +2421,9 @@ pub(crate) struct FnParseMode {
/// to true.
/// * The span is from Edition 2015. In particular, you can get a
/// 2015 span inside a 2021 crate using macros.
///
/// Or if `IsDotDotDot::Yes`, this function will also return `false` if the item being parsed
/// is inside an `extern` block.
pub(super) req_name: ReqName,
/// The context in which this function is parsed, used for diagnostics.
/// This indicates the fn is a free function or method and so on.
Expand Down Expand Up @@ -3055,11 +3070,25 @@ impl<'a> Parser<'a> {
return Ok((res?, Trailing::No, UsePreAttrPos::No));
}

let is_name_required = match this.token.kind {
token::DotDotDot => false,
_ => (fn_parse_mode.req_name)(
this.token.span.with_neighbor(this.prev_token.span).edition(),
),
let is_dot_dot_dot = if this.token.kind == token::DotDotDot {
IsDotDotDot::Yes
} else {
IsDotDotDot::No
};
let is_name_required = (fn_parse_mode.req_name)(
this.token.span.with_neighbor(this.prev_token.span).edition(),
is_dot_dot_dot,
);
let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes {
this.psess.buffer_lint(
VARARGS_WITHOUT_PATTERN,
this.token.span,
ast::CRATE_NODE_ID,
errors::VarargsWithoutPattern { span: this.token.span },
);
false
} else {
is_name_required
};
let (pat, ty) = if is_name_required || this.is_named_param() {
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ impl<'a> Parser<'a> {
// Inside parenthesized type arguments, we want types only, not names.
let mode = FnParseMode {
context: FnContext::Free,
req_name: |_| false,
req_name: |_, _| false,
req_body: false,
};
let param = p.parse_param_general(&mode, false, false);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ impl<'a> Parser<'a> {
attrs.clone(), // FIXME: unwanted clone of attrs
false,
true,
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true },
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
force_collect,
)? {
self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ impl<'a> Parser<'a> {
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
}
let mode = crate::parser::item::FnParseMode {
req_name: |_| false,
req_name: |_, _| false,
context: FnContext::Free,
req_body: false,
};
Expand Down Expand Up @@ -1352,7 +1352,8 @@ impl<'a> Parser<'a> {
self.bump();
let args_lo = self.token.span;
let snapshot = self.create_snapshot_for_diagnostic();
let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false };
let mode =
FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false };
match self.parse_fn_decl(&mode, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) {
Ok(decl) => {
self.dcx().emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span });
Expand Down Expand Up @@ -1443,7 +1444,8 @@ impl<'a> Parser<'a> {

// Parse `(T, U) -> R`.
let inputs_lo = self.token.span;
let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false };
let mode =
FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false };
let inputs: ThinVec<_> =
self.parse_fn_params(&mode)?.into_iter().map(|input| input.ty).collect();
let inputs_span = inputs_lo.to(self.prev_token.span);
Expand Down
2 changes: 1 addition & 1 deletion tests/crashes/132142.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
//@ known-bug: #132142

async extern "cmse-nonsecure-entry" fn fun(...) {}
async extern "cmse-nonsecure-entry" fn fun(_: ...) {}
6 changes: 3 additions & 3 deletions tests/pretty/hir-fn-variadic.pp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

fn main() {
fn g1(_: extern "C" fn(_: u8, va: ...)) { }
fn g2(_: extern "C" fn(_: u8, ...)) { }
fn g2(_: extern "C" fn(_: u8, _: ...)) { }
fn g3(_: extern "C" fn(u8, va: ...)) { }
fn g4(_: extern "C" fn(u8, ...)) { }
fn g4(_: extern "C" fn(u8, _: ...)) { }

fn g5(_: extern "C" fn(va: ...)) { }
fn g6(_: extern "C" fn(...)) { }
fn g6(_: extern "C" fn(_: ...)) { }

{
let _ =
Expand Down
10 changes: 5 additions & 5 deletions tests/pretty/hir-fn-variadic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ pub unsafe extern "C" fn bar(_: i32, mut va2: ...) -> usize {

fn main() {
fn g1(_: extern "C" fn(_: u8, va: ...)) {}
fn g2(_: extern "C" fn(_: u8, ...)) {}
fn g2(_: extern "C" fn(_: u8, _: ...)) {}
fn g3(_: extern "C" fn(u8, va: ...)) {}
fn g4(_: extern "C" fn(u8, ...)) {}
fn g4(_: extern "C" fn(u8, _: ...)) {}

fn g5(_: extern "C" fn(va: ...)) {}
fn g6(_: extern "C" fn(...)) {}
fn g6(_: extern "C" fn(_: ...)) {}

_ = { unsafe extern "C" fn f1(_: u8, va: ...) {} };
_ = { unsafe extern "C" fn f2(_: u8, ...) {} };
_ = { unsafe extern "C" fn f2(_: u8, _: ...) {} };

_ = { unsafe extern "C" fn f5(va: ...) {} };
_ = { unsafe extern "C" fn f6(...) {} };
_ = { unsafe extern "C" fn f6(_: ...) {} };
}
2 changes: 1 addition & 1 deletion tests/ui/c-variadic/issue-86053-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self ,
//~| ERROR unexpected `self` parameter in function
//~| ERROR unexpected `self` parameter in function
//~| ERROR unexpected `self` parameter in function
self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
//~^ ERROR unexpected `self` parameter in function
//~| ERROR unexpected `self` parameter in function
//~| ERROR unexpected `self` parameter in function
Expand Down
32 changes: 16 additions & 16 deletions tests/ui/c-variadic/issue-86053-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -25,46 +25,46 @@ LL | fn ordering4 < 'a , 'b > ( a : , self , self , self ,
error: unexpected `self` parameter in function
--> $DIR/issue-86053-1.rs:11:5
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^ must be the first parameter of an associated function

error: unexpected `self` parameter in function
--> $DIR/issue-86053-1.rs:11:20
--> $DIR/issue-86053-1.rs:11:23
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^ must be the first parameter of an associated function
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^ must be the first parameter of an associated function

error: unexpected `self` parameter in function
--> $DIR/issue-86053-1.rs:11:29
--> $DIR/issue-86053-1.rs:11:32
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^ must be the first parameter of an associated function
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^ must be the first parameter of an associated function

error: `...` must be the last argument of a C-variadic function
--> $DIR/issue-86053-1.rs:11:12
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^^^

error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention
--> $DIR/issue-86053-1.rs:11:12
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^ ^^^
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^^^^^^ ^^^^^^

error[E0412]: cannot find type `F` in this scope
--> $DIR/issue-86053-1.rs:11:48
--> $DIR/issue-86053-1.rs:11:54
|
LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^
LL | self , _: ... , self , self , _: ... ) where F : FnOnce ( & 'a & 'b usize ) {
| ^
|
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
= note: similarly named trait `Fn` defined here
help: a trait with a similar name exists
|
LL | self , ... , self , self , ... ) where Fn : FnOnce ( & 'a & 'b usize ) {
| +
LL | self , _: ... , self , self , _: ... ) where Fn : FnOnce ( & 'a & 'b usize ) {
| +
help: you might be missing a type parameter
|
LL | fn ordering4 < 'a , 'b, F > ( a : , self , self , self ,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/c-variadic/issue-86053-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

trait H<T> {}

unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), ...) {}
unsafe extern "C" fn ordering4<'a, F: H<&'static &'a ()>>(_: (), _: ...) {}
//~^ ERROR: in type `&'static &'a ()`, reference has a longer lifetime than the data it references [E0491]

fn main() {}
Loading
Loading