Skip to content

Commit 652ab4b

Browse files
committed
c_variadic: Add future-incompatibility warning for ... arguments without a pattern outside of extern blocks
1 parent 425a9c0 commit 652ab4b

28 files changed

+407
-170
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,5 +1059,8 @@ lint_useless_ptr_null_checks_ref = references are not nullable, so checking them
10591059
10601060
lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
10611061
1062+
lint_varargs_without_pattern = missing pattern for `...` argument
1063+
.suggestion = name the argument, or use `_` to continue ignoring it
1064+
10621065
lint_variant_size_differences =
10631066
enum variant is more than three times larger ({$largest} bytes) than the next largest

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,5 +476,8 @@ pub fn decorate_builtin_lint(
476476
BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
477477
lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
478478
}
479+
BuiltinLintDiag::VarargsWithoutPattern { span } => {
480+
lints::VarargsWithoutPattern { span }.decorate_lint(diag)
481+
}
479482
}
480483
}

compiler/rustc_lint/src/lints.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3234,6 +3234,13 @@ pub(crate) struct ReservedMultihash {
32343234
pub suggestion: Span,
32353235
}
32363236

3237+
#[derive(LintDiagnostic)]
3238+
#[diag(lint_varargs_without_pattern)]
3239+
pub(crate) struct VarargsWithoutPattern {
3240+
#[suggestion(code = "_: ...", applicability = "machine-applicable")]
3241+
pub span: Span,
3242+
}
3243+
32373244
#[derive(Debug)]
32383245
pub(crate) struct MismatchedLifetimeSyntaxes {
32393246
pub inputs: LifetimeSyntaxCategories<Vec<Span>>,

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ declare_lint_pass! {
142142
UNUSED_UNSAFE,
143143
UNUSED_VARIABLES,
144144
USELESS_DEPRECATED,
145+
VARARGS_WITHOUT_PATTERN,
145146
WARNINGS,
146147
// tidy-alphabetical-end
147148
]
@@ -5138,3 +5139,50 @@ declare_lint! {
51385139
"detects tail calls of functions marked with `#[track_caller]`",
51395140
@feature_gate = explicit_tail_calls;
51405141
}
5142+
5143+
declare_lint! {
5144+
/// The `varargs_without_pattern` lint detects when `...` is used as an argument to a
5145+
/// non-foreign function without any pattern being specified.
5146+
///
5147+
/// ### Example
5148+
///
5149+
/// ```rust
5150+
/// // Using `...` in non-foreign function definitions is unstable, however stability is
5151+
/// // currently only checked after attributes are expanded, so using `#[cfg(false)]` here will
5152+
/// // allow this to compile on stable Rust.
5153+
/// #[cfg(false)]
5154+
/// fn foo(...) {
5155+
///
5156+
/// }
5157+
/// ```
5158+
///
5159+
/// {{produces}}
5160+
///
5161+
/// ### Explanation
5162+
///
5163+
/// Patterns are currently required for all non-`...` arguments in function definitions (with
5164+
/// some exceptions in the 2015 edition). Requiring `...` arguments to have patterns in
5165+
/// non-foreign function defitions makes the language more consistent, and removes a source of
5166+
/// confusion for the unstable C variadic feature. `...` arguments without a pattern are already
5167+
/// stable and widely used in foreign function definitions; this lint only affects non-foreign
5168+
/// function defitions.
5169+
///
5170+
/// Using `...` (C varargs) in a non-foreign function definition is currently unstable. However,
5171+
/// stability checking for the `...` syntax in non-foreign function definitions is currently
5172+
/// implemented after attributes have been expanded, meaning that if the attribute removes the
5173+
/// use of the unstable syntax (e.g. `#[cfg(false)]`, or a procedural macro), the code will
5174+
/// compile on stable Rust; this is the only situtation where this lint affects code that
5175+
/// compiles on stable Rust.
5176+
///
5177+
/// This is a [future-incompatible] lint to transition this to a hard error in the future.
5178+
///
5179+
/// [future-incompatible]: ../index.md#future-incompatible-lints
5180+
pub VARARGS_WITHOUT_PATTERN,
5181+
Warn,
5182+
"detects usage of `...` arguments without a pattern in non-foreign items",
5183+
@future_incompatible = FutureIncompatibleInfo {
5184+
reason: FutureIncompatibilityReason::FutureReleaseError,
5185+
reference: "issue #145544 <https://github.com/rust-lang/rust/issues/145544>",
5186+
report_in_deps: false,
5187+
};
5188+
}

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,9 @@ pub enum BuiltinLintDiag {
809809
cfg_name: Symbol,
810810
controlled_by: &'static str,
811811
},
812+
VarargsWithoutPattern {
813+
span: Span,
814+
},
812815
}
813816

814817
/// Lints that are buffered up early on in the `Session` before the

compiler/rustc_parse/src/parser/attr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ impl<'a> Parser<'a> {
201201
AttrWrapper::empty(),
202202
true,
203203
false,
204-
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true },
204+
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
205205
ForceCollect::No,
206206
) {
207207
Ok(Some(item)) => {

compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use crate::errors::{
4646
};
4747
use crate::parser::FnContext;
4848
use crate::parser::attr::InnerAttrPolicy;
49+
use crate::parser::item::IsDotDotDot;
4950
use crate::{exp, fluent_generated as fluent};
5051

5152
/// Creates a placeholder argument.
@@ -2273,7 +2274,7 @@ impl<'a> Parser<'a> {
22732274
let maybe_emit_anon_params_note = |this: &mut Self, err: &mut Diag<'_>| {
22742275
let ed = this.token.span.with_neighbor(this.prev_token.span).edition();
22752276
if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait)
2276-
&& (fn_parse_mode.req_name)(ed)
2277+
&& (fn_parse_mode.req_name)(ed, IsDotDotDot::No)
22772278
{
22782279
err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
22792280
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use rustc_ast::{self as ast};
1010
use rustc_ast_pretty::pprust;
1111
use rustc_errors::codes::*;
1212
use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err};
13+
use rustc_session::lint::BuiltinLintDiag;
14+
use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN;
1315
use rustc_span::edit_distance::edit_distance;
1416
use rustc_span::edition::Edition;
1517
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, source_map, sym};
@@ -117,7 +119,7 @@ impl<'a> Parser<'a> {
117119
impl<'a> Parser<'a> {
118120
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> {
119121
let fn_parse_mode =
120-
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true };
122+
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
121123
self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(Box::new))
122124
}
123125

@@ -977,7 +979,7 @@ impl<'a> Parser<'a> {
977979
force_collect: ForceCollect,
978980
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
979981
let fn_parse_mode =
980-
FnParseMode { req_name: |_| true, context: FnContext::Impl, req_body: true };
982+
FnParseMode { req_name: |_, _| true, context: FnContext::Impl, req_body: true };
981983
self.parse_assoc_item(fn_parse_mode, force_collect)
982984
}
983985

@@ -986,7 +988,7 @@ impl<'a> Parser<'a> {
986988
force_collect: ForceCollect,
987989
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
988990
let fn_parse_mode = FnParseMode {
989-
req_name: |edition| edition >= Edition::Edition2018,
991+
req_name: |edition, _| edition >= Edition::Edition2018,
990992
context: FnContext::Trait,
991993
req_body: false,
992994
};
@@ -1266,8 +1268,11 @@ impl<'a> Parser<'a> {
12661268
&mut self,
12671269
force_collect: ForceCollect,
12681270
) -> PResult<'a, Option<Option<Box<ForeignItem>>>> {
1269-
let fn_parse_mode =
1270-
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: false };
1271+
let fn_parse_mode = FnParseMode {
1272+
req_name: |_, is_dot_dot_dot| is_dot_dot_dot == IsDotDotDot::No,
1273+
context: FnContext::Free,
1274+
req_body: false,
1275+
};
12711276
Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
12721277
|Item { attrs, id, span, vis, kind, tokens }| {
12731278
let kind = match ForeignItemKind::try_from(kind) {
@@ -2142,7 +2147,7 @@ impl<'a> Parser<'a> {
21422147
Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited, tokens: None };
21432148
// We use `parse_fn` to get a span for the function
21442149
let fn_parse_mode =
2145-
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true };
2150+
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
21462151
match self.parse_fn(
21472152
&mut AttrVec::new(),
21482153
fn_parse_mode,
@@ -2375,8 +2380,16 @@ impl<'a> Parser<'a> {
23752380
/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type.
23762381
///
23772382
/// This function pointer accepts an edition, because in edition 2015, trait declarations
2378-
/// were allowed to omit parameter names. In 2018, they became required.
2379-
type ReqName = fn(Edition) -> bool;
2383+
/// were allowed to omit parameter names. In 2018, they became required. It also accepts an
2384+
/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are
2385+
/// allowed to omit the name of the `...` but regular function items are not.
2386+
type ReqName = fn(Edition, IsDotDotDot) -> bool;
2387+
2388+
#[derive(Copy, Clone, PartialEq)]
2389+
pub(crate) enum IsDotDotDot {
2390+
Yes,
2391+
No,
2392+
}
23802393

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

3058-
let is_name_required = match this.token.kind {
3059-
token::DotDotDot => false,
3060-
_ => (fn_parse_mode.req_name)(
3061-
this.token.span.with_neighbor(this.prev_token.span).edition(),
3062-
),
3074+
let is_dot_dot_dot = if this.token.kind == token::DotDotDot {
3075+
IsDotDotDot::Yes
3076+
} else {
3077+
IsDotDotDot::No
3078+
};
3079+
let is_name_required = (fn_parse_mode.req_name)(
3080+
this.token.span.with_neighbor(this.prev_token.span).edition(),
3081+
is_dot_dot_dot,
3082+
);
3083+
let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes {
3084+
this.psess.buffer_lint(
3085+
VARARGS_WITHOUT_PATTERN,
3086+
this.token.span,
3087+
ast::CRATE_NODE_ID,
3088+
BuiltinLintDiag::VarargsWithoutPattern { span: this.token.span },
3089+
);
3090+
false
3091+
} else {
3092+
is_name_required
30633093
};
30643094
let (pat, ty) = if is_name_required || this.is_named_param() {
30653095
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);

compiler/rustc_parse/src/parser/path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ impl<'a> Parser<'a> {
404404
// Inside parenthesized type arguments, we want types only, not names.
405405
let mode = FnParseMode {
406406
context: FnContext::Free,
407-
req_name: |_| false,
407+
req_name: |_, _| false,
408408
req_body: false,
409409
};
410410
let param = p.parse_param_general(&mode, false, false);

compiler/rustc_parse/src/parser/stmt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ impl<'a> Parser<'a> {
153153
attrs.clone(), // FIXME: unwanted clone of attrs
154154
false,
155155
true,
156-
FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true },
156+
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true },
157157
force_collect,
158158
)? {
159159
self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item)))

0 commit comments

Comments
 (0)