Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add large_stack_arrays lint #4807

Merged
merged 1 commit into from
Nov 23, 2019
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ Released 2018-09-13
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.

[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are 334 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)

We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:

Expand Down
67 changes: 67 additions & 0 deletions clippy_lints/src/large_stack_arrays.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::mir::interpret::ConstValue;
use rustc::ty;
use rustc::{declare_tool_lint, impl_lint_pass};

use if_chain::if_chain;

use crate::rustc_target::abi::LayoutOf;
use crate::utils::{snippet, span_help_and_lint};

declare_clippy_lint! {
/// **What it does:** Checks for local arrays that may be too large.
///
/// **Why is this bad?** Large local arrays may cause stack overflow.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let a = [0u32; 1_000_000];
/// ```
pub LARGE_STACK_ARRAYS,
pedantic,
"allocating large arrays on stack may cause stack overflow"
}

pub struct LargeStackArrays {
maximum_allowed_size: u64,
}

impl LargeStackArrays {
#[must_use]
pub fn new(maximum_allowed_size: u64) -> Self {
Self { maximum_allowed_size }
}
}

impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeStackArrays {
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
if_chain! {
if let ExprKind::Repeat(_, _) = expr.kind;
if let ty::Array(element_type, cst) = cx.tables.expr_ty(expr).kind;
if let ConstValue::Scalar(element_count) = cst.val;
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
if self.maximum_allowed_size < element_count * element_size;
then {
span_help_and_lint(
flip1995 marked this conversation as resolved.
Show resolved Hide resolved
cx,
LARGE_STACK_ARRAYS,
expr.span,
&format!(
"allocating a local array larger than {} bytes",
self.maximum_allowed_size
),
&format!(
"consider allocating on the heap with vec!{}.into_boxed_slice()",
snippet(cx, expr.span, "[...]")
),
);
}
}
}
}
5 changes: 5 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ pub mod int_plus_one;
pub mod integer_division;
pub mod items_after_statements;
pub mod large_enum_variant;
pub mod large_stack_arrays;
pub mod len_zero;
pub mod let_if_seq;
pub mod lifetimes;
Expand Down Expand Up @@ -537,6 +538,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
&integer_division::INTEGER_DIVISION,
&items_after_statements::ITEMS_AFTER_STATEMENTS,
&large_enum_variant::LARGE_ENUM_VARIANT,
&large_stack_arrays::LARGE_STACK_ARRAYS,
&len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO,
&let_if_seq::USELESS_LET_IF_SEQ,
Expand Down Expand Up @@ -949,6 +951,8 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall);
store.register_late_pass(|| box exit::Exit);
store.register_late_pass(|| box to_digit_is_some::ToDigitIsSome);
let array_size_threshold = conf.array_size_threshold;
store.register_late_pass(move || box large_stack_arrays::LargeStackArrays::new(array_size_threshold));

store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
Expand Down Expand Up @@ -1002,6 +1006,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
LintId::of(&if_not_else::IF_NOT_ELSE),
LintId::of(&infinite_iter::MAYBE_INFINITE_ITER),
LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS),
LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS),
LintId::of(&literal_representation::LARGE_DIGIT_GROUPS),
LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP),
LintId::of(&loops::EXPLICIT_ITER_LOOP),
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/utils/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ define_Conf! {
(trivial_copy_size_limit, "trivial_copy_size_limit", None => Option<u64>),
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
(too_many_lines_threshold, "too_many_lines_threshold", 100 => u64),
/// Lint: LARGE_STACK_ARRAYS. The maximum allowed size for arrays on the stack
(array_size_threshold, "array_size_threshold", 512_000 => u64),
}

impl Default for Conf {
Expand Down
9 changes: 8 additions & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use lint::Lint;
pub use lint::LINT_LEVELS;

// begin lint list, do not remove this comment, it’s used in `update_lints`
pub const ALL_LINTS: [Lint; 333] = [
pub const ALL_LINTS: [Lint; 334] = [
Lint {
name: "absurd_extreme_comparisons",
group: "correctness",
Expand Down Expand Up @@ -903,6 +903,13 @@ pub const ALL_LINTS: [Lint; 333] = [
deprecation: None,
module: "large_enum_variant",
},
Lint {
name: "large_stack_arrays",
group: "pedantic",
desc: "allocating large arrays on stack may cause stack overflow",
deprecation: None,
module: "large_stack_arrays",
},
Lint {
name: "len_without_is_empty",
group: "style",
Expand Down
2 changes: 1 addition & 1 deletion tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `third-party` at line 5 column 1

error: aborting due to previous error

30 changes: 30 additions & 0 deletions tests/ui/large_stack_arrays.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![warn(clippy::large_stack_arrays)]
#![allow(clippy::large_enum_variant)]

#[derive(Clone, Copy)]
struct S {
pub data: [u64; 32],
}

#[derive(Clone, Copy)]
enum E {
S(S),
T(u32),
}

fn main() {
let bad = (
[0u32; 20_000_000],
[S { data: [0; 32] }; 5000],
[Some(""); 20_000_000],
[E::T(0); 5000],
);

let good = (
[0u32; 1000],
[S { data: [0; 32] }; 1000],
[Some(""); 1000],
[E::T(0); 1000],
[(); 20_000_000],
);
}
35 changes: 35 additions & 0 deletions tests/ui/large_stack_arrays.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:17:9
|
LL | [0u32; 20_000_000],
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::large-stack-arrays` implied by `-D warnings`
= help: consider allocating on the heap with vec![0u32; 20_000_000].into_boxed_slice()

error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:18:9
|
LL | [S { data: [0; 32] }; 5000],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![S { data: [0; 32] }; 5000].into_boxed_slice()

error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:19:9
|
LL | [Some(""); 20_000_000],
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![Some(""); 20_000_000].into_boxed_slice()

error: allocating a local array larger than 512000 bytes
--> $DIR/large_stack_arrays.rs:20:9
|
LL | [E::T(0); 5000],
| ^^^^^^^^^^^^^^^
|
= help: consider allocating on the heap with vec![E::T(0); 5000].into_boxed_slice()

error: aborting due to 4 previous errors