From 8ce8c42e0bf66d32504659cbbe91ad2a0c315891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 22 Jun 2024 04:32:49 +0000 Subject: [PATCH] Do not underline suggestions for code that is already there When a suggestion part is for already present code, do not highlight it. If after that there are no highlights left, do not show the suggestion at all. Fix clippy lint suggestion incorrectly treated as `span_help`. --- compiler/rustc_errors/src/emitter.rs | 21 +++++++++- compiler/rustc_errors/src/lib.rs | 22 ++++++++--- .../clippy_lints/src/unnecessary_wraps.rs | 4 +- .../ui/unnecessary_literal_unwrap.stderr | 4 +- .../clippy/tests/ui/unnecessary_wraps.stderr | 8 ++-- .../generic_const_exprs/issue-105608.stderr | 4 -- tests/ui/imports/issue-55884-2.stderr | 4 -- tests/ui/lint/wide_pointer_comparisons.stderr | 38 +++++++++---------- tests/ui/privacy/issue-75907.stderr | 2 +- tests/ui/privacy/privacy5.stderr | 20 +++++----- ...ue-42234-unknown-receiver-type.full.stderr | 4 -- ...-turbofish-surrounding-angle-braket.stderr | 2 +- 12 files changed, 75 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 73908e5808569..8963b009c31ec 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1767,7 +1767,10 @@ impl HumanEmitter { debug!(?suggestions); if suggestions.is_empty() { - // Suggestions coming from macros can have malformed spans. This is a heavy handed + // Here we check if there are suggestions that have actual code changes. We sometimes + // suggest the same code that is already there, instead of changing how we produce the + // suggestions and filtering there, we just don't emit the suggestion. + // Suggestions coming from macros can also have malformed spans. This is a heavy handed // approach to avoid ICEs by ignoring the suggestion outright. return Ok(()); } @@ -2046,7 +2049,9 @@ impl HumanEmitter { assert!(underline_start >= 0 && underline_end >= 0); let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { - if let DisplaySuggestion::Underline = show_code_change { + if let DisplaySuggestion::Underline = show_code_change + && is_different(sm, &part.snippet, part.span) + { // If this is a replacement, underline with `~`, if this is an addition // underline with `+`. buffer.putc( @@ -2824,6 +2829,18 @@ impl Style { } } +/// Whether the original and suggested code are the same. +pub fn is_different(sm: &SourceMap, suggested: &str, sp: Span) -> bool { + let found = match sm.span_to_snippet(sp) { + Ok(snippet) => snippet, + Err(e) => { + warn!(error = ?e, "Invalid span {:?}", sp); + return true; + } + }; + found != suggested +} + /// Whether the original and suggested code are visually similar enough to warrant extra wording. pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode. diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 09855394cdb2b..ceebcd46a6f7f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -50,7 +50,7 @@ pub use diagnostic_impls::{ IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; -use emitter::{is_case_difference, DynEmitter, Emitter}; +use emitter::{is_case_difference, is_different, DynEmitter, Emitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{Hash128, StableHasher}; @@ -357,10 +357,16 @@ impl CodeSuggestion { _ => 1, }) .sum(); - line_highlight.push(SubstitutionHighlight { - start: (cur_lo.col.0 as isize + acc) as usize, - end: (cur_lo.col.0 as isize + acc + len) as usize, - }); + if !is_different(sm, &part.snippet, part.span) { + // Account for cases where we are suggesting the same code that's already + // there. This shouldn't happen often, but in some cases for multipart + // suggestions it's much easier to handle it here than in the origin. + } else { + line_highlight.push(SubstitutionHighlight { + start: (cur_lo.col.0 as isize + acc) as usize, + end: (cur_lo.col.0 as isize + acc + len) as usize, + }); + } buf.push_str(&part.snippet); let cur_hi = sm.lookup_char_pos(part.span.hi()); // Account for the difference between the width of the current code and the @@ -392,7 +398,11 @@ impl CodeSuggestion { while buf.ends_with('\n') { buf.pop(); } - Some((buf, substitution.parts, highlights, only_capitalization)) + if highlights.iter().all(|parts| parts.is_empty()) { + None + } else { + Some((buf, substitution.parts, highlights, only_capitalization)) + } }) .collect() } diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index e4e7f7d06e706..080efe983c2a8 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -145,7 +145,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ( "this function's return value is unnecessary".to_string(), "remove the return type...".to_string(), - snippet(cx, fn_decl.output.span(), "..").to_string(), + // FIXME: we should instead get the span including the `->` and suggest an + // empty string for this case. + "()".to_string(), "...and then remove returned values", ) } else { diff --git a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr index 15708090361ed..37ee9195fce66 100644 --- a/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_literal_unwrap.stderr @@ -63,7 +63,7 @@ LL | let _val = None::<()>.expect("this always happens"); help: remove the `None` and `expect()` | LL | let _val = panic!("this always happens"); - | ~~~~~~~ ~ + | ~~~~~~~ error: used `unwrap_or_default()` on `None` value --> tests/ui/unnecessary_literal_unwrap.rs:22:24 @@ -134,7 +134,7 @@ LL | None::<()>.expect("this always happens"); help: remove the `None` and `expect()` | LL | panic!("this always happens"); - | ~~~~~~~ ~ + | ~~~~~~~ error: used `unwrap_or_default()` on `None` value --> tests/ui/unnecessary_literal_unwrap.rs:30:5 diff --git a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr index a55a23d449f57..59986d895b30e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_wraps.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_wraps.stderr @@ -118,8 +118,8 @@ LL | | } | help: remove the return type... | -LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { - | ~~~~~~~~~~ +LL | fn issue_6640_1(a: bool, b: bool) -> () { + | ~~ help: ...and then remove returned values | LL ~ return ; @@ -145,8 +145,8 @@ LL | | } | help: remove the return type... | -LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { - | ~~~~~~~~~~~~~~~ +LL | fn issue_6640_2(a: bool, b: bool) -> () { + | ~~ help: ...and then remove returned values | LL ~ return ; diff --git a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr index 09b618fb3f050..1c97eaddfe1bb 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr @@ -4,10 +4,6 @@ error[E0282]: type annotations needed LL | Combination::<0>.and::<_>().and::<_>(); | ^^^ cannot infer type of the type parameter `M` declared on the method `and` | -help: consider specifying the generic argument - | -LL | Combination::<0>.and::<_>().and::<_>(); - | ~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/imports/issue-55884-2.stderr b/tests/ui/imports/issue-55884-2.stderr index 8a9d5f2a6d8a6..0d4f01aeafc64 100644 --- a/tests/ui/imports/issue-55884-2.stderr +++ b/tests/ui/imports/issue-55884-2.stderr @@ -24,10 +24,6 @@ note: ...and refers to the struct `ParseOptions` which is defined here | LL | pub struct ParseOptions {} | ^^^^^^^^^^^^^^^^^^^^^^^ you could import this directly -help: import `ParseOptions` through the re-export - | -LL | pub use parser::ParseOptions; - | ~~~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/lint/wide_pointer_comparisons.stderr b/tests/ui/lint/wide_pointer_comparisons.stderr index 81a221c0ee640..7fe382393d7e1 100644 --- a/tests/ui/lint/wide_pointer_comparisons.stderr +++ b/tests/ui/lint/wide_pointer_comparisons.stderr @@ -74,7 +74,7 @@ LL | let _ = PartialEq::eq(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~~~~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:35:13 @@ -85,7 +85,7 @@ LL | let _ = PartialEq::ne(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~~~~~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:37:13 @@ -96,7 +96,7 @@ LL | let _ = a.eq(&b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ ~ + | ++++++++++++++++++ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:39:13 @@ -107,7 +107,7 @@ LL | let _ = a.ne(&b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ ~ + | +++++++++++++++++++ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:41:13 @@ -283,7 +283,7 @@ LL | let _ = PartialEq::eq(a, b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~ ~~~ ~ + | ~~~~~~~~~~~~~~~~~~~ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:85:17 @@ -294,7 +294,7 @@ LL | let _ = PartialEq::ne(a, b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~~ ~~~ ~ + | ~~~~~~~~~~~~~~~~~~~~ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:87:17 @@ -305,7 +305,7 @@ LL | let _ = PartialEq::eq(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~ ~~~ ~ + | ~~~~~~~~~~~~~~~~~~~ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:89:17 @@ -316,7 +316,7 @@ LL | let _ = PartialEq::ne(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(*a, *b); - | ~~~~~~~~~~~~~~~~~~~~ ~~~ ~ + | ~~~~~~~~~~~~~~~~~~~~ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:91:17 @@ -327,7 +327,7 @@ LL | let _ = a.eq(b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(*a, *b); - | +++++++++++++++++++ ~~~ ~ + | +++++++++++++++++++ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:93:17 @@ -338,7 +338,7 @@ LL | let _ = a.ne(b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(*a, *b); - | ++++++++++++++++++++ ~~~ ~ + | ++++++++++++++++++++ ~~~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:95:17 @@ -519,11 +519,11 @@ LL | let _ = PartialEq::eq(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~~~~~~ ~ help: use explicit `std::ptr::eq` method to compare metadata and addresses | LL | let _ = std::ptr::eq(a, b); - | ~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:133:17 @@ -534,11 +534,11 @@ LL | let _ = PartialEq::ne(&a, &b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(a, b); - | ~~~~~~~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~~~~~~~ ~ help: use explicit `std::ptr::eq` method to compare metadata and addresses | LL | let _ = !std::ptr::eq(a, b); - | ~~~~~~~~~~~~~~ ~ ~ + | ~~~~~~~~~~~~~~ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:135:17 @@ -549,11 +549,11 @@ LL | let _ = a.eq(&b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = std::ptr::addr_eq(a, b); - | ++++++++++++++++++ ~ ~ + | ++++++++++++++++++ ~ help: use explicit `std::ptr::eq` method to compare metadata and addresses | LL | let _ = std::ptr::eq(a, b); - | +++++++++++++ ~ ~ + | +++++++++++++ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:137:17 @@ -564,11 +564,11 @@ LL | let _ = a.ne(&b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | let _ = !std::ptr::addr_eq(a, b); - | +++++++++++++++++++ ~ ~ + | +++++++++++++++++++ ~ help: use explicit `std::ptr::eq` method to compare metadata and addresses | LL | let _ = !std::ptr::eq(a, b); - | ++++++++++++++ ~ ~ + | ++++++++++++++ ~ warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:142:9 @@ -594,7 +594,7 @@ LL | cmp!(a, b); help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses | LL | cmp!(std::ptr::addr_eq(a, b)); - | ++++++++++++++++++ ~ + + | ++++++++++++++++++ + warning: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected --> $DIR/wide_pointer_comparisons.rs:159:39 diff --git a/tests/ui/privacy/issue-75907.stderr b/tests/ui/privacy/issue-75907.stderr index f7cb874c2cc0d..3121cc04478a4 100644 --- a/tests/ui/privacy/issue-75907.stderr +++ b/tests/ui/privacy/issue-75907.stderr @@ -14,7 +14,7 @@ LL | let Bar(x, y, Foo(z)) = make_bar(); help: consider making the fields publicly accessible | LL | pub(crate) struct Bar(pub u8, pub u8, pub Foo); - | ~~~ ~~~ +++ + | ~~~ +++ error[E0532]: cannot match against a tuple struct which contains private fields --> $DIR/issue-75907.rs:15:19 diff --git a/tests/ui/privacy/privacy5.stderr b/tests/ui/privacy/privacy5.stderr index 615b0af2762d3..ec3abe9b81629 100644 --- a/tests/ui/privacy/privacy5.stderr +++ b/tests/ui/privacy/privacy5.stderr @@ -53,7 +53,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:56:12 @@ -262,7 +262,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:69:12 @@ -281,7 +281,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:70:12 @@ -300,7 +300,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:71:12 @@ -319,7 +319,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:72:18 @@ -338,7 +338,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:73:18 @@ -357,7 +357,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:74:18 @@ -376,7 +376,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `C` is private --> $DIR/privacy5.rs:75:18 @@ -395,7 +395,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:83:17 @@ -452,7 +452,7 @@ LL | pub struct C(pub isize, isize); help: consider making the fields publicly accessible | LL | pub struct C(pub isize, pub isize); - | ~~~ +++ + | +++ error[E0603]: tuple struct constructor `A` is private --> $DIR/privacy5.rs:90:20 diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr index e01e1edab5aa6..6559845c23ec5 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.full.stderr @@ -17,10 +17,6 @@ error[E0282]: type annotations needed LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` | -help: consider specifying the generic argument - | -LL | .sum::<_>() - | ~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr b/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr index 618ccba0d3d12..dde6060c4334b 100644 --- a/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr +++ b/tests/ui/suggestions/recover-missing-turbofish-surrounding-angle-braket.stderr @@ -40,7 +40,7 @@ LL | let _ = vec![1, 2, 3].into_iter().collect::Vec<_>>(); help: surround the type parameters with angle brackets | LL | let _ = vec![1, 2, 3].into_iter().collect::>(); - | + ~ + | + error: aborting due to 4 previous errors