Skip to content

Commit

Permalink
Auto merge of rust-lang#119286 - jyn514:linker-output, r=<try>
Browse files Browse the repository at this point in the history
show linker output even if the linker succeeds

- show stderr by default
- show stdout if `--verbose` is passed
- remove both from RUSTC_LOG
- hide the linker cli args unless `--verbose` is passed

fixes rust-lang#83436. fixes rust-lang#38206. fixes rust-lang#109979. helps with rust-lang#46998. cc https://rust-lang.zulipchat.com/#narrow/stream/233931-t-compiler.2Fmajor-changes/topic/uplift.20some.20-Zverbose.20calls.20and.20rename.20to.E2.80.A6.20compiler-team.23706/near/408986134

this is based on rust-lang#119129 for convenience so i didn't have to duplicate the changes around saving `--verbose` in rust-lang@cb6d033#diff-7a49efa20548d6806dbe1c66dd4dc445fda18fcbbf1709520cadecc4841aae12

try-job: aarch64-apple

r? `@bjorn3`
  • Loading branch information
bors committed Nov 30, 2024
2 parents e48ddd8 + fe9f272 commit 7b0e01d
Show file tree
Hide file tree
Showing 24 changed files with 285 additions and 59 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
38 changes: 34 additions & 4 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ErrorGuaranteed, FatalError};
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError, 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;
Expand All @@ -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;
Expand Down Expand Up @@ -762,6 +765,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
Expand Down Expand Up @@ -998,12 +1009,12 @@ fn link_natively(
let mut output = prog.stderr.clone();
output.extend_from_slice(&prog.stdout);
let escaped_output = escape_linker_output(&output, flavor);
// FIXME: Add UI tests for this error.
let err = errors::LinkingFailed {
linker_path: &linker_path,
exit_status: prog.status,
command: &cmd,
escaped_output,
verbose: sess.opts.verbose,
};
sess.dcx().emit_err(err);
// If MSVC's `link.exe` was expected but the return code
Expand Down Expand Up @@ -1045,8 +1056,27 @@ 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 (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 = escape_string(&prog.stderr);
debug!("original stderr: {stderr}");
let stderr = stderr
.strip_prefix("warning: ")
.unwrap_or(&stderr)
.replace(": warning: ", ": ");
lint(format!("linker stderr: {stderr}"));
}
if !prog.stdout.is_empty() && sess.opts.verbose {
lint(format!("linker stdout: {}", escape_string(&prog.stdout)))
}
}
Err(e) => {
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_codegen_ssa/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,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 {
Expand Down Expand Up @@ -926,6 +927,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);
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ pub(crate) struct LinkingFailed<'a> {
pub exit_status: ExitStatus,
pub command: &'a Command,
pub escaped_output: String,
pub verbose: bool,
}

impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {
Expand All @@ -359,7 +360,13 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {

let contains_undefined_ref = self.escaped_output.contains("undefined reference to");

diag.note(format!("{:?}", self.command)).note(self.escaped_output);
if self.verbose {
diag.note(format!("{:?}", self.command));
} else {
diag.note("use `--verbose` to show all linker arguments");
}

diag.note(self.escaped_output);

// Trying to match an error from OS linkers
// which by now we have no way to translate.
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::Symbol;

Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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 changes: 13 additions & 8 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,17 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
!has_future_breakage && !lint.eval_always
})
.filter_map(|lint| {
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
if matches!(lint_level, (Level::Allow, ..))
|| (matches!(lint_level, (.., LintLevelSource::Default)))
&& lint.default_level(tcx.sess.edition()) == Level::Allow
{
Some(LintId::of(lint))
if !lint.eval_always {
let lint_level =
map.lint_level_id_at_node(Some(tcx), tcx.sess, LintId::of(lint), CRATE_HIR_ID);
if matches!(lint_level, (Level::Allow, ..))
|| (matches!(lint_level, (.., LintLevelSource::Default)))
&& lint.default_level(tcx.sess.edition()) == Level::Allow
{
Some(LintId::of(lint))
} else {
None
}
} else {
None
}
Expand Down Expand Up @@ -248,8 +253,8 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> {
fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
}
fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
self.specs.lint_level_id_at_node(Some(self.tcx), sess, LintId::of(lint), self.cur)
}
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
self.specs.expectations.push((id, expectation))
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -4080,6 +4081,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.
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 30 additions & 18 deletions compiler/rustc_middle/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::cmp;
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_hir::{CRATE_HIR_ID, HirId, ItemLocalId};
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};
Expand All @@ -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,
Expand Down Expand Up @@ -119,7 +119,7 @@ impl ShallowLintLevelMap {
#[instrument(level = "trace", skip(self, tcx), ret)]
fn probe_for_lint_level(
&self,
tcx: TyCtxt<'_>,
tcx: Option<TyCtxt<'_>>,
id: LintId,
start: HirId,
) -> (Option<Level>, LintLevelSource) {
Expand All @@ -132,31 +132,38 @@ impl ShallowLintLevelMap {
let mut owner = start.owner;
let mut specs = &self.specs;

for parent in tcx.hir().parent_id_iter(start) {
if parent.owner != owner {
owner = parent.owner;
specs = &tcx.shallow_lint_levels_on(owner).specs;
}
if let Some(map) = specs.get(&parent.local_id)
&& let Some(&(level, src)) = map.get(&id)
{
return (Some(level), src);
if let Some(tcx) = tcx {
for parent in tcx.hir().parent_id_iter(start) {
if parent.owner != owner {
owner = parent.owner;
specs = &tcx.shallow_lint_levels_on(owner).specs;
}
if let Some(map) = specs.get(&parent.local_id)
&& let Some(&(level, src)) = map.get(&id)
{
return (Some(level), src);
}
}
}

(None, LintLevelSource::Default)
}

/// Fetch and return the user-visible lint level for the given lint at the given HirId.
#[instrument(level = "trace", skip(self, tcx), ret)]
#[instrument(level = "trace", skip(self, tcx, sess), ret)]
pub fn lint_level_id_at_node(
&self,
tcx: TyCtxt<'_>,
tcx: Option<TyCtxt<'_>>,
sess: &Session,
lint: LintId,
cur: HirId,
) -> (Level, LintLevelSource) {
assert!(
tcx.is_some() || cur == CRATE_HIR_ID,
"must pass in a tcx to access any level other than the root"
);
let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
let level = reveal_actual_level(level, &mut src, sess, lint, |lint| {
self.probe_for_lint_level(tcx, lint, cur)
});
(level, src)
Expand All @@ -166,14 +173,19 @@ impl ShallowLintLevelMap {
impl TyCtxt<'_> {
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(
Some(self),
self.sess,
LintId::of(lint),
id,
)
}
}

/// 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.
Expand Down
Loading

0 comments on commit 7b0e01d

Please sign in to comment.