From c0822ed9b8807ff350e24377e9d1999cc28edf0f Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 24 Dec 2023 18:38:00 -0500 Subject: [PATCH 1/9] show linker warnings even if it returns 0 --- compiler/rustc_codegen_ssa/messages.ftl | 2 ++ compiler/rustc_codegen_ssa/src/back/link.rs | 27 +++++++++++++++++-- compiler/rustc_codegen_ssa/src/base.rs | 4 ++- compiler/rustc_codegen_ssa/src/lib.rs | 1 + tests/run-make/linker-warning/fake-linker.sh | 17 ++++++++++++ tests/run-make/linker-warning/rmake.rs | 19 +++++++++++++ .../rust-lld-by-default-beta-stable/rmake.rs | 6 +---- .../rust-lld-by-default-nightly/rmake.rs | 17 ++++-------- .../run-make/rust-lld-custom-target/rmake.rs | 7 ++--- tests/run-make/rust-lld/rmake.rs | 9 ++++--- 10 files changed, 82 insertions(+), 27 deletions(-) create mode 100755 tests/run-make/linker-warning/fake-linker.sh diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 484f467068a14..ff49630de4cb9 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -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} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index df35b5e8426f1..378b6a50f7f77 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,6 +18,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::DiagCtxtHandle; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_macros::Diagnostic; 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; @@ -749,6 +750,14 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out } } +#[derive(Diagnostic)] +#[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 @@ -1028,8 +1037,22 @@ fn link_natively( sess.dcx().abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + + 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: ", ": "); + sess.dcx().emit_warn(LinkerOutput { inner: format!("linker stderr: {stderr}") }); + } + if !prog.stdout.is_empty() && sess.opts.verbose { + sess.dcx().emit_warn(LinkerOutput { + inner: format!("linker stdout: {}", escape_string(&prog.stdout)), + }); + } } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 544578b29f107..f14396c517c12 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -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 { @@ -927,6 +928,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); diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 65c6067c74066..332f2ffbc88ca 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -200,6 +200,7 @@ pub struct CrateInfo { pub dependency_formats: Lrc, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, + pub lint_levels: CodegenLintLevels, } #[derive(Encodable, Decodable)] diff --git a/tests/run-make/linker-warning/fake-linker.sh b/tests/run-make/linker-warning/fake-linker.sh new file mode 100755 index 0000000000000..ed4d472c3bfbd --- /dev/null +++ b/tests/run-make/linker-warning/fake-linker.sh @@ -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 diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index 4d21c5ea5690a..d2bb12aafcba9 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -10,6 +10,25 @@ 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, but only when --verbose is passed + run_rustc() + .link_arg("run_make_info") + .verbose() + .run() + .assert_stderr_contains("warning: linker stdout: foo"); + run_rustc() + .link_arg("run_make_info") + .run() + .assert_stderr_not_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") diff --git a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs b/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs index 2417a4274e4a0..85860fc95b829 100644 --- a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs +++ b/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs @@ -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().verbose().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{}", diff --git a/tests/run-make/rust-lld-by-default-nightly/rmake.rs b/tests/run-make/rust-lld-by-default-nightly/rmake.rs index 02bbe8227f032..4026af8306450 100644 --- a/tests/run-make/rust-lld-by-default-nightly/rmake.rs +++ b/tests/run-make/rust-lld-by-default-nightly/rmake.rs @@ -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().verbose().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,8 @@ 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().verbose().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 +30,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())) } diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs index a6f7c33793afe..17468223306da 100644 --- a/tests/run-make/rust-lld-custom-target/rmake.rs +++ b/tests/run-make/rust-lld-custom-target/rmake.rs @@ -15,7 +15,7 @@ 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") + .verbose() .crate_type("cdylib") .target("custom-target.json") .link_arg("-Wl,-v") @@ -29,7 +29,7 @@ fn main() { // But it can also be disabled via linker features. let output = rustc() - .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") + .verbose() .crate_type("cdylib") .target("custom-target.json") .arg("-Zlinker-features=-lld") @@ -44,6 +44,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())) } diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 1f311af1ed591..732bc6f6ba2f9 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -14,10 +14,10 @@ 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") + .verbose() .link_arg(linker_version_flag) .input("main.rs") .run(); @@ -29,8 +29,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) + .verbose() .arg("-Zlinker-features=-lld") .input("main.rs") .run(); @@ -43,8 +43,8 @@ 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) + .verbose() .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Zlinker-features=-lld") @@ -60,6 +60,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())) } From 537218afb2b7f6c8ef9793a52b45c73d1dcb4d4c Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 16 Oct 2024 01:14:10 -0400 Subject: [PATCH 2/9] make it possible to silence linker warnings with a crate-level attribute this was slightly complicated because codegen_ssa doesn't have access to a tcx. --- compiler/rustc_codegen_ssa/src/back/link.rs | 21 ++++++++----- compiler/rustc_codegen_ssa/src/lib.rs | 21 +++++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 34 +++++++++++++++++++++ compiler/rustc_lint_defs/src/lib.rs | 14 ++++++++- compiler/rustc_middle/src/lint.rs | 6 ++-- 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 378b6a50f7f77..80758aac13460 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,13 +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::Diagnostic; +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; @@ -30,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; @@ -750,7 +752,7 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out } } -#[derive(Diagnostic)] +#[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. @@ -1038,6 +1040,13 @@ fn link_natively( sess.dcx().abort_if_errors(); } + 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); @@ -1046,12 +1055,10 @@ fn link_natively( .strip_prefix("warning: ") .unwrap_or(&stderr) .replace(": warning: ", ": "); - sess.dcx().emit_warn(LinkerOutput { inner: format!("linker stderr: {stderr}") }); + lint(format!("linker stderr: {stderr}")); } if !prog.stdout.is_empty() && sess.opts.verbose { - sess.dcx().emit_warn(LinkerOutput { - inner: format!("linker stdout: {}", escape_string(&prog.stdout)), - }); + lint(format!("linker stdout: {}", escape_string(&prog.stdout))) } } Err(e) => { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 332f2ffbc88ca..40299ae263085 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -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; @@ -303,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) } + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9fc527a6a3ab3..5cdd18f5ea903 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -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. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7786d3eb59af8..9f4c5d89d0e92 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -161,7 +161,19 @@ impl ToStableHashKey for LintExpectation /// Setting for how to handle a lint. /// /// See: -#[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, diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 620d9f1c35790..cae980cde613f 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -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. From fda23843337472d03d9d789ce2b90f0efdb06ae0 Mon Sep 17 00:00:00 2001 From: jyn Date: Wed, 16 Oct 2024 01:35:01 -0400 Subject: [PATCH 3/9] warn on unused linker_messages warning attributes --- compiler/rustc_passes/messages.ftl | 3 +++ compiler/rustc_passes/src/check_attr.rs | 28 +++++++++++++++++++++++++ compiler/rustc_passes/src/errors.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + tests/ui/lint/linker-warning-bin.rs | 6 ++++++ tests/ui/lint/linker-warning.rs | 9 ++++++++ tests/ui/lint/linker-warning.stderr | 22 +++++++++++++++++++ 7 files changed, 71 insertions(+) create mode 100644 tests/ui/lint/linker-warning-bin.rs create mode 100644 tests/ui/lint/linker-warning.rs create mode 100644 tests/ui/lint/linker-warning.stderr diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3ed600a717f53..5d9718258b9bc 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -817,6 +817,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 diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 1b2b8ac5dd9e7..7df792c0f7963 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -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, @@ -2328,6 +2329,33 @@ 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 { + let err = match attr.style { + ast::AttrStyle::Outer => errors::OuterCrateLevelAttr, + ast::AttrStyle::Inner => errors::OuterCrateLevelAttr, + }; + self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, err); + 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 { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c3043ac60aa69..89dd10b3e77d7 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -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)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f5ce5dbc9d66a..aa22d0f706ddc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1189,6 +1189,7 @@ symbols! { link_section, linkage, linker, + linker_messages, lint_reasons, literal, load, diff --git a/tests/ui/lint/linker-warning-bin.rs b/tests/ui/lint/linker-warning-bin.rs new file mode 100644 index 0000000000000..ead0aa002bf28 --- /dev/null +++ b/tests/ui/lint/linker-warning-bin.rs @@ -0,0 +1,6 @@ +//@ build-pass +#![crate_type = "bin"] +#![warn(unused_attributes)] +#![allow(linker_messages)] + +fn main() {} diff --git a/tests/ui/lint/linker-warning.rs b/tests/ui/lint/linker-warning.rs new file mode 100644 index 0000000000000..10e3f56ab9590 --- /dev/null +++ b/tests/ui/lint/linker-warning.rs @@ -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() {} diff --git a/tests/ui/lint/linker-warning.stderr b/tests/ui/lint/linker-warning.stderr new file mode 100644 index 0000000000000..3a2c392fd0312 --- /dev/null +++ b/tests/ui/lint/linker-warning.stderr @@ -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 + From cedd4cad21298ca8b825ab3c56a84753176dc8e8 Mon Sep 17 00:00:00 2001 From: jyn Date: Fri, 29 Nov 2024 10:28:54 -0500 Subject: [PATCH 4/9] ignore linker errors on all platforms --- src/bootstrap/src/core/build_steps/test.rs | 2 ++ src/bootstrap/src/core/builder/cargo.rs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 9f3e4d9cc8995..38e056cbc14ea 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3577,6 +3577,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") diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index f9fb19ddb0952..031cbb7dcdbc8 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -269,6 +269,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. From b757663a00260e22799a1bbdc5d6ba603e4bb30d Mon Sep 17 00:00:00 2001 From: jyn Date: Sat, 21 Dec 2024 10:06:07 -0500 Subject: [PATCH 5/9] 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 --- compiler/rustc_errors/src/json.rs | 47 ++++++++++++++++--------- compiler/rustc_errors/src/json/tests.rs | 2 +- compiler/rustc_session/src/session.rs | 9 +++-- src/librustdoc/core.rs | 2 +- tests/run-make/linker-warning/rmake.rs | 29 +++++++++++++++ 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index c1188665a05f4..95c81fc5f4482 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -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>, #[setters(skip)] - sm: Lrc, + sm: Option>, fluent_bundle: Option>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, @@ -65,7 +65,7 @@ pub struct JsonEmitter { impl JsonEmitter { pub fn new( dst: Box, - sm: Lrc, + sm: Option>, 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, 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 { 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() } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 0de555b83d3ab..cebaf7c1cfe45 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -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, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 60f1154dc6d02..022c2aa880e25 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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 { 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, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 0dda3466a7173..78fee20aee36a 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -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, diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index d2bb12aafcba9..5967f5b32e9aa 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -44,4 +44,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""#); } From 26708aa941d64242e2cd8a8e29e0d16c875bcc2a Mon Sep 17 00:00:00 2001 From: jyn Date: Fri, 17 Jan 2025 08:00:28 -0500 Subject: [PATCH 6/9] Don't require `--verbose` to show linker stdout --- compiler/rustc_codegen_ssa/src/back/link.rs | 11 +++++++---- compiler/rustc_passes/src/check_attr.rs | 17 +++++++++++++---- tests/run-make/linker-warning/rmake.rs | 7 +------ .../rust-lld-by-default-beta-stable/rmake.rs | 5 +++-- .../rust-lld-by-default-nightly/rmake.rs | 5 ++--- tests/run-make/rust-lld-custom-target/rmake.rs | 2 -- tests/run-make/rust-lld/rmake.rs | 10 ++-------- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 80758aac13460..fae59a3b9dc86 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1040,6 +1040,11 @@ fn link_natively( sess.dcx().abort_if_errors(); } + let stderr = escape_string(&prog.stderr); + let stdout = escape_string(&prog.stdout); + info!("linker stderr:\n{}", &stderr); + info!("linker stdout:\n{}", &stdout); + let (level, src) = codegen_results.crate_info.lint_levels.linker_messages; let lint = |msg| { lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| { @@ -1049,16 +1054,14 @@ fn link_natively( 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))) + if !prog.stdout.is_empty() { + lint(format!("linker stdout: {}", stdout)) } } Err(e) => { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7df792c0f7963..3d88fe1a4d39c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2338,11 +2338,20 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) { if hir_id != CRATE_HIR_ID { - let err = match attr.style { - ast::AttrStyle::Outer => errors::OuterCrateLevelAttr, - ast::AttrStyle::Inner => errors::OuterCrateLevelAttr, + 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, + ), }; - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, err); return; } else { let never_needs_link = self diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index 5967f5b32e9aa..0a4c13c5f019e 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -14,16 +14,11 @@ fn main() { let warnings = run_rustc().link_arg("run_make_warn").run(); warnings.assert_stderr_contains("warning: linker stderr: bar"); - // Make sure it shows stdout, but only when --verbose is passed + // Make sure it shows stdout run_rustc() .link_arg("run_make_info") - .verbose() .run() .assert_stderr_contains("warning: linker stdout: foo"); - run_rustc() - .link_arg("run_make_info") - .run() - .assert_stderr_not_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) diff --git a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs b/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs index 85860fc95b829..d2f1e8b253a79 100644 --- a/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs +++ b/tests/run-make/rust-lld-by-default-beta-stable/rmake.rs @@ -12,7 +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().verbose().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{}", @@ -21,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())) } diff --git a/tests/run-make/rust-lld-by-default-nightly/rmake.rs b/tests/run-make/rust-lld-by-default-nightly/rmake.rs index 4026af8306450..a25a69b859bab 100644 --- a/tests/run-make/rust-lld-by-default-nightly/rmake.rs +++ b/tests/run-make/rust-lld-by-default-nightly/rmake.rs @@ -12,7 +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().verbose().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{}", @@ -20,8 +20,7 @@ fn main() { ); // But it can still be disabled by turning the linker feature off. - let output = - rustc().verbose().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{}", diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs index 17468223306da..a6f936ba6e5c5 100644 --- a/tests/run-make/rust-lld-custom-target/rmake.rs +++ b/tests/run-make/rust-lld-custom-target/rmake.rs @@ -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() - .verbose() .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() - .verbose() .crate_type("cdylib") .target("custom-target.json") .arg("-Zlinker-features=-lld") diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 732bc6f6ba2f9..1f098fdb1d910 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -17,7 +17,6 @@ fn main() { .arg("-Zlinker-features=+lld") .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") - .verbose() .link_arg(linker_version_flag) .input("main.rs") .run(); @@ -28,12 +27,8 @@ fn main() { ); // It should not be used when we explicitly opt-out of lld. - let output = rustc() - .link_arg(linker_version_flag) - .verbose() - .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{}", @@ -44,7 +39,6 @@ fn main() { // times to rustc. let output = rustc() .link_arg(linker_version_flag) - .verbose() .arg("-Clink-self-contained=+linker") .arg("-Zunstable-options") .arg("-Zlinker-features=-lld") From 0ff369c5a6359a586638254594cad328f8ce25d5 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 19 Jan 2025 09:26:08 -0500 Subject: [PATCH 7/9] Silence progress messages from MSVC link.exe These cannot be silenced with a CLI flag, and are not useful to warn about. They can still be viewed for debugging purposes using `RUSTC_LOG=rustc_codegen_ssa::link::back`. --- compiler/rustc_codegen_ssa/src/back/link.rs | 41 +++++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index fae59a3b9dc86..9688a5d47f2d9 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -992,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); @@ -1008,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(); @@ -1041,10 +1039,29 @@ fn link_natively( } let stderr = escape_string(&prog.stderr); - let stdout = escape_string(&prog.stdout); + 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| { @@ -1060,7 +1077,7 @@ fn link_natively( .replace(": warning: ", ": "); lint(format!("linker stderr: {stderr}")); } - if !prog.stdout.is_empty() { + if !stdout.is_empty() { lint(format!("linker stdout: {}", stdout)) } } From 7407c1268334a01142d1770dd5080ffeaa8eaa1a Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 20 Jan 2025 16:44:24 -0500 Subject: [PATCH 8/9] Ignore linker warnings on macOS for ui-fulldeps ld is showing things like this: ``` ld: ignoring duplicate libraries: '-lm' ``` I don't have time or a macbook that lets me investigate these. Just silence them for now. --- src/bootstrap/src/core/build_steps/test.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 38e056cbc14ea..95fbf76b79b7c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1862,12 +1862,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); } From 8b92619ce6b1c0ac41cb7fa97d6c550ee0b6785d Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 20 Jan 2025 19:09:20 -0500 Subject: [PATCH 9/9] Allow LLD version info to be either stderr or stdout --- tests/run-make/rust-lld/rmake.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-make/rust-lld/rmake.rs b/tests/run-make/rust-lld/rmake.rs index 1f098fdb1d910..76b15ab1799b9 100644 --- a/tests/run-make/rust-lld/rmake.rs +++ b/tests/run-make/rust-lld/rmake.rs @@ -55,6 +55,6 @@ fn main() { fn find_lld_version_in_logs(stderr: String) -> bool { let lld_version_re = - Regex::new(r"^warning: linker stdout: LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap(); + 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())) }