Auto merge of #119286 - jyn514:linker-output, r=bjorn3
show linker output even if the linker succeeds
Show stderr and stderr by default, controlled by a new `linker_messages` lint.
fixes https://github.com/rust-lang/rust/issues/83436. fixes https://github.com/rust-lang/rust/issues/38206. cc 408986134
<!-- try-job: dist-x86_64-msvc -->
try-job: aarch64-apple
r? `@bjorn3`
This commit is contained in:
commit
f7cc13af82
26 changed files with 350 additions and 73 deletions
|
@ -183,6 +183,8 @@ codegen_ssa_linker_file_stem = couldn't extract file stem from specified linker
|
|||
codegen_ssa_linker_not_found = linker `{$linker_path}` not found
|
||||
.note = {$error}
|
||||
|
||||
codegen_ssa_linker_output = {$inner}
|
||||
|
||||
codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for current linker
|
||||
|
||||
codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status}
|
||||
|
|
|
@ -15,12 +15,14 @@ use rustc_ast::CRATE_NODE_ID;
|
|||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_data_structures::temp_dir::MaybeTempDir;
|
||||
use rustc_errors::DiagCtxtHandle;
|
||||
use rustc_errors::{DiagCtxtHandle, LintDiagnostic};
|
||||
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
use rustc_macros::LintDiagnostic;
|
||||
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
|
||||
use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::lint::lint_level;
|
||||
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
|
||||
use rustc_middle::middle::dependency_format::Linkage;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
|
@ -29,6 +31,7 @@ use rustc_session::config::{
|
|||
OutputType, PrintKind, SplitDwarfKind, Strip,
|
||||
};
|
||||
use rustc_session::cstore::DllImport;
|
||||
use rustc_session::lint::builtin::LINKER_MESSAGES;
|
||||
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
|
||||
use rustc_session::search_paths::PathKind;
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
|
@ -749,6 +752,14 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(codegen_ssa_linker_output)]
|
||||
/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just
|
||||
/// end up with inconsistent languages within the same diagnostic.
|
||||
struct LinkerOutput {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
/// Create a dynamic library or executable.
|
||||
///
|
||||
/// This will invoke the system linker/cc to create the resulting file. This links to all upstream
|
||||
|
@ -981,6 +992,11 @@ fn link_natively(
|
|||
|
||||
match prog {
|
||||
Ok(prog) => {
|
||||
let is_msvc_link_exe = sess.target.is_like_msvc
|
||||
&& flavor == LinkerFlavor::Msvc(Lld::No)
|
||||
// Match exactly "link.exe"
|
||||
&& linker_path.to_str() == Some("link.exe");
|
||||
|
||||
if !prog.status.success() {
|
||||
let mut output = prog.stderr.clone();
|
||||
output.extend_from_slice(&prog.stdout);
|
||||
|
@ -997,16 +1013,9 @@ fn link_natively(
|
|||
// is not a Microsoft LNK error then suggest a way to fix or
|
||||
// install the Visual Studio build tools.
|
||||
if let Some(code) = prog.status.code() {
|
||||
if sess.target.is_like_msvc
|
||||
&& flavor == LinkerFlavor::Msvc(Lld::No)
|
||||
// Respect the command line override
|
||||
&& sess.opts.cg.linker.is_none()
|
||||
// Match exactly "link.exe"
|
||||
&& linker_path.to_str() == Some("link.exe")
|
||||
// All Microsoft `link.exe` linking error codes are
|
||||
// four digit numbers in the range 1000 to 9999 inclusive
|
||||
&& (code < 1000 || code > 9999)
|
||||
{
|
||||
// All Microsoft `link.exe` linking ror codes are
|
||||
// four digit numbers in the range 1000 to 9999 inclusive
|
||||
if is_msvc_link_exe && (code < 1000 || code > 9999) {
|
||||
let is_vs_installed = windows_registry::find_vs_version().is_ok();
|
||||
let has_linker =
|
||||
windows_registry::find_tool(&sess.target.arch, "link.exe").is_some();
|
||||
|
@ -1028,8 +1037,49 @@ fn link_natively(
|
|||
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
info!("linker stderr:\n{}", escape_string(&prog.stderr));
|
||||
info!("linker stdout:\n{}", escape_string(&prog.stdout));
|
||||
|
||||
let stderr = escape_string(&prog.stderr);
|
||||
let mut stdout = escape_string(&prog.stdout);
|
||||
info!("linker stderr:\n{}", &stderr);
|
||||
info!("linker stdout:\n{}", &stdout);
|
||||
|
||||
// Hide some progress messages from link.exe that we don't care about.
|
||||
// See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146
|
||||
if is_msvc_link_exe {
|
||||
if let Ok(str) = str::from_utf8(&prog.stdout) {
|
||||
let mut output = String::with_capacity(str.len());
|
||||
for line in stdout.lines() {
|
||||
if line.starts_with(" Creating library")
|
||||
|| line.starts_with("Generating code")
|
||||
|| line.starts_with("Finished generating code")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
output += line;
|
||||
output += "\r\n"
|
||||
}
|
||||
stdout = escape_string(output.trim().as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
let (level, src) = codegen_results.crate_info.lint_levels.linker_messages;
|
||||
let lint = |msg| {
|
||||
lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| {
|
||||
LinkerOutput { inner: msg }.decorate_lint(diag)
|
||||
})
|
||||
};
|
||||
|
||||
if !prog.stderr.is_empty() {
|
||||
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
|
||||
let stderr = stderr
|
||||
.strip_prefix("warning: ")
|
||||
.unwrap_or(&stderr)
|
||||
.replace(": warning: ", ": ");
|
||||
lint(format!("linker stderr: {stderr}"));
|
||||
}
|
||||
if !stdout.is_empty() {
|
||||
lint(format!("linker stdout: {}", stdout))
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
|
||||
|
|
|
@ -44,7 +44,8 @@ use crate::mir::operand::OperandValue;
|
|||
use crate::mir::place::PlaceRef;
|
||||
use crate::traits::*;
|
||||
use crate::{
|
||||
CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir,
|
||||
CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind,
|
||||
errors, meth, mir,
|
||||
};
|
||||
|
||||
pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate {
|
||||
|
@ -924,6 +925,7 @@ impl CrateInfo {
|
|||
dependency_formats: Lrc::clone(tcx.dependency_formats(())),
|
||||
windows_subsystem,
|
||||
natvis_debugger_visualizers: Default::default(),
|
||||
lint_levels: CodegenLintLevels::from_tcx(tcx),
|
||||
};
|
||||
|
||||
info.native_libraries.reserve(n_crates);
|
||||
|
|
|
@ -29,18 +29,23 @@ use rustc_ast as ast;
|
|||
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_hir::CRATE_HIR_ID;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable};
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::lint::LintLevelSource;
|
||||
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
|
||||
use rustc_middle::middle::dependency_format::Dependencies;
|
||||
use rustc_middle::middle::exported_symbols::SymbolExportKind;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::util::Providers;
|
||||
use rustc_serialize::opaque::{FileEncoder, MemDecoder};
|
||||
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
|
||||
use rustc_session::cstore::{self, CrateSource};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::lint::builtin::LINKER_MESSAGES;
|
||||
use rustc_session::utils::NativeLibKind;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
|
@ -200,6 +205,7 @@ pub struct CrateInfo {
|
|||
pub dependency_formats: Lrc<Dependencies>,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>,
|
||||
pub lint_levels: CodegenLintLevels,
|
||||
}
|
||||
|
||||
#[derive(Encodable, Decodable)]
|
||||
|
@ -302,3 +308,19 @@ impl CodegenResults {
|
|||
Ok((codegen_results, outputs))
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of lint levels used in codegen.
|
||||
///
|
||||
/// When using `-Z link-only`, we don't have access to the tcx and must work
|
||||
/// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable.
|
||||
/// Instead, encode exactly the information we need.
|
||||
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
|
||||
pub struct CodegenLintLevels {
|
||||
linker_messages: (Level, LintLevelSource),
|
||||
}
|
||||
|
||||
impl CodegenLintLevels {
|
||||
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
|
||||
Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use rustc_error_messages::FluentArgs;
|
|||
use rustc_lint_defs::Applicability;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::hygiene::ExpnData;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::source_map::{FilePathMapping, SourceMap};
|
||||
use serde::Serialize;
|
||||
use termcolor::{ColorSpec, WriteColor};
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub struct JsonEmitter {
|
|||
#[setters(skip)]
|
||||
dst: IntoDynSyncSend<Box<dyn Write + Send>>,
|
||||
#[setters(skip)]
|
||||
sm: Lrc<SourceMap>,
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fluent_bundle: Option<Lrc<FluentBundle>>,
|
||||
#[setters(skip)]
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
|
@ -65,7 +65,7 @@ pub struct JsonEmitter {
|
|||
impl JsonEmitter {
|
||||
pub fn new(
|
||||
dst: Box<dyn Write + Send>,
|
||||
sm: Lrc<SourceMap>,
|
||||
sm: Option<Lrc<SourceMap>>,
|
||||
fallback_bundle: LazyFallbackBundle,
|
||||
pretty: bool,
|
||||
json_rendered: HumanReadableErrorType,
|
||||
|
@ -171,7 +171,7 @@ impl Emitter for JsonEmitter {
|
|||
}
|
||||
|
||||
fn source_map(&self) -> Option<&SourceMap> {
|
||||
Some(&self.sm)
|
||||
self.sm.as_deref()
|
||||
}
|
||||
|
||||
fn should_show_explain(&self) -> bool {
|
||||
|
@ -371,7 +371,7 @@ impl Diagnostic {
|
|||
}
|
||||
HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle))
|
||||
.short_message(short)
|
||||
.sm(Some(Lrc::clone(&je.sm)))
|
||||
.sm(je.sm.clone())
|
||||
.fluent_bundle(je.fluent_bundle.clone())
|
||||
.diagnostic_width(je.diagnostic_width)
|
||||
.macro_backtrace(je.macro_backtrace)
|
||||
|
@ -458,23 +458,34 @@ impl DiagnosticSpan {
|
|||
mut backtrace: impl Iterator<Item = ExpnData>,
|
||||
je: &JsonEmitter,
|
||||
) -> 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
|
||||
// is an empty string, increase the length to include the newline so we don't
|
||||
// leave an empty line
|
||||
if start.col.0 == 0
|
||||
&& let Some((suggestion, _)) = suggestion
|
||||
&& 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')
|
||||
{
|
||||
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 call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
|
||||
let def_site_span = Self::from_span_full(
|
||||
je.sm.guess_head_span(bt.def_site),
|
||||
sm.guess_head_span(bt.def_site),
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
|
@ -489,7 +500,7 @@ impl 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_end: start.file.original_relative_byte_pos(span.hi()).0,
|
||||
line_start: start.line,
|
||||
|
@ -559,19 +570,20 @@ impl DiagnosticSpanLine {
|
|||
/// `span` within the line.
|
||||
fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> {
|
||||
je.sm
|
||||
.span_to_lines(span)
|
||||
.map(|lines| {
|
||||
.as_ref()
|
||||
.and_then(|sm| {
|
||||
let lines = sm.span_to_lines(span).ok()?;
|
||||
// We can't get any lines if the source is unavailable.
|
||||
if !should_show_source_code(
|
||||
&je.ignored_directories_in_source_blocks,
|
||||
&je.sm,
|
||||
&sm,
|
||||
&lines.file,
|
||||
) {
|
||||
return vec![];
|
||||
return None;
|
||||
}
|
||||
|
||||
let sf = &*lines.file;
|
||||
lines
|
||||
let span_lines = lines
|
||||
.lines
|
||||
.iter()
|
||||
.map(|line| {
|
||||
|
@ -582,8 +594,9 @@ impl DiagnosticSpanLine {
|
|||
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 je = JsonEmitter::new(
|
||||
Box::new(Shared { data: output.clone() }),
|
||||
sm,
|
||||
Some(sm),
|
||||
fallback_bundle,
|
||||
true, // pretty
|
||||
HumanReadableErrorType::Short,
|
||||
|
|
|
@ -60,6 +60,7 @@ declare_lint_pass! {
|
|||
LARGE_ASSIGNMENTS,
|
||||
LATE_BOUND_LIFETIME_ARGUMENTS,
|
||||
LEGACY_DERIVE_HELPERS,
|
||||
LINKER_MESSAGES,
|
||||
LONG_RUNNING_CONST_EVAL,
|
||||
LOSSY_PROVENANCE_CASTS,
|
||||
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
|
||||
|
@ -4085,6 +4086,39 @@ declare_lint! {
|
|||
"call to foreign functions or function pointers with FFI-unwind ABI"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `linker_messages` lint forwards warnings from the linker.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,ignore (needs CLI args, platform-specific)
|
||||
/// extern "C" {
|
||||
/// fn foo();
|
||||
/// }
|
||||
/// fn main () { unsafe { foo(); } }
|
||||
/// ```
|
||||
///
|
||||
/// On Linux, using `gcc -Wl,--warn-unresolved-symbols` as a linker, this will produce
|
||||
///
|
||||
/// ```text
|
||||
/// warning: linker stderr: rust-lld: undefined symbol: foo
|
||||
/// >>> referenced by rust_out.69edbd30df4ae57d-cgu.0
|
||||
/// >>> rust_out.rust_out.69edbd30df4ae57d-cgu.0.rcgu.o:(rust_out::main::h3a90094b06757803)
|
||||
/// |
|
||||
/// = note: `#[warn(linker_messages)]` on by default
|
||||
///
|
||||
/// warning: 1 warning emitted
|
||||
/// ```
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Linkers emit platform-specific and program-specific warnings that cannot be predicted in advance by the rust compiler.
|
||||
/// They are forwarded by default, but can be disabled by adding `#![allow(linker_messages)]` at the crate root.
|
||||
pub LINKER_MESSAGES,
|
||||
Warn,
|
||||
"warnings emitted at runtime by the target-specific linker program"
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `named_arguments_used_positionally` lint detects cases where named arguments are only
|
||||
/// used positionally in format strings. This usage is valid but potentially very confusing.
|
||||
|
|
|
@ -161,7 +161,19 @@ impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectation
|
|||
/// Setting for how to handle a lint.
|
||||
///
|
||||
/// See: <https://doc.rust-lang.org/rustc/lints/levels.html>
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, HashStable_Generic)]
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
Ord,
|
||||
Debug,
|
||||
Hash,
|
||||
Encodable,
|
||||
Decodable,
|
||||
HashStable_Generic
|
||||
)]
|
||||
pub enum Level {
|
||||
/// The `allow` level will not issue any message.
|
||||
Allow,
|
||||
|
|
|
@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexMap;
|
|||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_errors::{Diag, MultiSpan};
|
||||
use rustc_hir::{HirId, ItemLocalId};
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_macros::{Decodable, Encodable, HashStable};
|
||||
use rustc_session::Session;
|
||||
use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS};
|
||||
use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId};
|
||||
|
@ -15,7 +15,7 @@ use tracing::instrument;
|
|||
use crate::ty::TyCtxt;
|
||||
|
||||
/// How a lint level was set.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, HashStable, Debug)]
|
||||
pub enum LintLevelSource {
|
||||
/// Lint is at the default level as declared in rustc.
|
||||
Default,
|
||||
|
@ -173,7 +173,7 @@ impl TyCtxt<'_> {
|
|||
/// This struct represents a lint expectation and holds all required information
|
||||
/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after
|
||||
/// the `LateLintPass` has completed.
|
||||
#[derive(Clone, Debug, HashStable)]
|
||||
#[derive(Clone, Debug, Encodable, Decodable, HashStable)]
|
||||
pub struct LintExpectation {
|
||||
/// The reason for this expectation that can optionally be added as part of
|
||||
/// the attribute. It will be displayed as part of the lint message.
|
||||
|
|
|
@ -812,6 +812,9 @@ passes_unused_duplicate =
|
|||
passes_unused_empty_lints_note =
|
||||
attribute `{$name}` with an empty list has no effect
|
||||
|
||||
passes_unused_linker_warnings_note =
|
||||
the `linker_warnings` lint can only be controlled at the root of a crate that needs to be linked
|
||||
|
||||
passes_unused_multiple =
|
||||
multiple `{$name}` attributes
|
||||
.suggestion = remove this attribute
|
||||
|
|
|
@ -25,6 +25,7 @@ use rustc_middle::traits::ObligationCause;
|
|||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::{self, TyCtxt, TypingMode};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::config::CrateType;
|
||||
use rustc_session::lint::builtin::{
|
||||
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
|
||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
|
||||
|
@ -2327,6 +2328,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
&& item.path == sym::reason
|
||||
{
|
||||
errors::UnusedNote::NoLints { name: attr.name_or_empty() }
|
||||
} else if matches!(
|
||||
attr.name_or_empty(),
|
||||
sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect
|
||||
) && let Some(meta) = attr.meta_item_list()
|
||||
&& meta.iter().any(|meta| {
|
||||
meta.meta_item().map_or(false, |item| item.path == sym::linker_messages)
|
||||
})
|
||||
{
|
||||
if hir_id != CRATE_HIR_ID {
|
||||
match attr.style {
|
||||
ast::AttrStyle::Outer => self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::OuterCrateLevelAttr,
|
||||
),
|
||||
ast::AttrStyle::Inner => self.tcx.emit_node_span_lint(
|
||||
UNUSED_ATTRIBUTES,
|
||||
hir_id,
|
||||
attr.span,
|
||||
errors::InnerCrateLevelAttr,
|
||||
),
|
||||
};
|
||||
return;
|
||||
} else {
|
||||
let never_needs_link = self
|
||||
.tcx
|
||||
.crate_types()
|
||||
.iter()
|
||||
.all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib));
|
||||
if never_needs_link {
|
||||
errors::UnusedNote::LinkerWarningsBinaryCrateOnly
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if attr.name_or_empty() == sym::default_method_body_is_const {
|
||||
errors::UnusedNote::DefaultMethodBodyConst
|
||||
} else {
|
||||
|
|
|
@ -802,6 +802,8 @@ pub(crate) enum UnusedNote {
|
|||
NoLints { name: Symbol },
|
||||
#[note(passes_unused_default_method_body_const_note)]
|
||||
DefaultMethodBodyConst,
|
||||
#[note(passes_unused_linker_warnings_note)]
|
||||
LinkerWarningsBinaryCrateOnly,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
|
@ -895,13 +895,16 @@ fn default_emitter(
|
|||
}
|
||||
t => t,
|
||||
};
|
||||
|
||||
let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) };
|
||||
|
||||
match sopts.error_format {
|
||||
config::ErrorOutputType::HumanReadable(kind, color_config) => {
|
||||
let short = kind.short();
|
||||
|
||||
if let HumanReadableErrorType::AnnotateSnippet = kind {
|
||||
let emitter = AnnotateSnippetEmitter::new(
|
||||
Some(source_map),
|
||||
source_map,
|
||||
bundle,
|
||||
fallback_bundle,
|
||||
short,
|
||||
|
@ -911,7 +914,7 @@ fn default_emitter(
|
|||
} else {
|
||||
let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle)
|
||||
.fluent_bundle(bundle)
|
||||
.sm(Some(source_map))
|
||||
.sm(source_map)
|
||||
.short_message(short)
|
||||
.teach(sopts.unstable_opts.teach)
|
||||
.diagnostic_width(sopts.diagnostic_width)
|
||||
|
@ -1442,7 +1445,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
|
|||
config::ErrorOutputType::Json { pretty, json_rendered, color_config } => {
|
||||
Box::new(JsonEmitter::new(
|
||||
Box::new(io::BufWriter::new(io::stderr())),
|
||||
Lrc::new(SourceMap::new(FilePathMapping::empty())),
|
||||
Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))),
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
|
|
|
@ -1189,6 +1189,7 @@ symbols! {
|
|||
link_section,
|
||||
linkage,
|
||||
linker,
|
||||
linker_messages,
|
||||
lint_reasons,
|
||||
literal,
|
||||
load,
|
||||
|
|
|
@ -1842,12 +1842,19 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
|
|||
let mut hostflags = flags.clone();
|
||||
hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
|
||||
hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
|
||||
for flag in hostflags {
|
||||
cmd.arg("--host-rustcflags").arg(flag);
|
||||
}
|
||||
|
||||
let mut targetflags = flags;
|
||||
targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
|
||||
|
||||
// FIXME: on macOS, we get linker warnings about duplicate `-lm` flags. We should investigate why this happens.
|
||||
if suite == "ui-fulldeps" && target.ends_with("darwin") {
|
||||
hostflags.push("-Alinker_messages".into());
|
||||
targetflags.push("-Alinker_messages".into());
|
||||
}
|
||||
|
||||
for flag in hostflags {
|
||||
cmd.arg("--host-rustcflags").arg(flag);
|
||||
}
|
||||
for flag in targetflags {
|
||||
cmd.arg("--target-rustcflags").arg(flag);
|
||||
}
|
||||
|
@ -3554,6 +3561,8 @@ impl Step for CodegenGCC {
|
|||
let mut cargo = build_cargo();
|
||||
|
||||
cargo
|
||||
// cg_gcc's build system ignores RUSTFLAGS. pass some flags through CG_RUSTFLAGS instead.
|
||||
.env("CG_RUSTFLAGS", "-Alinker-messages")
|
||||
.arg("--")
|
||||
.arg("test")
|
||||
.arg("--use-system-gcc")
|
||||
|
|
|
@ -273,6 +273,13 @@ impl Cargo {
|
|||
self.rustflags.arg("-Clink-arg=-gz");
|
||||
}
|
||||
|
||||
// Ignore linker warnings for now. These are complicated to fix and don't affect the build.
|
||||
// FIXME: we should really investigate these...
|
||||
// cfg(bootstrap)
|
||||
if compiler.stage != 0 {
|
||||
self.rustflags.arg("-Alinker-messages");
|
||||
}
|
||||
|
||||
// Throughout the build Cargo can execute a number of build scripts
|
||||
// compiling C/C++ code and we need to pass compilers, archivers, flags, etc
|
||||
// obtained previously to those build scripts.
|
||||
|
|
|
@ -178,7 +178,7 @@ pub(crate) fn new_dcx(
|
|||
Box::new(
|
||||
JsonEmitter::new(
|
||||
Box::new(io::BufWriter::new(io::stderr())),
|
||||
source_map,
|
||||
Some(source_map),
|
||||
fallback_bundle,
|
||||
pretty,
|
||||
json_rendered,
|
||||
|
|
17
tests/run-make/linker-warning/fake-linker.sh
Executable file
17
tests/run-make/linker-warning/fake-linker.sh
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
code=0
|
||||
while ! [ $# = 0 ]; do
|
||||
case "$1" in
|
||||
run_make_info) echo "foo"
|
||||
;;
|
||||
run_make_warn) echo "warning: bar" >&2
|
||||
;;
|
||||
run_make_error) echo "error: baz" >&2; code=1
|
||||
;;
|
||||
*) ;; # rustc passes lots of args we don't care about
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
exit $code
|
|
@ -10,6 +10,20 @@ fn main() {
|
|||
// first, compile our linker
|
||||
rustc().arg("fake-linker.rs").output("fake-linker").run();
|
||||
|
||||
// Run rustc with our fake linker, and make sure it shows warnings
|
||||
let warnings = run_rustc().link_arg("run_make_warn").run();
|
||||
warnings.assert_stderr_contains("warning: linker stderr: bar");
|
||||
|
||||
// Make sure it shows stdout
|
||||
run_rustc()
|
||||
.link_arg("run_make_info")
|
||||
.run()
|
||||
.assert_stderr_contains("warning: linker stdout: foo");
|
||||
|
||||
// Make sure we short-circuit this new path if the linker exits with an error
|
||||
// (so the diagnostic is less verbose)
|
||||
run_rustc().link_arg("run_make_error").run_fail().assert_stderr_contains("note: error: baz");
|
||||
|
||||
// Make sure we don't show the linker args unless `--verbose` is passed
|
||||
run_rustc()
|
||||
.link_arg("run_make_error")
|
||||
|
@ -25,4 +39,33 @@ fn main() {
|
|||
.assert_stderr_contains("object files omitted")
|
||||
.assert_stderr_contains_regex(r"\{")
|
||||
.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""#);
|
||||
}
|
||||
|
|
|
@ -12,11 +12,7 @@ use run_make_support::rustc;
|
|||
fn main() {
|
||||
// A regular compilation should not use rust-lld by default. We'll check that by asking the
|
||||
// linker to display its version number with a link-arg.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.link_arg("-Wl,-v")
|
||||
.input("main.rs")
|
||||
.run();
|
||||
let output = rustc().link_arg("-Wl,-v").input("main.rs").run();
|
||||
assert!(
|
||||
!find_lld_version_in_logs(output.stderr_utf8()),
|
||||
"the LLD version string should not be present in the output logs:\n{}",
|
||||
|
@ -25,6 +21,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn find_lld_version_in_logs(stderr: String) -> bool {
|
||||
let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
let lld_version_re =
|
||||
Regex::new(r"^warning: linker stdout: LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
stderr.lines().any(|line| lld_version_re.is_match(line.trim()))
|
||||
}
|
||||
|
|
|
@ -12,11 +12,7 @@ use run_make_support::rustc;
|
|||
fn main() {
|
||||
// A regular compilation should use rust-lld by default. We'll check that by asking the linker
|
||||
// to display its version number with a link-arg.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.link_arg("-Wl,-v")
|
||||
.input("main.rs")
|
||||
.run();
|
||||
let output = rustc().link_arg("-Wl,-v").input("main.rs").run();
|
||||
assert!(
|
||||
find_lld_version_in_logs(output.stderr_utf8()),
|
||||
"the LLD version string should be present in the output logs:\n{}",
|
||||
|
@ -24,12 +20,7 @@ fn main() {
|
|||
);
|
||||
|
||||
// But it can still be disabled by turning the linker feature off.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.link_arg("-Wl,-v")
|
||||
.arg("-Zlinker-features=-lld")
|
||||
.input("main.rs")
|
||||
.run();
|
||||
let output = rustc().link_arg("-Wl,-v").arg("-Zlinker-features=-lld").input("main.rs").run();
|
||||
assert!(
|
||||
!find_lld_version_in_logs(output.stderr_utf8()),
|
||||
"the LLD version string should not be present in the output logs:\n{}",
|
||||
|
@ -38,6 +29,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn find_lld_version_in_logs(stderr: String) -> bool {
|
||||
let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
let lld_version_re =
|
||||
Regex::new(r"^warning: linker stdout: LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
stderr.lines().any(|line| lld_version_re.is_match(line.trim()))
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ fn main() {
|
|||
// Compile to a custom target spec with rust-lld enabled by default. We'll check that by asking
|
||||
// the linker to display its version number with a link-arg.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.crate_type("cdylib")
|
||||
.target("custom-target.json")
|
||||
.link_arg("-Wl,-v")
|
||||
|
@ -29,7 +28,6 @@ fn main() {
|
|||
|
||||
// But it can also be disabled via linker features.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.crate_type("cdylib")
|
||||
.target("custom-target.json")
|
||||
.arg("-Zlinker-features=-lld")
|
||||
|
@ -44,6 +42,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn find_lld_version_in_logs(stderr: String) -> bool {
|
||||
let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
let lld_version_re =
|
||||
Regex::new(r"^warning: linker stdout: LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
stderr.lines().any(|line| lld_version_re.is_match(line.trim()))
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ fn main() {
|
|||
// Opt-in to lld and the self-contained linker, to link with rust-lld. We'll check that by
|
||||
// asking the linker to display its version number with a link-arg.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.arg("-Zlinker-features=+lld")
|
||||
.arg("-Clink-self-contained=+linker")
|
||||
.arg("-Zunstable-options")
|
||||
|
@ -28,12 +27,8 @@ fn main() {
|
|||
);
|
||||
|
||||
// It should not be used when we explicitly opt-out of lld.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.link_arg(linker_version_flag)
|
||||
.arg("-Zlinker-features=-lld")
|
||||
.input("main.rs")
|
||||
.run();
|
||||
let output =
|
||||
rustc().link_arg(linker_version_flag).arg("-Zlinker-features=-lld").input("main.rs").run();
|
||||
assert!(
|
||||
!find_lld_version_in_logs(output.stderr_utf8()),
|
||||
"the LLD version string should not be present in the output logs:\n{}",
|
||||
|
@ -43,7 +38,6 @@ fn main() {
|
|||
// While we're here, also check that the last linker feature flag "wins" when passed multiple
|
||||
// times to rustc.
|
||||
let output = rustc()
|
||||
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info")
|
||||
.link_arg(linker_version_flag)
|
||||
.arg("-Clink-self-contained=+linker")
|
||||
.arg("-Zunstable-options")
|
||||
|
@ -60,6 +54,7 @@ fn main() {
|
|||
}
|
||||
|
||||
fn find_lld_version_in_logs(stderr: String) -> bool {
|
||||
let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
let lld_version_re =
|
||||
Regex::new(r"^warning: linker std(out|err): LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
|
||||
stderr.lines().any(|line| lld_version_re.is_match(line.trim()))
|
||||
}
|
||||
|
|
6
tests/ui/lint/linker-warning-bin.rs
Normal file
6
tests/ui/lint/linker-warning-bin.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
//@ build-pass
|
||||
#![crate_type = "bin"]
|
||||
#![warn(unused_attributes)]
|
||||
#![allow(linker_messages)]
|
||||
|
||||
fn main() {}
|
9
tests/ui/lint/linker-warning.rs
Normal file
9
tests/ui/lint/linker-warning.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
//@ check-pass
|
||||
#![crate_type = "lib"]
|
||||
#![warn(unused_attributes)]
|
||||
#![allow(linker_messages)]
|
||||
//~^ WARNING unused attribute
|
||||
|
||||
#[allow(linker_messages)]
|
||||
//~^ WARNING should be an inner attribute
|
||||
fn foo() {}
|
22
tests/ui/lint/linker-warning.stderr
Normal file
22
tests/ui/lint/linker-warning.stderr
Normal file
|
@ -0,0 +1,22 @@
|
|||
warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
|
||||
--> $DIR/linker-warning.rs:7:1
|
||||
|
|
||||
LL | #[allow(linker_messages)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/linker-warning.rs:3:9
|
||||
|
|
||||
LL | #![warn(unused_attributes)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unused attribute
|
||||
--> $DIR/linker-warning.rs:4:1
|
||||
|
|
||||
LL | #![allow(linker_messages)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
||||
|
|
||||
= note: the `linker_warnings` lint can only be controlled at the root of a crate that needs to be linked
|
||||
|
||||
warning: 2 warnings emitted
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue