test [large_stack_arrays
] with proc-macro and make sure not to offer false help messages if in one.
This commit is contained in:
parent
666e2f2868
commit
2861729dad
@ -1,7 +1,8 @@
|
|||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::is_from_proc_macro;
|
||||||
use clippy_utils::macros::macro_backtrace;
|
use clippy_utils::macros::macro_backtrace;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
|
use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_middle::ty::{self, ConstKind};
|
use rustc_middle::ty::{self, ConstKind};
|
||||||
@ -27,21 +28,41 @@
|
|||||||
|
|
||||||
pub struct LargeStackArrays {
|
pub struct LargeStackArrays {
|
||||||
maximum_allowed_size: u128,
|
maximum_allowed_size: u128,
|
||||||
|
prev_vec_macro_callsite: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LargeStackArrays {
|
impl LargeStackArrays {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(maximum_allowed_size: u128) -> Self {
|
pub fn new(maximum_allowed_size: u128) -> Self {
|
||||||
Self { maximum_allowed_size }
|
Self {
|
||||||
|
maximum_allowed_size,
|
||||||
|
prev_vec_macro_callsite: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the given span of an expr is already in a `vec!` call.
|
||||||
|
fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool {
|
||||||
|
// First, we check if this is span is within the last encountered `vec!` macro's root callsite.
|
||||||
|
self.prev_vec_macro_callsite
|
||||||
|
.is_some_and(|vec_mac| vec_mac.contains(span))
|
||||||
|
|| {
|
||||||
|
// Then, we try backtracking the macro expansions, to see if there's a `vec!` macro,
|
||||||
|
// and update the `prev_vec_macro_callsite`.
|
||||||
|
let res = macro_backtrace(span).any(|mac| cx.tcx.is_diagnostic_item(sym::vec_macro, mac.def_id));
|
||||||
|
if res {
|
||||||
|
self.prev_vec_macro_callsite = Some(span.source_callsite());
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||||
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
||||||
&& !is_from_vec_macro(cx, expr.span)
|
&& !self.is_from_vec_macro(cx, expr.span)
|
||||||
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||||
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
||||||
@ -66,7 +87,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||||||
self.maximum_allowed_size
|
self.maximum_allowed_size
|
||||||
),
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
if !expr.span.from_expansion() {
|
if !might_be_expanded(cx, expr) {
|
||||||
diag.help(format!(
|
diag.help(format!(
|
||||||
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
||||||
snippet(cx, expr.span, "[...]")
|
snippet(cx, expr.span, "[...]")
|
||||||
@ -78,7 +99,20 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We shouldn't lint messages if the expr is already in a `vec!` call
|
/// Only giving help messages if the expr does not contains macro expanded codes.
|
||||||
fn is_from_vec_macro(cx: &LateContext<'_>, expr_span: Span) -> bool {
|
fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
|
||||||
macro_backtrace(expr_span).any(|mac| cx.tcx.is_diagnostic_item(sym::vec_macro, mac.def_id))
|
/// Check if the span of `ArrayLen` of a repeat expression is within the expr's span,
|
||||||
|
/// if not, meaning this repeat expr is definitely from some proc-macro.
|
||||||
|
///
|
||||||
|
/// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the
|
||||||
|
/// correct result.
|
||||||
|
fn repeat_expr_might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
|
||||||
|
let ExprKind::Repeat(_, ArrayLen::Body(anon_const)) = expr.kind else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let len_span = cx.tcx.def_span(anon_const.def_id);
|
||||||
|
!expr.span.contains(len_span)
|
||||||
|
}
|
||||||
|
|
||||||
|
expr.span.from_expansion() || is_from_proc_macro(cx, expr) || repeat_expr_might_be_expanded(cx, expr)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
use proc_macro::Delimiter::{self, Brace, Parenthesis};
|
use proc_macro::Delimiter::{self, Brace, Parenthesis};
|
||||||
use proc_macro::Spacing::{self, Alone, Joint};
|
use proc_macro::Spacing::{self, Alone, Joint};
|
||||||
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
|
use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT};
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
type Result<T> = core::result::Result<T, TokenStream>;
|
type Result<T> = core::result::Result<T, TokenStream>;
|
||||||
|
|
||||||
@ -124,6 +125,22 @@ fn write_with_span(s: Span, mut input: IntoIter, out: &mut TokenStream) -> Resul
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes an array repeat expression such as `[0_u32; 2]`, and return the tokens with 10 times the
|
||||||
|
/// original size, which turns to `[0_u32; 20]`.
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn make_it_big(input: TokenStream) -> TokenStream {
|
||||||
|
let mut expr_repeat = syn::parse_macro_input!(input as syn::ExprRepeat);
|
||||||
|
let len_span = expr_repeat.len.span();
|
||||||
|
if let syn::Expr::Lit(expr_lit) = &mut *expr_repeat.len {
|
||||||
|
if let syn::Lit::Int(lit_int) = &expr_lit.lit {
|
||||||
|
let orig_val = lit_int.base10_parse::<usize>().expect("not a valid length parameter");
|
||||||
|
let new_val = orig_val.saturating_mul(10);
|
||||||
|
expr_lit.lit = syn::parse_quote_spanned!( len_span => #new_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quote::quote!(#expr_repeat).into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
|
/// Within the item this attribute is attached to, an `inline!` macro is available which expands the
|
||||||
/// contained tokens as though they came from a macro expansion.
|
/// contained tokens as though they came from a macro expansion.
|
||||||
///
|
///
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
//@aux-build:proc_macros.rs
|
||||||
#![warn(clippy::large_stack_arrays)]
|
#![warn(clippy::large_stack_arrays)]
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
|
||||||
|
extern crate proc_macros;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct S {
|
struct S {
|
||||||
pub data: [u64; 32],
|
pub data: [u64; 32],
|
||||||
@ -74,12 +77,18 @@ macro_rules! dummy {
|
|||||||
::std::vec![$($id),*]
|
::std::vec![$($id),*]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
macro_rules! create_then_move {
|
||||||
|
($id:ident; $n:literal) => {{
|
||||||
|
let _x_ = [$id; $n];
|
||||||
|
//~^ ERROR: allocating a local array larger than 512000 bytes
|
||||||
|
_x_
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
let x = [0u32; 50_000];
|
let x = [0u32; 50_000];
|
||||||
let y = vec![x, x, x, x, x];
|
let y = vec![x, x, x, x, x];
|
||||||
let y = vec![dummy![x, x, x, x, x]];
|
let y = vec![dummy![x, x, x, x, x]];
|
||||||
let y = vec![dummy![[x, x, x, x, x]]];
|
let y = vec![dummy![[x, x, x, x, x]]];
|
||||||
//~^ ERROR: allocating a local array larger than 512000 bytes
|
|
||||||
let y = dummy![x, x, x, x, x];
|
let y = dummy![x, x, x, x, x];
|
||||||
let y = [x, x, dummy!(x), x, x];
|
let y = [x, x, dummy!(x), x, x];
|
||||||
//~^ ERROR: allocating a local array larger than 512000 bytes
|
//~^ ERROR: allocating a local array larger than 512000 bytes
|
||||||
@ -88,4 +97,9 @@ macro_rules! dummy {
|
|||||||
let y = dummy!(vec![dummy![x, x, x, x, x]]);
|
let y = dummy!(vec![dummy![x, x, x, x, x]]);
|
||||||
let y = dummy![[x, x, x, x, x]];
|
let y = dummy![[x, x, x, x, x]];
|
||||||
//~^ ERROR: allocating a local array larger than 512000 bytes
|
//~^ ERROR: allocating a local array larger than 512000 bytes
|
||||||
|
|
||||||
|
let y = proc_macros::make_it_big!([x; 1]);
|
||||||
|
//~^ ERROR: allocating a local array larger than 512000 bytes
|
||||||
|
let y = vec![proc_macros::make_it_big!([x; 10])];
|
||||||
|
let y = vec![create_then_move![x; 5]; 5];
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:29:14
|
--> tests/ui/large_stack_arrays.rs:32:14
|
||||||
|
|
|
|
||||||
LL | let _x = [build(); 3];
|
LL | let _x = [build(); 3];
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -9,7 +9,7 @@ LL | let _x = [build(); 3];
|
|||||||
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
|
= help: to override `-D warnings` add `#[allow(clippy::large_stack_arrays)]`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:32:14
|
--> tests/ui/large_stack_arrays.rs:35:14
|
||||||
|
|
|
|
||||||
LL | let _y = [build(), build(), build()];
|
LL | let _y = [build(), build(), build()];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -17,7 +17,7 @@ LL | let _y = [build(), build(), build()];
|
|||||||
= help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:38:9
|
--> tests/ui/large_stack_arrays.rs:41:9
|
||||||
|
|
|
|
||||||
LL | [0u32; 20_000_000],
|
LL | [0u32; 20_000_000],
|
||||||
| ^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
@ -25,7 +25,7 @@ LL | [0u32; 20_000_000],
|
|||||||
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:40:9
|
--> tests/ui/large_stack_arrays.rs:43:9
|
||||||
|
|
|
|
||||||
LL | [S { data: [0; 32] }; 5000],
|
LL | [S { data: [0; 32] }; 5000],
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -33,7 +33,7 @@ LL | [S { data: [0; 32] }; 5000],
|
|||||||
= help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()`
|
= 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
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:42:9
|
--> tests/ui/large_stack_arrays.rs:45:9
|
||||||
|
|
|
|
||||||
LL | [Some(""); 20_000_000],
|
LL | [Some(""); 20_000_000],
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -41,7 +41,7 @@ LL | [Some(""); 20_000_000],
|
|||||||
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:44:9
|
--> tests/ui/large_stack_arrays.rs:47:9
|
||||||
|
|
|
|
||||||
LL | [E::T(0); 5000],
|
LL | [E::T(0); 5000],
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
@ -49,7 +49,7 @@ LL | [E::T(0); 5000],
|
|||||||
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:46:9
|
--> tests/ui/large_stack_arrays.rs:49:9
|
||||||
|
|
|
|
||||||
LL | [0u8; usize::MAX],
|
LL | [0u8; usize::MAX],
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
@ -57,15 +57,7 @@ LL | [0u8; usize::MAX],
|
|||||||
= help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:81:25
|
--> tests/ui/large_stack_arrays.rs:93:13
|
||||||
|
|
|
||||||
LL | let y = vec![dummy![[x, x, x, x, x]]];
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()`
|
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
|
||||||
--> tests/ui/large_stack_arrays.rs:84:13
|
|
||||||
|
|
|
|
||||||
LL | let y = [x, x, dummy!(x), x, x];
|
LL | let y = [x, x, dummy!(x), x, x];
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -73,7 +65,7 @@ LL | let y = [x, x, dummy!(x), x, x];
|
|||||||
= help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![x, x, dummy!(x), x, x].into_boxed_slice()`
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:67:13
|
--> tests/ui/large_stack_arrays.rs:70:13
|
||||||
|
|
|
|
||||||
LL | [$a, $b, $a, $b]
|
LL | [$a, $b, $a, $b]
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
@ -84,12 +76,29 @@ LL | let y = dummy![x => x];
|
|||||||
= note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the macro `dummy` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: allocating a local array larger than 512000 bytes
|
error: allocating a local array larger than 512000 bytes
|
||||||
--> tests/ui/large_stack_arrays.rs:89:20
|
--> tests/ui/large_stack_arrays.rs:98:20
|
||||||
|
|
|
|
||||||
LL | let y = dummy![[x, x, x, x, x]];
|
LL | let y = dummy![[x, x, x, x, x]];
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()`
|
= help: consider allocating on the heap with `vec![x, x, x, x, x].into_boxed_slice()`
|
||||||
|
|
||||||
error: aborting due to 11 previous errors
|
error: allocating a local array larger than 512000 bytes
|
||||||
|
--> tests/ui/large_stack_arrays.rs:101:39
|
||||||
|
|
|
||||||
|
LL | let y = proc_macros::make_it_big!([x; 1]);
|
||||||
|
| ^^^^^^
|
||||||
|
|
||||||
|
error: allocating a local array larger than 512000 bytes
|
||||||
|
--> tests/ui/large_stack_arrays.rs:82:23
|
||||||
|
|
|
||||||
|
LL | let _x_ = [$id; $n];
|
||||||
|
| ^^^^^^^^^
|
||||||
|
...
|
||||||
|
LL | let y = vec![create_then_move![x; 5]; 5];
|
||||||
|
| ----------------------- in this macro invocation
|
||||||
|
|
|
||||||
|
= note: this error originates in the macro `create_then_move` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user