1
Fork 0

don't ICE when emitting linker errors during -Z link-only

note that this still ICEs when passed `-Z link-only --error-format json` because i can't be bothered to fix it right now
This commit is contained in:
jyn 2024-12-21 10:06:07 -05:00
parent cedd4cad21
commit b757663a00
5 changed files with 67 additions and 22 deletions

View file

@ -21,7 +21,7 @@ use rustc_error_messages::FluentArgs;
use rustc_lint_defs::Applicability; use rustc_lint_defs::Applicability;
use rustc_span::Span; use rustc_span::Span;
use rustc_span::hygiene::ExpnData; use rustc_span::hygiene::ExpnData;
use rustc_span::source_map::SourceMap; use rustc_span::source_map::{FilePathMapping, SourceMap};
use serde::Serialize; use serde::Serialize;
use termcolor::{ColorSpec, WriteColor}; use termcolor::{ColorSpec, WriteColor};
@ -45,7 +45,7 @@ pub struct JsonEmitter {
#[setters(skip)] #[setters(skip)]
dst: IntoDynSyncSend<Box<dyn Write + Send>>, dst: IntoDynSyncSend<Box<dyn Write + Send>>,
#[setters(skip)] #[setters(skip)]
sm: Lrc<SourceMap>, sm: Option<Lrc<SourceMap>>,
fluent_bundle: Option<Lrc<FluentBundle>>, fluent_bundle: Option<Lrc<FluentBundle>>,
#[setters(skip)] #[setters(skip)]
fallback_bundle: LazyFallbackBundle, fallback_bundle: LazyFallbackBundle,
@ -65,7 +65,7 @@ pub struct JsonEmitter {
impl JsonEmitter { impl JsonEmitter {
pub fn new( pub fn new(
dst: Box<dyn Write + Send>, dst: Box<dyn Write + Send>,
sm: Lrc<SourceMap>, sm: Option<Lrc<SourceMap>>,
fallback_bundle: LazyFallbackBundle, fallback_bundle: LazyFallbackBundle,
pretty: bool, pretty: bool,
json_rendered: HumanReadableErrorType, json_rendered: HumanReadableErrorType,
@ -171,7 +171,7 @@ impl Emitter for JsonEmitter {
} }
fn source_map(&self) -> Option<&SourceMap> { fn source_map(&self) -> Option<&SourceMap> {
Some(&self.sm) self.sm.as_deref()
} }
fn should_show_explain(&self) -> bool { fn should_show_explain(&self) -> bool {
@ -371,7 +371,7 @@ impl Diagnostic {
} }
HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle)) HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle))
.short_message(short) .short_message(short)
.sm(Some(Lrc::clone(&je.sm))) .sm(je.sm.clone())
.fluent_bundle(je.fluent_bundle.clone()) .fluent_bundle(je.fluent_bundle.clone())
.diagnostic_width(je.diagnostic_width) .diagnostic_width(je.diagnostic_width)
.macro_backtrace(je.macro_backtrace) .macro_backtrace(je.macro_backtrace)
@ -458,23 +458,34 @@ impl DiagnosticSpan {
mut backtrace: impl Iterator<Item = ExpnData>, mut backtrace: impl Iterator<Item = ExpnData>,
je: &JsonEmitter, je: &JsonEmitter,
) -> DiagnosticSpan { ) -> DiagnosticSpan {
let start = je.sm.lookup_char_pos(span.lo()); let empty_source_map;
let sm = match &je.sm {
Some(s) => s,
None => {
span = rustc_span::DUMMY_SP;
empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty()));
empty_source_map
.new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new());
&empty_source_map
}
};
let start = sm.lookup_char_pos(span.lo());
// If this goes from the start of a line to the end and the replacement // If this goes from the start of a line to the end and the replacement
// is an empty string, increase the length to include the newline so we don't // is an empty string, increase the length to include the newline so we don't
// leave an empty line // leave an empty line
if start.col.0 == 0 if start.col.0 == 0
&& let Some((suggestion, _)) = suggestion && let Some((suggestion, _)) = suggestion
&& suggestion.is_empty() && suggestion.is_empty()
&& let Ok(after) = je.sm.span_to_next_source(span) && let Ok(after) = sm.span_to_next_source(span)
&& after.starts_with('\n') && after.starts_with('\n')
{ {
span = span.with_hi(span.hi() + rustc_span::BytePos(1)); span = span.with_hi(span.hi() + rustc_span::BytePos(1));
} }
let end = je.sm.lookup_char_pos(span.hi()); let end = sm.lookup_char_pos(span.hi());
let backtrace_step = backtrace.next().map(|bt| { let backtrace_step = backtrace.next().map(|bt| {
let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
let def_site_span = Self::from_span_full( let def_site_span = Self::from_span_full(
je.sm.guess_head_span(bt.def_site), sm.guess_head_span(bt.def_site),
false, false,
None, None,
None, None,
@ -489,7 +500,7 @@ impl DiagnosticSpan {
}); });
DiagnosticSpan { DiagnosticSpan {
file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), file_name: sm.filename_for_diagnostics(&start.file.name).to_string(),
byte_start: start.file.original_relative_byte_pos(span.lo()).0, byte_start: start.file.original_relative_byte_pos(span.lo()).0,
byte_end: start.file.original_relative_byte_pos(span.hi()).0, byte_end: start.file.original_relative_byte_pos(span.hi()).0,
line_start: start.line, line_start: start.line,
@ -559,19 +570,20 @@ impl DiagnosticSpanLine {
/// `span` within the line. /// `span` within the line.
fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
je.sm je.sm
.span_to_lines(span) .as_ref()
.map(|lines| { .and_then(|sm| {
let lines = sm.span_to_lines(span).ok()?;
// We can't get any lines if the source is unavailable. // We can't get any lines if the source is unavailable.
if !should_show_source_code( if !should_show_source_code(
&je.ignored_directories_in_source_blocks, &je.ignored_directories_in_source_blocks,
&je.sm, &sm,
&lines.file, &lines.file,
) { ) {
return vec![]; return None;
} }
let sf = &*lines.file; let sf = &*lines.file;
lines let span_lines = lines
.lines .lines
.iter() .iter()
.map(|line| { .map(|line| {
@ -582,8 +594,9 @@ impl DiagnosticSpanLine {
line.end_col.0 + 1, line.end_col.0 + 1,
) )
}) })
.collect() .collect();
Some(span_lines)
}) })
.unwrap_or_else(|_| vec![]) .unwrap_or_default()
} }
} }

View file

@ -47,7 +47,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
let output = Arc::new(Mutex::new(Vec::new())); let output = Arc::new(Mutex::new(Vec::new()));
let je = JsonEmitter::new( let je = JsonEmitter::new(
Box::new(Shared { data: output.clone() }), Box::new(Shared { data: output.clone() }),
sm, Some(sm),
fallback_bundle, fallback_bundle,
true, // pretty true, // pretty
HumanReadableErrorType::Short, HumanReadableErrorType::Short,

View file

@ -895,13 +895,16 @@ fn default_emitter(
} }
t => t, t => t,
}; };
let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) };
match sopts.error_format { match sopts.error_format {
config::ErrorOutputType::HumanReadable(kind, color_config) => { config::ErrorOutputType::HumanReadable(kind, color_config) => {
let short = kind.short(); let short = kind.short();
if let HumanReadableErrorType::AnnotateSnippet = kind { if let HumanReadableErrorType::AnnotateSnippet = kind {
let emitter = AnnotateSnippetEmitter::new( let emitter = AnnotateSnippetEmitter::new(
Some(source_map), source_map,
bundle, bundle,
fallback_bundle, fallback_bundle,
short, short,
@ -911,7 +914,7 @@ fn default_emitter(
} else { } else {
let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle) let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
.fluent_bundle(bundle) .fluent_bundle(bundle)
.sm(Some(source_map)) .sm(source_map)
.short_message(short) .short_message(short)
.teach(sopts.unstable_opts.teach) .teach(sopts.unstable_opts.teach)
.diagnostic_width(sopts.diagnostic_width) .diagnostic_width(sopts.diagnostic_width)
@ -1442,7 +1445,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { config::ErrorOutputType::Json { pretty, json_rendered, color_config } => {
Box::new(JsonEmitter::new( Box::new(JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())), Box::new(io::BufWriter::new(io::stderr())),
Lrc::new(SourceMap::new(FilePathMapping::empty())), Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))),
fallback_bundle, fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,

View file

@ -178,7 +178,7 @@ pub(crate) fn new_dcx(
Box::new( Box::new(
JsonEmitter::new( JsonEmitter::new(
Box::new(io::BufWriter::new(io::stderr())), Box::new(io::BufWriter::new(io::stderr())),
source_map, Some(source_map),
fallback_bundle, fallback_bundle,
pretty, pretty,
json_rendered, json_rendered,

View file

@ -44,4 +44,33 @@ fn main() {
.assert_stderr_contains("object files omitted") .assert_stderr_contains("object files omitted")
.assert_stderr_contains_regex(r"\{") .assert_stderr_contains_regex(r"\{")
.assert_stderr_not_contains_regex(r"lib(/|\\\\)libstd"); .assert_stderr_not_contains_regex(r"lib(/|\\\\)libstd");
// Make sure we show linker warnings even across `-Z no-link`
rustc()
.arg("-Zno-link")
.input("-")
.stdin_buf("#![deny(linker_messages)] \n fn main() {}")
.run()
.assert_stderr_equals("");
rustc()
.arg("-Zlink-only")
.arg("rust_out.rlink")
.linker("./fake-linker")
.link_arg("run_make_warn")
.run_fail()
// NOTE: the error message here is quite bad (we don't have a source
// span, but still try to print the lint source). But `-Z link-only` is
// unstable and this still shows the linker warning itself so this is
// probably good enough.
.assert_stderr_contains("linker stderr: bar");
// Same thing, but with json output.
rustc()
.error_format("json")
.arg("-Zlink-only")
.arg("rust_out.rlink")
.linker("./fake-linker")
.link_arg("run_make_warn")
.run_fail()
.assert_stderr_contains(r#""$message_type":"diagnostic""#);
} }