Skip to content

Commit ac23a6b

Browse files
committed
Uplift clippy::zero_prefixed_literal as leading_zeros_in_decimal_literals
1 parent ae2fc97 commit ac23a6b

File tree

19 files changed

+265
-257
lines changed

19 files changed

+265
-257
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,10 @@ lint_invalid_reference_casting_note_book = for more information, visit <https://
475475
476476
lint_invalid_reference_casting_note_ty_has_interior_mutability = even for types with interior mutability, the only legal way to obtain a mutable pointer from a shared reference is through `UnsafeCell::get`
477477
478+
lint_leading_zeros_in_decimal_literals = this is a decimal constant
479+
.suggestion_remove_zeros = if you meant to use a decimal constant, remove leading zeros to avoid confusion
480+
.suggestion_prefix_octal = if you meant to use an octal constant, prefix it with `0o` instead
481+
478482
lint_legacy_derive_helpers = derive helper attribute is used before it is introduced
479483
.label = the attribute is introduced here
480484

compiler/rustc_lint/src/builtin.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_ast::visit::{FnCtxt, FnKind};
2222
use rustc_ast::{self as ast, *};
2323
use rustc_ast_pretty::pprust::expr_to_string;
2424
use rustc_attr_data_structures::{AttributeKind, find_attr};
25+
use rustc_data_structures::packed::Pu128;
2526
use rustc_errors::{Applicability, LintDiagnostic};
2627
use rustc_feature::GateIssue;
2728
use rustc_hir as hir;
@@ -58,7 +59,7 @@ use crate::lints::{
5859
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
5960
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,
6061
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
61-
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
62+
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, LeadingZeros,
6263
};
6364
use crate::nonstandard_style::{MethodLateContext, method_context};
6465
use crate::{
@@ -3103,3 +3104,73 @@ impl EarlyLintPass for SpecialModuleName {
31033104
}
31043105
}
31053106
}
3107+
3108+
declare_lint! {
3109+
/// The `leading_zeros_in_decimal_literals` lint
3110+
/// detects decimal integral literals with leading zeros.
3111+
///
3112+
/// ### Example
3113+
///
3114+
/// ```rust,no_run
3115+
/// fn is_executable(unix_mode: u32) -> bool {
3116+
/// unix_mode & 0111 != 0
3117+
/// }
3118+
/// ```
3119+
///
3120+
/// {{produces}}
3121+
///
3122+
/// ### Explanation
3123+
/// In some languages (including the infamous C language and most of its family),
3124+
/// a leading zero marks an octal constant. In Rust however, a `0o` prefix is used instead.
3125+
/// Thus, a leading zero can be confusing for both the writer and a reader.
3126+
///
3127+
/// In Rust:
3128+
/// ```rust,no_run
3129+
/// fn main() {
3130+
/// let a = 0123;
3131+
/// println!("{}", a);
3132+
/// }
3133+
/// ```
3134+
///
3135+
/// prints `123`, while in C:
3136+
///
3137+
/// ```c
3138+
/// #include <stdio.h>
3139+
///
3140+
/// int main() {
3141+
/// int a = 0123;
3142+
/// printf("%d\n", a);
3143+
/// }
3144+
/// ```
3145+
///
3146+
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
3147+
pub LEADING_ZEROS_IN_DECIMAL_LITERALS,
3148+
Warn,
3149+
"leading `0` in decimal integer literals",
3150+
}
3151+
3152+
declare_lint_pass!(LeadingZerosInDecimals => [LEADING_ZEROS_IN_DECIMAL_LITERALS]);
3153+
3154+
impl EarlyLintPass for LeadingZerosInDecimals {
3155+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
3156+
if let ExprKind::Lit(literal) = expr.kind
3157+
&& let Ok(LitKind::Int(Pu128(10..), _)) = LitKind::from_token_lit(literal)
3158+
&& let s = literal.symbol.as_str()
3159+
&& let Some(tail) = s.strip_prefix('0')
3160+
&& !tail.starts_with(['b', 'o', 'x'])
3161+
&& let nonzero_digits = tail.trim_start_matches(['0', '_'])
3162+
&& !nonzero_digits.contains(['8', '9'])
3163+
{
3164+
let lit_span = expr.span;
3165+
let zeros_offset = s.len() - nonzero_digits.len();
3166+
cx.emit_span_lint(
3167+
LEADING_ZEROS_IN_DECIMAL_LITERALS,
3168+
lit_span,
3169+
LeadingZeros {
3170+
remove_zeros: lit_span.with_hi(lit_span.lo() + BytePos(zeros_offset as u32)),
3171+
prefix_octal: lit_span.with_hi(lit_span.lo() + BytePos(1)),
3172+
},
3173+
);
3174+
}
3175+
}
3176+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ early_lint_methods!(
182182
Expr2024: Expr2024,
183183
Precedence: Precedence,
184184
DoubleNegations: DoubleNegations,
185+
LeadingZerosInDecimals: LeadingZerosInDecimals,
185186
]
186187
]
187188
);

compiler/rustc_lint/src/lints.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3298,3 +3298,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
32983298
}
32993299
}
33003300
}
3301+
3302+
#[derive(LintDiagnostic)]
3303+
#[diag(lint_leading_zeros_in_decimal_literals)]
3304+
pub(crate) struct LeadingZeros {
3305+
#[suggestion(lint_suggestion_remove_zeros, code = "", applicability = "maybe-incorrect")]
3306+
pub remove_zeros: Span,
3307+
#[suggestion(lint_suggestion_prefix_octal, code = "0o", applicability = "maybe-incorrect")]
3308+
pub prefix_octal: Span,
3309+
}

library/coretests/tests/time.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(leading_zeros_in_decimal_literals)]
2+
13
use core::time::Duration;
24

35
#[test]

src/tools/clippy/clippy_lints/src/declared_lints.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
511511
crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO,
512512
crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO,
513513
crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
514-
crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
515514
crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
516515
crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
517516
crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO,

src/tools/clippy/clippy_lints/src/deprecated_lints.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [
192192
("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
193193
#[clippy::version = ""]
194194
("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"),
195+
#[clippy::version = "CURRENT_RUSTC_VERSION"]
196+
("clippy::zero_prefixed_literal", "leading_zeros_in_decimal_literals"),
195197
#[clippy::version = ""]
196198
("clippy::zero_width_space", "clippy::invisible_characters"),
197199
]}

src/tools/clippy/clippy_lints/src/misc_early/mod.rs

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ mod redundant_at_rest_pattern;
55
mod redundant_pattern;
66
mod unneeded_field_pattern;
77
mod unneeded_wildcard_pattern;
8-
mod zero_prefixed_literal;
98

109
use clippy_utils::diagnostics::span_lint;
1110
use clippy_utils::source::snippet_opt;
@@ -167,45 +166,6 @@ declare_clippy_lint! {
167166
"literals whose suffix is separated by an underscore"
168167
}
169168

170-
declare_clippy_lint! {
171-
/// ### What it does
172-
/// Warns if an integral constant literal starts with `0`.
173-
///
174-
/// ### Why is this bad?
175-
/// In some languages (including the infamous C language
176-
/// and most of its
177-
/// family), this marks an octal constant. In Rust however, this is a decimal
178-
/// constant. This could
179-
/// be confusing for both the writer and a reader of the constant.
180-
///
181-
/// ### Example
182-
///
183-
/// In Rust:
184-
/// ```no_run
185-
/// fn main() {
186-
/// let a = 0123;
187-
/// println!("{}", a);
188-
/// }
189-
/// ```
190-
///
191-
/// prints `123`, while in C:
192-
///
193-
/// ```c
194-
/// #include <stdio.h>
195-
///
196-
/// int main() {
197-
/// int a = 0123;
198-
/// printf("%d\n", a);
199-
/// }
200-
/// ```
201-
///
202-
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
203-
#[clippy::version = "pre 1.29.0"]
204-
pub ZERO_PREFIXED_LITERAL,
205-
complexity,
206-
"integer literals starting with `0`"
207-
}
208-
209169
declare_clippy_lint! {
210170
/// ### What it does
211171
/// Warns if a generic shadows a built-in type.
@@ -334,7 +294,6 @@ declare_lint_pass!(MiscEarlyLints => [
334294
MIXED_CASE_HEX_LITERALS,
335295
UNSEPARATED_LITERAL_SUFFIX,
336296
SEPARATED_LITERAL_SUFFIX,
337-
ZERO_PREFIXED_LITERAL,
338297
BUILTIN_TYPE_SHADOW,
339298
REDUNDANT_PATTERN,
340299
UNNEEDED_WILDCARD_PATTERN,
@@ -409,7 +368,7 @@ impl MiscEarlyLints {
409368
};
410369

411370
let lit_kind = LitKind::from_token_lit(lit);
412-
if let Ok(LitKind::Int(value, lit_int_type)) = lit_kind {
371+
if let Ok(LitKind::Int(_value, lit_int_type)) = lit_kind {
413372
let suffix = match lit_int_type {
414373
LitIntType::Signed(ty) => ty.name_str(),
415374
LitIntType::Unsigned(ty) => ty.name_str(),
@@ -418,10 +377,6 @@ impl MiscEarlyLints {
418377
literal_suffix::check(cx, span, &lit_snip, suffix, "integer");
419378
if lit_snip.starts_with("0x") {
420379
mixed_case_hex_literals::check(cx, span, suffix, &lit_snip);
421-
} else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
422-
// nothing to do
423-
} else if value != 0 && lit_snip.starts_with('0') {
424-
zero_prefixed_literal::check(cx, span, &lit_snip);
425380
}
426381
} else if let Ok(LitKind::Float(_, LitFloatType::Suffixed(float_ty))) = lit_kind {
427382
let suffix = float_ty.name_str();

src/tools/clippy/clippy_lints/src/misc_early/zero_prefixed_literal.rs

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/tools/clippy/tests/ui/literals.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
// does not test any rustfixable lints
33

44
#![warn(clippy::mixed_case_hex_literals)]
5-
#![warn(clippy::zero_prefixed_literal)]
65
#![warn(clippy::unseparated_literal_suffix)]
76
#![warn(clippy::separated_literal_suffix)]
8-
#![allow(dead_code, overflowing_literals)]
7+
#![allow(dead_code, overflowing_literals, leading_zeros_in_decimal_literals)]
98

109
fn main() {
1110
let ok1 = 0xABCD;
@@ -36,14 +35,12 @@ fn main() {
3635

3736
let fail_multi_zero = 000_123usize;
3837
//~^ unseparated_literal_suffix
39-
//~| zero_prefixed_literal
4038

4139
let ok9 = 0;
4240
let ok10 = 0_i64;
4341
//~^ separated_literal_suffix
4442

45-
let fail8 = 0123;
46-
//~^ zero_prefixed_literal
43+
let ok10andhalf = 0123;
4744

4845
let ok11 = 0o123;
4946
let ok12 = 0b10_1010;
@@ -75,13 +72,8 @@ fn main() {
7572
}
7673

7774
fn issue9651() {
78-
// lint but octal form is not possible here
75+
// octal form is not possible here
7976
let _ = 08;
80-
//~^ zero_prefixed_literal
81-
8277
let _ = 09;
83-
//~^ zero_prefixed_literal
84-
8578
let _ = 089;
86-
//~^ zero_prefixed_literal
8779
}

0 commit comments

Comments
 (0)