diff options
Diffstat (limited to 'compiler/rustc_errors/src')
| -rw-r--r-- | compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic.rs | 33 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/diagnostic_builder.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/emitter.rs | 179 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/json/tests.rs | 7 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 171 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/snippet.rs | 2 |
8 files changed, 347 insertions, 82 deletions
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 577baec21f0..2253007ce30 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -145,8 +145,9 @@ impl AnnotateSnippetEmitterWriter { title: Some(Annotation { label: Some(&message), id: code.as_ref().map(|c| match c { - DiagnosticId::Error(val) - | DiagnosticId::Lint { name: val, has_future_breakage: _ } => val.as_str(), + DiagnosticId::Error(val) | DiagnosticId::Lint { name: val, .. } => { + val.as_str() + } }), annotation_type: annotation_type_for_level(*level), }), diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 14ccced2c6a..8199c44ee2a 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -29,7 +29,7 @@ pub struct Diagnostic { #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DiagnosticId { Error(String), - Lint { name: String, has_future_breakage: bool }, + Lint { name: String, has_future_breakage: bool, is_force_warn: bool }, } /// A "sub"-diagnostic attached to a parent diagnostic. @@ -109,6 +109,13 @@ impl Diagnostic { } } + pub fn is_force_warn(&self) -> bool { + match self.code { + Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn, + _ => false, + } + } + /// Cancel the diagnostic (a structured diagnostic must either be emitted or /// canceled or it will panic when dropped). pub fn cancel(&mut self) { @@ -437,6 +444,30 @@ impl Diagnostic { self } + /// Prints out a message with multiple suggested edits of the code. + /// See also [`Diagnostic::span_suggestion()`]. + pub fn multipart_suggestions( + &mut self, + msg: &str, + suggestions: impl Iterator<Item = Vec<(Span, String)>>, + applicability: Applicability, + ) -> &mut Self { + self.suggestions.push(CodeSuggestion { + substitutions: suggestions + .map(|sugg| Substitution { + parts: sugg + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }) + .collect(), + msg: msg.to_owned(), + style: SuggestionStyle::ShowCode, + applicability, + tool_metadata: Default::default(), + }); + self + } /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline, it will only show the message and not the suggestion. /// diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 282877d5dd1..d35b2924803 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -301,6 +301,20 @@ impl<'a> DiagnosticBuilder<'a> { self } + /// See [`Diagnostic::multipart_suggestions()`]. + pub fn multipart_suggestions( + &mut self, + msg: &str, + suggestions: impl Iterator<Item = Vec<(Span, String)>>, + applicability: Applicability, + ) -> &mut Self { + if !self.0.allow_suggestions { + return self; + } + self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability); + self + } + /// See [`Diagnostic::span_suggestion_short()`]. pub fn span_suggestion_short( &mut self, diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d3f92bf3047..25777f4133b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -9,13 +9,15 @@ use Destination::*; -use rustc_lint_defs::FutureBreakage; use rustc_span::source_map::SourceMap; use rustc_span::{MultiSpan, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; -use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle}; +use crate::{ + CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SubstitutionHighlight, + SuggestionStyle, +}; use rustc_lint_defs::pluralize; @@ -193,7 +195,7 @@ pub trait Emitter { /// other formats can, and will, simply ignore it. fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {} - fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {} + fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {} /// Emit list of unused externs fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {} @@ -309,9 +311,7 @@ pub trait Emitter { // are some which do actually involve macros. ExpnKind::Inlined | ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, - ExpnKind::Macro { kind: macro_kind, name, proc_macro: _ } => { - Some((macro_kind, name)) - } + ExpnKind::Macro(macro_kind, name) => Some((macro_kind, name)), } }); @@ -368,23 +368,11 @@ pub trait Emitter { continue; } - if matches!(trace.kind, ExpnKind::Inlined) { - new_labels - .push((trace.call_site, "in the inlined copy of this code".to_string())); - } else if always_backtrace { - let proc_macro = if let ExpnKind::Macro { kind: _, name: _, proc_macro: true } = - trace.kind - { - "procedural macro " - } else { - "" - }; - + if always_backtrace && !matches!(trace.kind, ExpnKind::Inlined) { new_labels.push(( trace.def_site, format!( - "in this expansion of {}`{}`{}", - proc_macro, + "in this expansion of `{}`{}", trace.kind.descr(), if macro_backtrace.len() > 1 { // if macro_backtrace.len() == 1 it'll be @@ -410,17 +398,27 @@ pub trait Emitter { // and it needs an "in this macro invocation" label to match that. let redundant_span = trace.call_site.contains(sp); - if !redundant_span - && matches!( - trace.kind, - ExpnKind::Macro { kind: MacroKind::Bang, name: _, proc_macro: _ } - ) - || always_backtrace - { + if !redundant_span || always_backtrace { + let msg: Cow<'static, _> = match trace.kind { + ExpnKind::Macro(MacroKind::Attr, _) => { + "this procedural macro expansion".into() + } + ExpnKind::Macro(MacroKind::Derive, _) => { + "this derive macro expansion".into() + } + ExpnKind::Macro(MacroKind::Bang, _) => "this macro invocation".into(), + ExpnKind::Inlined => "the inlined copy of this code".into(), + ExpnKind::Root => "in the crate root".into(), + ExpnKind::AstPass(kind) => kind.descr().into(), + ExpnKind::Desugaring(kind) => { + format!("this {} desugaring", kind.descr()).into() + } + }; new_labels.push(( trace.call_site, format!( - "in this macro invocation{}", + "in {}{}", + msg, if macro_backtrace.len() > 1 && always_backtrace { // only specify order when the macro // backtrace is multiple levels deep @@ -959,7 +957,6 @@ impl EmitterWriter { // | for pos in 0..=line_len { draw_col_separator(buffer, line_offset + pos + 1, width_offset - 2); - buffer.putc(line_offset + pos + 1, width_offset - 2, '|', Style::LineNumber); } // Write the horizontal lines for multiline annotations @@ -1349,7 +1346,11 @@ impl EmitterWriter { let buffer_msg_line_offset = buffer.num_lines(); // Add spacing line - draw_col_separator(&mut buffer, buffer_msg_line_offset, max_line_num_len + 1); + draw_col_separator_no_space( + &mut buffer, + buffer_msg_line_offset, + max_line_num_len + 1, + ); // Then, the secondary file indicator buffer.prepend(buffer_msg_line_offset + 1, "::: ", Style::LineNumber); @@ -1592,8 +1593,11 @@ impl EmitterWriter { ); let mut row_num = 2; + draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); let mut notice_capitalization = false; - for (complete, parts, only_capitalization) in suggestions.iter().take(MAX_SUGGESTIONS) { + for (complete, parts, highlights, only_capitalization) in + suggestions.iter().take(MAX_SUGGESTIONS) + { notice_capitalization |= only_capitalization; // Only show underline if the suggestion spans a single line and doesn't cover the // entirety of the code output. If you have multiple replacements in the same line @@ -1601,16 +1605,26 @@ impl EmitterWriter { let show_underline = !(parts.len() == 1 && parts[0].snippet.trim() == complete.trim()) && complete.lines().count() == 1; - let lines = sm + let has_deletion = parts.iter().any(|p| p.is_deletion()); + let is_multiline = complete.lines().count() > 1; + + let show_diff = has_deletion && !is_multiline; + + if show_diff { + row_num += 1; + } + + let file_lines = sm .span_to_lines(parts[0].span) .expect("span_to_lines failed when emitting suggestion"); - assert!(!lines.lines.is_empty() || parts[0].span.is_dummy()); + assert!(!file_lines.lines.is_empty() || parts[0].span.is_dummy()); let line_start = sm.lookup_char_pos(parts[0].span.lo()).line; draw_col_separator_no_space(&mut buffer, 1, max_line_num_len + 1); let mut lines = complete.lines(); - for (line_pos, line) in lines.by_ref().take(MAX_SUGGESTION_HIGHLIGHT_LINES).enumerate() + for (line_pos, (line, highlight_parts)) in + lines.by_ref().zip(highlights).take(MAX_SUGGESTION_HIGHLIGHT_LINES).enumerate() { // Print the span column to avoid confusion buffer.puts( @@ -1619,9 +1633,68 @@ impl EmitterWriter { &self.maybe_anonymized(line_start + line_pos), Style::LineNumber, ); + if show_diff { + // Add the line number for both addition and removal to drive the point home. + // + // N - fn foo<A: T>(bar: A) { + // N + fn foo(bar: impl T) { + buffer.puts( + row_num - 1, + 0, + &self.maybe_anonymized(line_start + line_pos), + Style::LineNumber, + ); + buffer.puts(row_num - 1, max_line_num_len + 1, "- ", Style::Removal); + buffer.puts( + row_num - 1, + max_line_num_len + 3, + &replace_tabs( + &*file_lines + .file + .get_line(file_lines.lines[line_pos].line_index) + .unwrap(), + ), + Style::NoStyle, + ); + buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); + } else if is_multiline { + match &highlight_parts[..] { + [SubstitutionHighlight { start: 0, end }] if *end == line.len() => { + buffer.puts(row_num, max_line_num_len + 1, "+ ", Style::Addition); + } + [] => { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + } + _ => { + buffer.puts(row_num, max_line_num_len + 1, "~ ", Style::Addition); + } + } + } else { + draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); + } + // print the suggestion - draw_col_separator(&mut buffer, row_num, max_line_num_len + 1); buffer.append(row_num, &replace_tabs(line), Style::NoStyle); + + // Colorize addition/replacements with green. + for &SubstitutionHighlight { start, end } in highlight_parts { + // Account for tabs when highlighting (#87972). + let tabs: usize = line + .chars() + .take(start) + .map(|ch| match ch { + '\t' => 3, + _ => 0, + }) + .sum(); + buffer.set_style_range( + row_num, + max_line_num_len + 3 + start + tabs, + max_line_num_len + 3 + end + tabs, + Style::Addition, + true, + ); + } row_num += 1; } @@ -1656,25 +1729,29 @@ impl EmitterWriter { let underline_start = (span_start_pos + start) as isize + offset; let underline_end = (span_start_pos + start + sub_len) as isize + offset; assert!(underline_start >= 0 && underline_end >= 0); + let padding: usize = max_line_num_len + 3; for p in underline_start..underline_end { - buffer.putc( - row_num, - ((max_line_num_len + 3) as isize + p) as usize, - '^', - Style::UnderlinePrimary, - ); - } - // underline removals too - if underline_start == underline_end { - for p in underline_start - 1..underline_start + 1 { + if !show_diff { + // If this is a replacement, underline with `^`, if this is an addition + // underline with `+`. buffer.putc( row_num, - ((max_line_num_len + 3) as isize + p) as usize, - '-', - Style::UnderlineSecondary, + (padding as isize + p) as usize, + if part.is_addition(&sm) { '+' } else { '~' }, + Style::Addition, ); } } + if show_diff { + // Colorize removal with red in diff format. + buffer.set_style_range( + row_num - 2, + (padding as isize + span_start_pos as isize) as usize, + (padding as isize + span_end_pos as isize) as usize, + Style::Removal, + true, + ); + } // length of the code after substitution let full_sub_len = part @@ -2131,6 +2208,12 @@ impl<'a> WritableDst<'a> { fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { let mut spec = ColorSpec::new(); match style { + Style::Addition => { + spec.set_fg(Some(Color::Green)).set_intense(true); + } + Style::Removal => { + spec.set_fg(Some(Color::Red)).set_intense(true); + } Style::LineAndColumn => {} Style::LineNumber => { spec.set_bold(true); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 5d175a3ade9..1b6cd04cca6 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -16,7 +16,7 @@ use crate::registry::Registry; use crate::DiagnosticId; use crate::ToolMetadata; use crate::{CodeSuggestion, SubDiagnostic}; -use rustc_lint_defs::{Applicability, FutureBreakage}; +use rustc_lint_defs::Applicability; use rustc_data_structures::sync::Lrc; use rustc_span::hygiene::ExpnData; @@ -134,17 +134,14 @@ impl Emitter for JsonEmitter { } } - fn emit_future_breakage_report(&mut self, diags: Vec<(FutureBreakage, crate::Diagnostic)>) { + fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) { let data: Vec<FutureBreakageItem> = diags .into_iter() - .map(|(breakage, mut diag)| { + .map(|mut diag| { if diag.level == crate::Level::Allow { diag.level = crate::Level::Warning; } - FutureBreakageItem { - future_breakage_date: breakage.date, - diagnostic: Diagnostic::from_errors_diagnostic(&diag, self), - } + FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) } }) .collect(); let report = FutureIncompatReport { future_incompat_report: data }; @@ -216,7 +213,7 @@ macro_rules! encode_fields { $( $enc.emit_struct_field( stringify!($name), - idx, + idx == 0, |enc| $name.encode(enc), )?; idx += 1; @@ -229,7 +226,7 @@ macro_rules! encode_fields { // Special-case encoder to skip tool_metadata if not set impl<E: Encoder> Encodable<E> for Diagnostic { fn encode(&self, s: &mut E) -> Result<(), E::Error> { - s.emit_struct("diagnostic", 7, |s| { + s.emit_struct(false, |s| { let mut idx = 0; idx = encode_fields!( @@ -326,7 +323,6 @@ struct ArtifactNotification<'a> { #[derive(Encodable)] struct FutureBreakageItem { - future_breakage_date: Option<&'static str>, diagnostic: Diagnostic, } @@ -559,7 +555,7 @@ impl DiagnosticCode { s.map(|s| { let s = match s { DiagnosticId::Error(s) => s, - DiagnosticId::Lint { name, has_future_breakage: _ } => name, + DiagnosticId::Lint { name, .. } => name, }; let je_result = je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index e69e868c8ed..d055937ac36 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -39,16 +39,11 @@ impl<T: Write> Write for Shared<T> { } } -fn with_default_session_globals(f: impl FnOnce()) { - let session_globals = rustc_span::SessionGlobals::new(rustc_span::edition::DEFAULT_EDITION); - rustc_span::SESSION_GLOBALS.set(&session_globals, f); -} - /// Test the span yields correct positions in JSON. fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let expected_output = TestData { spans: vec![expected_output] }; - with_default_session_globals(|| { + rustc_span::create_default_session_globals_then(|| { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index f53ce7ceace..a48d4fe8bb5 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -5,7 +5,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] #![feature(backtrace)] -#![feature(extended_key_value_attributes)] #![feature(format_args_capture)] #![feature(iter_zip)] #![feature(nll)] @@ -24,7 +23,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; -use rustc_lint_defs::FutureBreakage; pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_serialize::json::Json; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -162,32 +160,77 @@ pub struct SubstitutionPart { pub snippet: String, } +/// Used to translate between `Span`s and byte positions within a single output line in highlighted +/// code of structured suggestions. +#[derive(Debug, Clone, Copy)] +pub struct SubstitutionHighlight { + start: usize, + end: usize, +} + +impl SubstitutionPart { + pub fn is_addition(&self, sm: &SourceMap) -> bool { + !self.snippet.is_empty() + && sm + .span_to_snippet(self.span) + .map_or(self.span.is_empty(), |snippet| snippet.trim().is_empty()) + } + + pub fn is_deletion(&self) -> bool { + self.snippet.trim().is_empty() + } + + pub fn is_replacement(&self, sm: &SourceMap) -> bool { + !self.snippet.is_empty() + && sm + .span_to_snippet(self.span) + .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty()) + } +} + impl CodeSuggestion { /// Returns the assembled code suggestions, whether they should be shown with an underline /// and whether the substitution only differs in capitalization. - pub fn splice_lines(&self, sm: &SourceMap) -> Vec<(String, Vec<SubstitutionPart>, bool)> { + pub fn splice_lines( + &self, + sm: &SourceMap, + ) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> { + // For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector + // corresponds to the output snippet's lines, while the second level corresponds to the + // substrings within that line that should be highlighted. + use rustc_span::{CharPos, Pos}; + /// Append to a buffer the remainder of the line of existing source code, and return the + /// count of lines that have been added for accurate highlighting. fn push_trailing( buf: &mut String, line_opt: Option<&Cow<'_, str>>, lo: &Loc, hi_opt: Option<&Loc>, - ) { + ) -> usize { + let mut line_count = 0; let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize())); if let Some(line) = line_opt { if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) { let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi)); match hi_opt { - Some(hi) if hi > lo => buf.push_str(&line[lo..hi]), + Some(hi) if hi > lo => { + line_count = line[lo..hi].matches('\n').count(); + buf.push_str(&line[lo..hi]) + } Some(_) => (), - None => buf.push_str(&line[lo..]), + None => { + line_count = line[lo..].matches('\n').count(); + buf.push_str(&line[lo..]) + } } } if hi_opt.is_none() { buf.push('\n'); } } + line_count } assert!(!self.substitutions.is_empty()); @@ -222,6 +265,7 @@ impl CodeSuggestion { return None; } + let mut highlights = vec![]; // To build up the result, we do this for each span: // - push the line segment trailing the previous span // (at the beginning a "phantom" span pointing at the start of the line) @@ -238,17 +282,34 @@ impl CodeSuggestion { lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index)); let mut buf = String::new(); + let mut line_highlight = vec![]; + // We need to keep track of the difference between the existing code and the added + // or deleted code in order to point at the correct column *after* substitution. + let mut acc = 0; for part in &substitution.parts { let cur_lo = sm.lookup_char_pos(part.span.lo()); if prev_hi.line == cur_lo.line { - push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); + let mut count = + push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo)); + while count > 0 { + highlights.push(std::mem::take(&mut line_highlight)); + acc = 0; + count -= 1; + } } else { - push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + acc = 0; + highlights.push(std::mem::take(&mut line_highlight)); + let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None); + while count > 0 { + highlights.push(std::mem::take(&mut line_highlight)); + count -= 1; + } // push lines between the previous and current span (if any) for idx in prev_hi.line..(cur_lo.line - 1) { if let Some(line) = sf.get_line(idx) { buf.push_str(line.as_ref()); buf.push('\n'); + highlights.push(std::mem::take(&mut line_highlight)); } } if let Some(cur_line) = sf.get_line(cur_lo.line - 1) { @@ -259,10 +320,46 @@ impl CodeSuggestion { buf.push_str(&cur_line[..end]); } } + // Add a whole line highlight per line in the snippet. + let len: isize = part + .snippet + .split('\n') + .next() + .unwrap_or(&part.snippet) + .chars() + .map(|c| match c { + '\t' => 4, + _ => 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, + }); buf.push_str(&part.snippet); - prev_hi = sm.lookup_char_pos(part.span.hi()); + let cur_hi = sm.lookup_char_pos(part.span.hi()); + if prev_hi.line == cur_lo.line { + // Account for the difference between the width of the current code and the + // snippet being suggested, so that the *later* suggestions are correctly + // aligned on the screen. + acc += len as isize - (cur_hi.col.0 - cur_lo.col.0) as isize; + } + prev_hi = cur_hi; prev_line = sf.get_line(prev_hi.line - 1); + for line in part.snippet.split('\n').skip(1) { + acc = 0; + highlights.push(std::mem::take(&mut line_highlight)); + let end: usize = line + .chars() + .map(|c| match c { + '\t' => 4, + _ => 1, + }) + .sum(); + line_highlight.push(SubstitutionHighlight { start: 0, end }); + } } + highlights.push(std::mem::take(&mut line_highlight)); let only_capitalization = is_case_difference(sm, &buf, bounding_span); // if the replacement already ends with a newline, don't print the next line if !buf.ends_with('\n') { @@ -272,7 +369,7 @@ impl CodeSuggestion { while buf.ends_with('\n') { buf.pop(); } - Some((buf, substitution.parts, only_capitalization)) + Some((buf, substitution.parts, highlights, only_capitalization)) }) .collect() } @@ -344,6 +441,9 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec<Diagnostic>, + + /// If set to `true`, no warning or error will be emitted. + quiet: bool, } /// A key denoting where from a diagnostic was stashed. @@ -458,10 +558,19 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + quiet: false, }), } } + pub fn with_disabled_diagnostic<T, F: FnOnce() -> T>(&self, f: F) -> T { + let prev = self.inner.borrow_mut().quiet; + self.inner.borrow_mut().quiet = true; + let ret = f(); + self.inner.borrow_mut().quiet = prev; + ret + } + // This is here to not allow mutation of flags; // as of this writing it's only used in tests in librustc_middle. pub fn can_emit_warnings(&self) -> bool { @@ -521,12 +630,28 @@ impl Handler { } /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// + /// The builder will be canceled if warnings cannot be emitted. pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> { let mut result = self.struct_warn(msg); result.set_span(span); result } + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. + /// + /// This will "force" the warning meaning it will not be canceled even + /// if warnings cannot be emitted. + pub fn struct_span_force_warn( + &self, + span: impl Into<MultiSpan>, + msg: &str, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_force_warn(msg); + result.set_span(span); + result + } + /// Construct a builder at the `Allow` level at the given `span` and with the `msg`. pub fn struct_span_allow( &self, @@ -552,6 +677,8 @@ impl Handler { } /// Construct a builder at the `Warning` level with the `msg`. + /// + /// The builder will be canceled if warnings cannot be emitted. pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { let mut result = DiagnosticBuilder::new(self, Level::Warning, msg); if !self.flags.can_emit_warnings { @@ -560,6 +687,14 @@ impl Handler { result } + /// Construct a builder at the `Warning` level with the `msg`. + /// + /// This will "force" a warning meaning it will not be canceled even + /// if warnings cannot be emitted. + pub fn struct_force_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Warning, msg) + } + /// Construct a builder at the `Allow` level with the `msg`. pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> { DiagnosticBuilder::new(self, Level::Allow, msg) @@ -715,6 +850,7 @@ impl Handler { self.inner.borrow_mut().bug(msg) } + #[inline] pub fn err_count(&self) -> usize { self.inner.borrow().err_count() } @@ -764,7 +900,7 @@ impl Handler { self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) } - pub fn emit_future_breakage_report(&self, diags: Vec<(FutureBreakage, Diagnostic)>) { + pub fn emit_future_breakage_report(&self, diags: Vec<Diagnostic>) { self.inner.borrow_mut().emitter.emit_future_breakage_report(diags) } @@ -793,7 +929,7 @@ impl HandlerInner { } fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) { - if diagnostic.cancelled() { + if diagnostic.cancelled() || self.quiet { return; } @@ -801,7 +937,10 @@ impl HandlerInner { self.future_breakage_diagnostics.push(diagnostic.clone()); } - if diagnostic.level == Warning && !self.flags.can_emit_warnings { + if diagnostic.level == Warning + && !self.flags.can_emit_warnings + && !diagnostic.is_force_warn() + { if diagnostic.has_future_breakage() { (*TRACK_DIAGNOSTICS)(diagnostic); } @@ -873,7 +1012,7 @@ impl HandlerInner { match (errors.len(), warnings.len()) { (0, 0) => return, - (0, _) => self.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), + (0, _) => self.emitter.emit_diagnostic(&Diagnostic::new(Level::Warning, &warnings)), (_, 0) => { let _ = self.fatal(&errors); } @@ -924,6 +1063,7 @@ impl HandlerInner { } } + #[inline] fn err_count(&self) -> usize { self.err_count + self.stashed_diagnostics.len() } @@ -1006,6 +1146,9 @@ impl HandlerInner { } fn delay_as_bug(&mut self, diagnostic: Diagnostic) { + if self.quiet { + return; + } if self.flags.report_delayed_bugs { self.emit_diagnostic(&diagnostic); } diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index 3fe02bd0cee..64353461e90 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -177,4 +177,6 @@ pub enum Style { NoStyle, Level(Level), Highlight, + Addition, + Removal, } |
