diff options
| author | bors <bors@rust-lang.org> | 2023-01-28 10:00:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-01-28 10:00:56 +0000 |
| commit | d8da51366817317d19be9bd138943eafb30bc987 (patch) | |
| tree | 23ff9c0cf43972239454598d7d21506a641ba43b /compiler | |
| parent | 226b2496fc3ef997fa1cccf84259539203f82375 (diff) | |
| parent | 228ddf04fca01e8224ad4fb8a8e9db8483642249 (diff) | |
| download | rust-d8da51366817317d19be9bd138943eafb30bc987.tar.gz rust-d8da51366817317d19be9bd138943eafb30bc987.zip | |
Auto merge of #106916 - lukas-code:overlapping-substs, r=estebank
Remove overlapping parts of multipart suggestions This PR adds a debug assertion that the parts of a single substitution cannot overlap, fixes a overlapping substitution from the testsuite, and fixes https://github.com/rust-lang/rust/issues/106870. Note that a single suggestion can still have multiple overlapping substitutions / possible edits, we just don't suggest overlapping replacements in a single edit anymore. I've also included a fix for an unrelated bug where rustfix for `explicit_outlives_requirements` would produce multiple trailing commas for a where clause.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 71 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 24 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 37 |
4 files changed, 93 insertions, 40 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 51b2ff6a003..4ad24c1400d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -629,19 +629,27 @@ impl Diagnostic { applicability: Applicability, style: SuggestionStyle, ) -> &mut Self { - assert!(!suggestion.is_empty()); - debug_assert!( - !(suggestion.iter().any(|(sp, text)| sp.is_empty() && text.is_empty())), - "Span must not be empty and have no suggestion" + let mut parts = suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect::<Vec<_>>(); + + parts.sort_unstable_by_key(|part| part.span); + + assert!(!parts.is_empty()); + debug_assert_eq!( + parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()), + None, + "Span must not be empty and have no suggestion", + ); + debug_assert_eq!( + parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), + None, + "suggestion must not have overlapping parts", ); self.push_suggestion(CodeSuggestion { - substitutions: vec![Substitution { - parts: suggestion - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], + substitutions: vec![Substitution { parts }], msg: self.subdiagnostic_message_to_diagnostic_message(msg), style, applicability, @@ -802,25 +810,34 @@ impl Diagnostic { suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self { - let suggestions: Vec<_> = suggestions.into_iter().collect(); - debug_assert!( - !(suggestions - .iter() - .flatten() - .any(|(sp, suggestion)| sp.is_empty() && suggestion.is_empty())), - "Span must not be empty and have no suggestion" - ); + let substitutions = suggestions + .into_iter() + .map(|sugg| { + let mut parts = sugg + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect::<Vec<_>>(); + + parts.sort_unstable_by_key(|part| part.span); + + assert!(!parts.is_empty()); + debug_assert_eq!( + parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()), + None, + "Span must not be empty and have no suggestion", + ); + debug_assert_eq!( + parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), + None, + "suggestion must not have overlapping parts", + ); + + Substitution { parts } + }) + .collect(); self.push_suggestion(CodeSuggestion { - substitutions: suggestions - .into_iter() - .map(|sugg| Substitution { - parts: sugg - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }) - .collect(), + substitutions, msg: self.subdiagnostic_message_to_diagnostic_message(msg), style: SuggestionStyle::ShowCode, applicability, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 535812fb0e2..d076fc08b0e 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -3,6 +3,7 @@ //! This module contains the code for creating and emitting diagnostics. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_windows)] #![feature(drain_filter)] #![feature(if_let_guard)] #![feature(is_terminal)] diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 4ebd75f0185..cd431f57019 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -10,7 +10,7 @@ use crate::mbe::transcribe::transcribe; use rustc_ast as ast; use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; -use rustc_ast::tokenstream::{DelimSpan, TokenStream}; +use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, TransparencyError}; @@ -212,7 +212,6 @@ fn expand_macro<'cx>( }; let arm_span = rhses[i].span(); - let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>(); // rhs has holes ( `$id` and `$(...)` that need filled) let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) { Ok(tts) => tts, @@ -224,12 +223,25 @@ fn expand_macro<'cx>( // Replace all the tokens for the corresponding positions in the macro, to maintain // proper positions in error reporting, while maintaining the macro_backtrace. - if rhs_spans.len() == tts.len() { + if tts.len() == rhs.tts.len() { tts = tts.map_enumerated(|i, tt| { let mut tt = tt.clone(); - let mut sp = rhs_spans[i]; - sp = sp.with_ctxt(tt.span().ctxt()); - tt.set_span(sp); + let rhs_tt = &rhs.tts[i]; + let ctxt = tt.span().ctxt(); + match (&mut tt, rhs_tt) { + // preserve the delim spans if able + ( + TokenTree::Delimited(target_sp, ..), + mbe::TokenTree::Delimited(source_sp, ..), + ) => { + target_sp.open = source_sp.open.with_ctxt(ctxt); + target_sp.close = source_sp.close.with_ctxt(ctxt); + } + _ => { + let sp = rhs_tt.span().with_ctxt(ctxt); + tt.set_span(sp); + } + } tt }); } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index f27fd90c55c..9b22ad098b2 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2175,13 +2175,31 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { dropped_predicate_count += 1; } - if drop_predicate && !in_where_clause { - lint_spans.push(predicate_span); - } else if drop_predicate && i + 1 < num_predicates { - // If all the bounds on a predicate were inferable and there are - // further predicates, we want to eat the trailing comma. - let next_predicate_span = hir_generics.predicates[i + 1].span(); - where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo())); + if drop_predicate { + if !in_where_clause { + lint_spans.push(predicate_span); + } else if predicate_span.from_expansion() { + // Don't try to extend the span if it comes from a macro expansion. + where_lint_spans.push(predicate_span); + } else if i + 1 < num_predicates { + // If all the bounds on a predicate were inferable and there are + // further predicates, we want to eat the trailing comma. + let next_predicate_span = hir_generics.predicates[i + 1].span(); + if next_predicate_span.from_expansion() { + where_lint_spans.push(predicate_span); + } else { + where_lint_spans + .push(predicate_span.to(next_predicate_span.shrink_to_lo())); + } + } else { + // Eat the optional trailing comma after the last predicate. + let where_span = hir_generics.where_clause_span; + if where_span.from_expansion() { + where_lint_spans.push(predicate_span); + } else { + where_lint_spans.push(predicate_span.to(where_span.shrink_to_hi())); + } + } } else { where_lint_spans.extend(self.consolidate_outlives_bound_spans( predicate_span.shrink_to_lo(), @@ -2225,6 +2243,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { Applicability::MaybeIncorrect }; + // Due to macros, there might be several predicates with the same span + // and we only want to suggest removing them once. + lint_spans.sort_unstable(); + lint_spans.dedup(); + cx.emit_spanned_lint( EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |
