@@ -5,7 +5,7 @@ use clippy_utils::comparisons::{Rel, normalize_comparison};
55use clippy_utils:: diagnostics:: span_lint_and_then;
66use clippy_utils:: higher:: { If , Range } ;
77use clippy_utils:: macros:: { find_assert_eq_args, first_node_macro_backtrace, root_macro_call} ;
8- use clippy_utils:: source:: snippet;
8+ use clippy_utils:: source:: { snippet, snippet_with_applicability } ;
99use clippy_utils:: visitors:: for_each_expr_without_closures;
1010use clippy_utils:: { eq_expr_value, hash_expr} ;
1111use rustc_ast:: { BinOpKind , LitKind , RangeLimits } ;
@@ -67,16 +67,13 @@ declare_clippy_lint! {
6767}
6868declare_lint_pass ! ( MissingAssertsForIndexing => [ MISSING_ASSERTS_FOR_INDEXING ] ) ;
6969
70- fn report_lint < F > ( cx : & LateContext < ' _ > , full_span : Span , msg : & ' static str , indexes : & [ Span ] , f : F )
70+ fn report_lint < F > ( cx : & LateContext < ' _ > , index_spans : Vec < Span > , msg : & ' static str , f : F )
7171where
7272 F : FnOnce ( & mut Diag < ' _ , ( ) > ) ,
7373{
74- span_lint_and_then ( cx, MISSING_ASSERTS_FOR_INDEXING , full_span , msg, |diag| {
74+ span_lint_and_then ( cx, MISSING_ASSERTS_FOR_INDEXING , index_spans , msg, |diag| {
7575 f ( diag) ;
76- for span in indexes {
77- diag. span_note ( * span, "slice indexed here" ) ;
78- }
79- diag. note ( "asserting the length before indexing will elide bounds checks" ) ;
76+ diag. note_once ( "asserting the length before indexing will elide bounds checks" ) ;
8077 } ) ;
8178}
8279
@@ -213,15 +210,6 @@ impl<'hir> IndexEntry<'hir> {
213210 | IndexEntry :: IndexWithoutAssert { slice, .. } => slice,
214211 }
215212 }
216-
217- pub fn index_spans ( & self ) -> Option < & [ Span ] > {
218- match self {
219- IndexEntry :: StrayAssert { .. } => None ,
220- IndexEntry :: AssertWithIndex { indexes, .. } | IndexEntry :: IndexWithoutAssert { indexes, .. } => {
221- Some ( indexes)
222- } ,
223- }
224- }
225213}
226214
227215/// Extracts the upper index of a slice indexing expression.
@@ -354,86 +342,69 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
354342/// Inspects indexes and reports lints.
355343///
356344/// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
357- fn report_indexes ( cx : & LateContext < ' _ > , map : & UnindexMap < u64 , Vec < IndexEntry < ' _ > > > ) {
358- for bucket in map. values ( ) {
345+ fn report_indexes ( cx : & LateContext < ' _ > , map : UnindexMap < u64 , Vec < IndexEntry < ' _ > > > ) {
346+ for bucket in map. into_values ( ) {
359347 for entry in bucket {
360- let Some ( full_span) = entry
361- . index_spans ( )
362- . and_then ( |spans| spans. first ( ) . zip ( spans. last ( ) ) )
363- . map ( |( low, & high) | low. to ( high) )
364- else {
365- continue ;
366- } ;
367-
368- match * entry {
348+ match entry {
369349 IndexEntry :: AssertWithIndex {
370350 highest_index,
371351 is_first_highest,
372352 asserted_len,
373- ref indexes,
353+ indexes,
374354 comparison,
375355 assert_span,
376356 slice,
377357 macro_call,
378358 } if indexes. len ( ) > 1 && !is_first_highest => {
359+ let mut app = Applicability :: MachineApplicable ;
360+ let slice_str = snippet_with_applicability ( cx, slice. span , "_" , & mut app) ;
379361 // if we have found an `assert!`, let's also check that it's actually right
380362 // and if it covers the highest index and if not, suggest the correct length
381363 let sugg = match comparison {
382364 // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks.
383365 // The user probably meant `v.len() > 5`
384- LengthComparison :: LengthLessThanInt | LengthComparison :: LengthLessThanOrEqualInt => Some (
385- format ! ( "assert!({}.len() > {highest_index})" , snippet ( cx , slice . span , ".." ) ) ,
386- ) ,
366+ LengthComparison :: LengthLessThanInt | LengthComparison :: LengthLessThanOrEqualInt => {
367+ Some ( format ! ( "assert!({slice_str }.len() > {highest_index})" , ) )
368+ } ,
387369 // `5 < v.len()` == `v.len() > 5`
388- LengthComparison :: IntLessThanLength if asserted_len < highest_index => Some ( format ! (
389- "assert!({}.len() > {highest_index})" ,
390- snippet( cx, slice. span, ".." )
391- ) ) ,
370+ LengthComparison :: IntLessThanLength if asserted_len < highest_index => {
371+ Some ( format ! ( "assert!({slice_str}.len() > {highest_index})" , ) )
372+ } ,
392373 // `5 <= v.len() == `v.len() >= 5`
393- LengthComparison :: IntLessThanOrEqualLength if asserted_len <= highest_index => Some ( format ! (
394- "assert!({}.len() > {highest_index})" ,
395- snippet( cx, slice. span, ".." )
396- ) ) ,
374+ LengthComparison :: IntLessThanOrEqualLength if asserted_len <= highest_index => {
375+ Some ( format ! ( "assert!({slice_str}.len() > {highest_index})" , ) )
376+ } ,
397377 // `highest_index` here is rather a length, so we need to add 1 to it
398378 LengthComparison :: LengthEqualInt if asserted_len < highest_index + 1 => match macro_call {
399- sym:: assert_eq_macro => Some ( format ! (
400- "assert_eq!({}.len(), {})" ,
401- snippet( cx, slice. span, ".." ) ,
402- highest_index + 1
403- ) ) ,
404- sym:: debug_assert_eq_macro => Some ( format ! (
405- "debug_assert_eq!({}.len(), {})" ,
406- snippet( cx, slice. span, ".." ) ,
407- highest_index + 1
408- ) ) ,
409- _ => Some ( format ! (
410- "assert!({}.len() == {})" ,
411- snippet( cx, slice. span, ".." ) ,
412- highest_index + 1
413- ) ) ,
379+ sym:: assert_eq_macro => {
380+ Some ( format ! ( "assert_eq!({slice_str}.len(), {})" , highest_index + 1 ) )
381+ } ,
382+ sym:: debug_assert_eq_macro => {
383+ Some ( format ! ( "debug_assert_eq!({slice_str}.len(), {})" , highest_index + 1 ) )
384+ } ,
385+ _ => Some ( format ! ( "assert!({slice_str}.len() == {})" , highest_index + 1 ) ) ,
414386 } ,
415387 _ => None ,
416388 } ;
417389
418390 if let Some ( sugg) = sugg {
419391 report_lint (
420392 cx,
421- full_span,
422- "indexing into a slice multiple times with an `assert` that does not cover the highest index" ,
423393 indexes,
394+ "indexing into a slice multiple times with an `assert` that does not cover the highest index" ,
424395 |diag| {
425- diag. span_suggestion (
396+ diag. span_suggestion_verbose (
426397 assert_span,
427398 "provide the highest index that is indexed with" ,
428399 sugg,
429- Applicability :: MachineApplicable ,
400+ app ,
430401 ) ;
431402 } ,
432403 ) ;
433404 }
434405 } ,
435406 IndexEntry :: IndexWithoutAssert {
436- ref indexes,
407+ indexes,
437408 highest_index,
438409 is_first_highest,
439410 slice,
@@ -442,9 +413,8 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
442413 // adding an `assert!` that covers the highest index
443414 report_lint (
444415 cx,
445- full_span,
446- "indexing into a slice multiple times without an `assert`" ,
447416 indexes,
417+ "indexing into a slice multiple times without an `assert`" ,
448418 |diag| {
449419 diag. help ( format ! (
450420 "consider asserting the length before indexing: `assert!({}.len() > {highest_index});`" ,
@@ -469,6 +439,6 @@ impl LateLintPass<'_> for MissingAssertsForIndexing {
469439 ControlFlow :: < !, ( ) > :: Continue ( ( ) )
470440 } ) ;
471441
472- report_indexes ( cx, & map) ;
442+ report_indexes ( cx, map) ;
473443 }
474444}
0 commit comments