Auto merge of #12624 - J-ZhengLi:issue12586, r=xFrednet
fix [`large_stack_arrays`] linting in `vec` macro fixes: #12586 this PR also adds a wrapper function `matching_root_macro_call` to `clippy_utils::macros`, considering how often that same pattern appears in the codebase. (I'm always very indecisive towards naming, so, if anyone have better idea of how that function should be named, feel free to suggest it) --- changelog: fix [`large_stack_arrays`] linting in `vec` macro; add `matching_root_macro_call` to clippy_utils
This commit is contained in:
commit
c6bf9548d5
11 changed files with 194 additions and 48 deletions
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
|||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{
|
||||
find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
|
||||
is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
|
||||
is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
|
||||
};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
|
|
@ -271,9 +271,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
|
|||
let mut suggest_format = |spec| {
|
||||
let message = format!("for the {spec} to apply consider using `format!()`");
|
||||
|
||||
if let Some(mac_call) = root_macro_call(arg_span)
|
||||
&& self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
|
||||
{
|
||||
if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
|
||||
diag.span_suggestion(
|
||||
self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
|
||||
message,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
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_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::{self, ConstKind};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -25,20 +28,41 @@ declare_clippy_lint! {
|
|||
|
||||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u128,
|
||||
prev_vec_macro_callsite: Option<Span>,
|
||||
}
|
||||
|
||||
impl LargeStackArrays {
|
||||
#[must_use]
|
||||
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<'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
|
||||
&& !self.is_from_vec_macro(cx, expr.span)
|
||||
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
||||
|
|
@ -54,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
|||
})
|
||||
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
|
||||
{
|
||||
span_lint_and_help(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LARGE_STACK_ARRAYS,
|
||||
expr.span,
|
||||
|
|
@ -62,12 +86,33 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
|||
"allocating a local array larger than {} bytes",
|
||||
self.maximum_allowed_size
|
||||
),
|
||||
None,
|
||||
format!(
|
||||
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
||||
snippet(cx, expr.span, "[...]")
|
||||
),
|
||||
|diag| {
|
||||
if !might_be_expanded(cx, expr) {
|
||||
diag.help(format!(
|
||||
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
||||
snippet(cx, expr.span, "[...]")
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only giving help messages if the expr does not contains macro expanded codes.
|
||||
fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
|
||||
/// 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
use crate::rustc_lint::LintContext;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call};
|
||||
use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
|||
&& !expr.span.from_expansion()
|
||||
&& let then = peel_blocks_with_stmt(then)
|
||||
&& let Some(macro_call) = root_macro_call(then.span)
|
||||
&& cx.tcx.item_name(macro_call.def_id) == sym::panic
|
||||
&& is_panic(cx, macro_call.def_id)
|
||||
&& !cx.tcx.sess.source_map().is_multiline(cond.span)
|
||||
&& let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span)
|
||||
&& let Some(panic_snippet) = panic_snippet.strip_suffix(')')
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_config::msrvs::{self, Msrv};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::root_macro_call;
|
||||
use clippy_utils::macros::matching_root_macro_call;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{higher, in_constant};
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
|
|
@ -9,7 +9,6 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -97,9 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(macro_call) = root_macro_call(expr.span)
|
||||
&& is_matches_macro(cx, macro_call.def_id)
|
||||
{
|
||||
if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
|
||||
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
||||
let range = check_pat(&arm.pat.kind);
|
||||
check_is_ascii(cx, macro_call.span, recv, &range);
|
||||
|
|
@ -187,11 +184,3 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
|
|||
CharRange::Otherwise
|
||||
}
|
||||
}
|
||||
|
||||
fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
|
||||
if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
|
||||
return sym::matches_macro == name;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call};
|
||||
use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call};
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
|
||||
|
|
@ -247,8 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
} else if let Some(macro_call) = root_macro_call(expr.span)
|
||||
&& cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
|
||||
} else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
|
||||
// we know for a fact that the wildcard pattern is the second arm
|
||||
&& let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
|
||||
&& path_to_local_id(scrutinee, filter_param_id)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::macros::root_macro_call;
|
||||
use clippy_utils::macros::matching_root_macro_call;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -65,8 +65,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s
|
|||
|
||||
/// Checks `vec![Vec::with_capacity(x); n]`
|
||||
fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let Some(mac_call) = root_macro_call(expr.span)
|
||||
&& cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id)
|
||||
if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
|
||||
&& let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
|
||||
&& fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
|
||||
&& !len_expr.span.from_expansion()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call;
|
||||
use clippy_utils::macros::matching_root_macro_call;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
|
||||
|
|
@ -145,9 +145,7 @@ impl SlowVectorInit {
|
|||
// Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro.
|
||||
// This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an
|
||||
// empty vec
|
||||
if expr.span.from_expansion()
|
||||
&& root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro)
|
||||
{
|
||||
if expr.span.from_expansion() && matching_root_macro_call(cx, expr.span, sym::vec_macro).is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue