Skip to content

Commit ceea3c6

Browse files
authored
Rollup merge of #5248 - ThibsG:ConstValues, r=flip1995
Add lint on large non scalar const This PR adds the new lint `non_scalar_const` that aims to warn against `const` declaration of large arrays. For performance, because of inlining, large arrays should be preferably declared as `static`. Note: i made this one to warn on all const arrays, whether they are in a body function or not. I don't know if this is really necessary, i could just reduce this lint to variables out of function scope. Fixes: #400 changelog: add new lint for large non-scalar types declared as const
2 parents 3481bf4 + 629cc4a commit ceea3c6

File tree

9 files changed

+253
-5
lines changed

9 files changed

+253
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,7 @@ Released 2018-09-13
13201320
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
13211321
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
13221322
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
1323+
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
13231324
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
13241325
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
13251326
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use crate::rustc_target::abi::LayoutOf;
2+
use crate::utils::span_lint_and_then;
3+
use if_chain::if_chain;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Item, ItemKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_middle::mir::interpret::ConstValue;
8+
use rustc_middle::ty::{self, ConstKind};
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::{BytePos, Pos, Span};
11+
use rustc_typeck::hir_ty_to_ty;
12+
13+
declare_clippy_lint! {
14+
/// **What it does:** Checks for large `const` arrays that should
15+
/// be defined as `static` instead.
16+
///
17+
/// **Why is this bad?** Performance: const variables are inlined upon use.
18+
/// Static items result in only one instance and has a fixed location in memory.
19+
///
20+
/// **Known problems:** None.
21+
///
22+
/// **Example:**
23+
/// ```rust,ignore
24+
/// // Bad
25+
/// pub const a = [0u32; 1_000_000];
26+
///
27+
/// // Good
28+
/// pub static a = [0u32; 1_000_000];
29+
/// ```
30+
pub LARGE_CONST_ARRAYS,
31+
perf,
32+
"large non-scalar const array may cause performance overhead"
33+
}
34+
35+
pub struct LargeConstArrays {
36+
maximum_allowed_size: u64,
37+
}
38+
39+
impl LargeConstArrays {
40+
#[must_use]
41+
pub fn new(maximum_allowed_size: u64) -> Self {
42+
Self { maximum_allowed_size }
43+
}
44+
}
45+
46+
impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
47+
48+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeConstArrays {
49+
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) {
50+
if_chain! {
51+
if !item.span.from_expansion();
52+
if let ItemKind::Const(hir_ty, _) = &item.kind;
53+
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
54+
if let ty::Array(element_type, cst) = ty.kind;
55+
if let ConstKind::Value(val) = cst.val;
56+
if let ConstValue::Scalar(element_count) = val;
57+
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
58+
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
59+
if self.maximum_allowed_size < element_count * element_size;
60+
61+
then {
62+
let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
63+
let sugg_span = Span::new(
64+
hi_pos - BytePos::from_usize("const".len()),
65+
hi_pos,
66+
item.span.ctxt(),
67+
);
68+
span_lint_and_then(
69+
cx,
70+
LARGE_CONST_ARRAYS,
71+
item.span,
72+
"large array defined as const",
73+
|db| {
74+
db.span_suggestion(
75+
sugg_span,
76+
"make this a static item",
77+
"static".to_string(),
78+
Applicability::MachineApplicable,
79+
);
80+
}
81+
);
82+
}
83+
}
84+
}
85+
}

clippy_lints/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ mod inline_fn_without_body;
232232
mod int_plus_one;
233233
mod integer_division;
234234
mod items_after_statements;
235+
mod large_const_arrays;
235236
mod large_enum_variant;
236237
mod large_stack_arrays;
237238
mod len_zero;
@@ -582,6 +583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
582583
&int_plus_one::INT_PLUS_ONE,
583584
&integer_division::INTEGER_DIVISION,
584585
&items_after_statements::ITEMS_AFTER_STATEMENTS,
586+
&large_const_arrays::LARGE_CONST_ARRAYS,
585587
&large_enum_variant::LARGE_ENUM_VARIANT,
586588
&large_stack_arrays::LARGE_STACK_ARRAYS,
587589
&len_zero::LEN_WITHOUT_IS_EMPTY,
@@ -1026,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10261028
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
10271029
let array_size_threshold = conf.array_size_threshold;
10281030
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));
1031+
store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold));
10291032
store.register_late_pass(move || box floating_point_arithmetic::FloatingPointArithmetic);
10301033
store.register_early_pass(|| box as_conversions::AsConversions);
10311034
store.register_early_pass(|| box utils::internal_lints::ProduceIce);
@@ -1225,6 +1228,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12251228
LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
12261229
LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
12271230
LintId::of(&int_plus_one::INT_PLUS_ONE),
1231+
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
12281232
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
12291233
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
12301234
LintId::of(&len_zero::LEN_ZERO),
@@ -1656,6 +1660,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16561660
LintId::of(&bytecount::NAIVE_BYTECOUNT),
16571661
LintId::of(&entry::MAP_ENTRY),
16581662
LintId::of(&escape::BOXED_LOCAL),
1663+
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
16591664
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
16601665
LintId::of(&loops::MANUAL_MEMCPY),
16611666
LintId::of(&loops::NEEDLESS_COLLECT),

clippy_lints/src/utils/conf.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ define_Conf! {
150150
(trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
151151
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
152152
(too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
153-
/// Lint: LARGE_STACK_ARRAYS. The maximum allowed size for arrays on the stack
153+
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
154154
(array_size_threshold, "array_size_threshold": u64, 512_000),
155155
/// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed
156156
(vec_box_size_threshold, "vec_box_size_threshold": u64, 4096),

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
948948
deprecation: None,
949949
module: "non_expressive_names",
950950
},
951+
Lint {
952+
name: "large_const_arrays",
953+
group: "perf",
954+
desc: "large non-scalar const array may cause performance overhead",
955+
deprecation: None,
956+
module: "large_const_arrays",
957+
},
951958
Lint {
952959
name: "large_digit_groups",
953960
group: "pedantic",

tests/ui/large_const_arrays.fixed

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::large_const_arrays)]
4+
#![allow(dead_code)]
5+
6+
#[derive(Clone, Copy)]
7+
pub struct S {
8+
pub data: [u64; 32],
9+
}
10+
11+
// Should lint
12+
pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
13+
pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
14+
static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
15+
16+
// Good
17+
pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
18+
pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
19+
const G_FOO: [u32; 1_000] = [0u32; 1_000];
20+
21+
fn main() {
22+
// Should lint
23+
pub static BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
24+
static BAR: [u32; 1_000_000] = [0u32; 1_000_000];
25+
pub static BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
26+
static BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
27+
pub static BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
28+
static BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
29+
30+
// Good
31+
pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
32+
const G_BAR: [u32; 1_000] = [0u32; 1_000];
33+
pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
34+
const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
35+
pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
36+
const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
37+
}

tests/ui/large_const_arrays.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::large_const_arrays)]
4+
#![allow(dead_code)]
5+
6+
#[derive(Clone, Copy)]
7+
pub struct S {
8+
pub data: [u64; 32],
9+
}
10+
11+
// Should lint
12+
pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
13+
pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
14+
const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
15+
16+
// Good
17+
pub(crate) const G_FOO_PUB_CRATE: [u32; 1_000] = [0u32; 1_000];
18+
pub const G_FOO_PUB: [u32; 1_000] = [0u32; 1_000];
19+
const G_FOO: [u32; 1_000] = [0u32; 1_000];
20+
21+
fn main() {
22+
// Should lint
23+
pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
24+
const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
25+
pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
26+
const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
27+
pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
28+
const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
29+
30+
// Good
31+
pub const G_BAR_PUB: [u32; 1_000] = [0u32; 1_000];
32+
const G_BAR: [u32; 1_000] = [0u32; 1_000];
33+
pub const G_BAR_STRUCT_PUB: [S; 500] = [S { data: [0; 32] }; 500];
34+
const G_BAR_STRUCT: [S; 500] = [S { data: [0; 32] }; 500];
35+
pub const G_BAR_S_PUB: [Option<&str>; 200] = [Some("str"); 200];
36+
const G_BAR_S: [Option<&str>; 200] = [Some("str"); 200];
37+
}

tests/ui/large_const_arrays.stderr

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
error: large array defined as const
2+
--> $DIR/large_const_arrays.rs:12:1
3+
|
4+
LL | pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
5+
| ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
| |
7+
| help: make this a static item: `static`
8+
|
9+
= note: `-D clippy::large-const-arrays` implied by `-D warnings`
10+
11+
error: large array defined as const
12+
--> $DIR/large_const_arrays.rs:13:1
13+
|
14+
LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
15+
| ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
| |
17+
| help: make this a static item: `static`
18+
19+
error: large array defined as const
20+
--> $DIR/large_const_arrays.rs:14:1
21+
|
22+
LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
23+
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
| |
25+
| help: make this a static item: `static`
26+
27+
error: large array defined as const
28+
--> $DIR/large_const_arrays.rs:23:5
29+
|
30+
LL | pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
31+
| ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
| |
33+
| help: make this a static item: `static`
34+
35+
error: large array defined as const
36+
--> $DIR/large_const_arrays.rs:24:5
37+
|
38+
LL | const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
39+
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
| |
41+
| help: make this a static item: `static`
42+
43+
error: large array defined as const
44+
--> $DIR/large_const_arrays.rs:25:5
45+
|
46+
LL | pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
47+
| ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
48+
| |
49+
| help: make this a static item: `static`
50+
51+
error: large array defined as const
52+
--> $DIR/large_const_arrays.rs:26:5
53+
|
54+
LL | const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
55+
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56+
| |
57+
| help: make this a static item: `static`
58+
59+
error: large array defined as const
60+
--> $DIR/large_const_arrays.rs:27:5
61+
|
62+
LL | pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
63+
| ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64+
| |
65+
| help: make this a static item: `static`
66+
67+
error: large array defined as const
68+
--> $DIR/large_const_arrays.rs:28:5
69+
|
70+
LL | const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
71+
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
72+
| |
73+
| help: make this a static item: `static`
74+
75+
error: aborting due to 9 previous errors
76+

util/lintlib.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
group_re = re.compile(r'''\s*([a-z_][a-z_0-9]+)''')
1515
conf_re = re.compile(r'''define_Conf! {\n([^}]*)\n}''', re.MULTILINE)
1616
confvar_re = re.compile(
17-
r'''/// Lint: (\w+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
17+
r'''/// Lint: ([\w,\s]+)\. (.*)\n\s*\([^,]+,\s+"([^"]+)":\s+([^,]+),\s+([^\.\)]+).*\),''', re.MULTILINE)
1818
comment_re = re.compile(r'''\s*/// ?(.*)''')
1919

2020
lint_levels = {
@@ -93,9 +93,9 @@ def parse_configs(path):
9393
match = re.search(conf_re, contents)
9494
confvars = re.findall(confvar_re, match.group(1))
9595

96-
for (lint, doc, name, ty, default) in confvars:
97-
configs[lint.lower()] = Config(name.replace("_", "-"), ty, doc, default)
98-
96+
for (lints, doc, name, ty, default) in confvars:
97+
for lint in lints.split(','):
98+
configs[lint.strip().lower()] = Config(name.replace("_", "-"), ty, doc, default)
9999
return configs
100100

101101

0 commit comments

Comments
 (0)