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:
parent
cedd4cad21
commit
b757663a00
5 changed files with 67 additions and 22 deletions
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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""#);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue