|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | 2 | use clippy_utils::higher::VecArgs; |
3 | 3 | use clippy_utils::last_path_segment; |
4 | | -use clippy_utils::macros::{root_macro_call_first_node, MacroCall}; |
| 4 | +use clippy_utils::macros::root_macro_call_first_node; |
| 5 | +use clippy_utils::source::{indent_of, snippet}; |
| 6 | +use rustc_errors::Applicability; |
5 | 7 | use rustc_hir::{Expr, ExprKind, QPath, TyKind}; |
6 | 8 | use rustc_lint::{LateContext, LateLintPass}; |
7 | 9 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
8 | | -use rustc_span::{sym, Symbol}; |
| 10 | +use rustc_span::{sym, Span, Symbol}; |
9 | 11 |
|
10 | 12 | declare_clippy_lint! { |
11 | 13 | /// ### What it does |
@@ -47,27 +49,76 @@ declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); |
47 | 49 | impl LateLintPass<'_> for RcCloneInVecInit { |
48 | 50 | fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { |
49 | 51 | let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; |
50 | | - let Some(VecArgs::Repeat(elem, _)) = VecArgs::hir(cx, expr) else { return; }; |
| 52 | + let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; |
51 | 53 | let Some(symbol) = new_reference_call(cx, elem) else { return; }; |
52 | 54 |
|
53 | | - emit_lint(cx, symbol, ¯o_call); |
| 55 | + emit_lint(cx, symbol, macro_call.span, elem, len); |
54 | 56 | } |
55 | 57 | } |
56 | 58 |
|
57 | | -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, macro_call: &MacroCall) { |
| 59 | +fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { |
| 60 | + let elem_snippet = snippet(cx, elem.span, "..").to_string(); |
| 61 | + if elem_snippet.contains('\n') { |
| 62 | + // This string must be found in `elem_snippet`, otherwise we won't be constructing |
| 63 | + // the snippet in the first place. |
| 64 | + let reference_creation = format!("{symbol_name}::new"); |
| 65 | + let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); |
| 66 | + |
| 67 | + return format!("{code_until_reference_creation}{reference_creation}(..)"); |
| 68 | + } |
| 69 | + |
| 70 | + elem_snippet |
| 71 | +} |
| 72 | + |
| 73 | +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { |
| 74 | + format!( |
| 75 | + r#"{{ |
| 76 | +{indent} let mut v = Vec::with_capacity({len}); |
| 77 | +{indent} (0..{len}).for_each(|_| v.push({elem})); |
| 78 | +{indent} v |
| 79 | +{indent}}}"# |
| 80 | + ) |
| 81 | +} |
| 82 | + |
| 83 | +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { |
| 84 | + format!( |
| 85 | + "{{ |
| 86 | +{indent} let data = {elem}; |
| 87 | +{indent} vec![data; {len}] |
| 88 | +{indent}}}" |
| 89 | + ) |
| 90 | +} |
| 91 | + |
| 92 | +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { |
58 | 93 | let symbol_name = symbol.as_str(); |
59 | 94 |
|
60 | 95 | span_lint_and_then( |
61 | 96 | cx, |
62 | 97 | RC_CLONE_IN_VEC_INIT, |
63 | | - macro_call.span, |
| 98 | + lint_span, |
64 | 99 | &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), |
65 | 100 | |diag| { |
| 101 | + let len_snippet = snippet(cx, len.span, ".."); |
| 102 | + let elem_snippet = elem_snippet(cx, elem, symbol_name); |
| 103 | + let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); |
| 104 | + let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); |
| 105 | + let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); |
| 106 | + |
66 | 107 | diag.note(format!("each element will point to the same `{symbol_name}` instance")); |
67 | | - diag.help(format!( |
68 | | - "if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" |
69 | | - )); |
70 | | - diag.help("or if not, initialize each element individually"); |
| 108 | + diag.span_suggestion( |
| 109 | + lint_span, |
| 110 | + format!("consider initializing each `{symbol_name}` element individually"), |
| 111 | + loop_init_suggestion, |
| 112 | + Applicability::Unspecified, |
| 113 | + ); |
| 114 | + diag.span_suggestion( |
| 115 | + lint_span, |
| 116 | + format!( |
| 117 | + "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" |
| 118 | + ), |
| 119 | + extract_suggestion, |
| 120 | + Applicability::Unspecified, |
| 121 | + ); |
71 | 122 | }, |
72 | 123 | ); |
73 | 124 | } |
|
0 commit comments