| author | bors <bors@rust-lang.org> 2026-05-01 17:15:17 UTC |
| committer | bors <bors@rust-lang.org> 2026-05-01 17:15:17 UTC |
| log | 67bcaa9c4b1b1015efae3d591155c79760b1e0a5 |
| tree | a68bc9ed6335b623fa0f71e845f45a4772f14a4b |
| parent | 0164cc139285d5452053bcc5a83da046a501ed61 |
| parent | 4e99b682ce126f0f8e736d110474baf0acb9c989 |
linker-messages is warn-by-default again
cc rust-lang/rust#136096
I ended up keeping it a lint and adding an option for lints to ignore `-Dwarnings` (there was already a lint that did that actually, it was just hard-coded in rustc_middle instead of in rustc_lint_defs like I'd expect). This allows people to actually see the warnings without them failing the build in CI.
11 files changed, 129 insertions(+), 18 deletions(-)
compiler/rustc_codegen_ssa/src/back/link.rs+27-6| ... | ... | @@ -692,6 +692,15 @@ fn is_windows_gnu_ld(sess: &Session) -> bool { |
| 692 | 692 | sess.target.is_like_windows |
| 693 | 693 | && !sess.target.is_like_msvc |
| 694 | 694 | && matches!(flavor, LinkerFlavor::Gnu(_, Lld::No)) |
| 695 | && sess.target.options.cfg_abi != CfgAbi::Llvm | |
| 696 | } | |
| 697 | ||
| 698 | fn is_windows_gnu_clang(sess: &Session) -> bool { | |
| 699 | let (_, flavor) = linker_and_flavor(sess); | |
| 700 | sess.target.is_like_windows | |
| 701 | && !sess.target.is_like_msvc | |
| 702 | && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, Lld::No)) | |
| 703 | && sess.target.options.cfg_abi == CfgAbi::Llvm | |
| 695 | 704 | } |
| 696 | 705 | |
| 697 | 706 | fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { |
| ... | ... | @@ -720,9 +729,10 @@ fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8] |
| 720 | 729 | escaped_stdout = for_each(&stdout, |line, output| { |
| 721 | 730 | // Hide some progress messages from link.exe that we don't care about. |
| 722 | 731 | // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 |
| 723 | if line.starts_with(" Creating library") | |
| 724 | || line.starts_with("Generating code") | |
| 725 | || line.starts_with("Finished generating code") | |
| 732 | let trimmed = line.trim_start(); | |
| 733 | if trimmed.starts_with("Creating library") | |
| 734 | || trimmed.starts_with("Generating code") | |
| 735 | || trimmed.starts_with("Finished generating code") | |
| 726 | 736 | { |
| 727 | 737 | linker_info += line; |
| 728 | 738 | linker_info += "\r\n"; |
| ... | ... | @@ -781,7 +791,18 @@ fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8] |
| 781 | 791 | *output += "\n" |
| 782 | 792 | } |
| 783 | 793 | }); |
| 784 | } | |
| 794 | } else if is_windows_gnu_clang(sess) { | |
| 795 | info!("inferred Windows Clang (GNU ABI)"); | |
| 796 | escaped_stderr = for_each(&stderr, |line, output| { | |
| 797 | if line.contains("argument unused during compilation: '-nolibc'") { | |
| 798 | linker_info += line; | |
| 799 | linker_info += "\n"; | |
| 800 | } else { | |
| 801 | *output += line; | |
| 802 | *output += "\n" | |
| 803 | } | |
| 804 | }); | |
| 805 | }; | |
| 785 | 806 | |
| 786 | 807 | let lint_msg = |msg| { |
| 787 | 808 | emit_lint_base( |
| ... | ... | @@ -805,10 +826,10 @@ fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8] |
| 805 | 826 | .strip_prefix("Warning: ") |
| 806 | 827 | .unwrap_or(&escaped_stderr) |
| 807 | 828 | .replace(": warning: ", ": "); |
| 808 | lint_msg(format!("linker stderr: {escaped_stderr}")); | |
| 829 | lint_msg(format!("linker stderr: {}", escaped_stderr.trim_end())); | |
| 809 | 830 | } |
| 810 | 831 | if !escaped_stdout.is_empty() { |
| 811 | lint_msg(format!("linker stdout: {}", escaped_stdout)) | |
| 832 | lint_msg(format!("linker stdout: {}", escaped_stdout.trim_end())) | |
| 812 | 833 | } |
| 813 | 834 | if !linker_info.is_empty() { |
| 814 | 835 | lint_info(linker_info); |
compiler/rustc_lint_defs/src/builtin.rs+11-2| ... | ... | @@ -193,6 +193,11 @@ declare_lint! { |
| 193 | 193 | reason: fcw!(FutureReleaseError #81670), |
| 194 | 194 | report_in_deps: true, |
| 195 | 195 | }; |
| 196 | // We exempt `FORBIDDEN_LINT_GROUPS` from `-Dwarnings` because it specifically | |
| 197 | // triggers in cases (like #80988) where you have `forbid(warnings)`, | |
| 198 | // and so if we turned that into an error, it'd defeat the purpose of the | |
| 199 | // future compatibility warning. | |
| 200 | ignore_deny_warnings | |
| 196 | 201 | } |
| 197 | 202 | |
| 198 | 203 | declare_lint! { |
| ... | ... | @@ -4073,8 +4078,12 @@ declare_lint! { |
| 4073 | 4078 | /// and actionable warning of similar quality to our other diagnostics. See this tracking |
| 4074 | 4079 | /// issue for more details: <https://github.com/rust-lang/rust/issues/136096>. |
| 4075 | 4080 | pub LINKER_MESSAGES, |
| 4076 | Allow, | |
| 4077 | "warnings emitted at runtime by the target-specific linker program" | |
| 4081 | Warn, | |
| 4082 | "warnings emitted at runtime by the target-specific linker program", | |
| 4083 | // Linker messages don't live up to the high standard people expect of rustc's errors. | |
| 4084 | // Prevent `-D warnings` from applying to it. | |
| 4085 | // It's still possible to pass `-D linker-messages` specifically. | |
| 4086 | ignore_deny_warnings | |
| 4078 | 4087 | } |
| 4079 | 4088 | |
| 4080 | 4089 | declare_lint! { |
compiler/rustc_lint_defs/src/lib.rs+4| ... | ... | @@ -343,6 +343,9 @@ pub struct Lint { |
| 343 | 343 | /// `true` if this lint should not be filtered out under any circustamces |
| 344 | 344 | /// (e.g. the unknown_attributes lint) |
| 345 | 345 | pub eval_always: bool, |
| 346 | ||
| 347 | /// `true` if this lint is unaffected by `-D warnings` | |
| 348 | pub ignore_deny_warnings: bool, | |
| 346 | 349 | } |
| 347 | 350 | |
| 348 | 351 | /// Extra information for a future incompatibility lint. |
| ... | ... | @@ -558,6 +561,7 @@ impl Lint { |
| 558 | 561 | feature_gate: None, |
| 559 | 562 | crate_level_only: false, |
| 560 | 563 | eval_always: false, |
| 564 | ignore_deny_warnings: false, | |
| 561 | 565 | } |
| 562 | 566 | } |
| 563 | 567 |
compiler/rustc_middle/src/lint.rs+36-10| ... | ... | @@ -7,8 +7,9 @@ use rustc_hir::{HirId, ItemLocalId}; |
| 7 | 7 | use rustc_lint_defs::EditionFcw; |
| 8 | 8 | use rustc_macros::{Decodable, Encodable, HashStable}; |
| 9 | 9 | use rustc_session::Session; |
| 10 | use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; | |
| 11 | use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; | |
| 10 | use rustc_session::lint::{ | |
| 11 | FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, builtin, | |
| 12 | }; | |
| 12 | 13 | use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw}; |
| 13 | 14 | use tracing::instrument; |
| 14 | 15 | |
| ... | ... | @@ -89,17 +90,30 @@ pub fn reveal_actual_level( |
| 89 | 90 | level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None)); |
| 90 | 91 | |
| 91 | 92 | // If we're about to issue a warning, check at the last minute for any |
| 92 | // directives against the warnings "lint". If, for example, there's an | |
| 93 | // directives against the `warnings` lint group. If, for example, there's an | |
| 93 | 94 | // `allow(warnings)` in scope then we want to respect that instead. |
| 94 | // | |
| 95 | // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically | |
| 96 | // triggers in cases (like #80988) where you have `forbid(warnings)`, | |
| 97 | // and so if we turned that into an error, it'd defeat the purpose of the | |
| 98 | // future compatibility warning. | |
| 99 | if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) { | |
| 95 | if level == Level::Warn { | |
| 100 | 96 | let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS)); |
| 101 | 97 | if let Some((configured_warning_level, configured_lint_id)) = warnings_level { |
| 102 | if configured_warning_level != Level::Warn { | |
| 98 | let respect_warnings_lint_group = match configured_warning_level { | |
| 99 | // -Wwarnings is a no-op. | |
| 100 | Level::Warn => false, | |
| 101 | // Some warnings cannot be denied from the `warnings` lint group, only individually. | |
| 102 | Level::Deny | Level::Forbid => !lint.lint.ignore_deny_warnings, | |
| 103 | // All warnings respect -Awarnings. | |
| 104 | Level::Allow => true, | |
| 105 | // Not sure what the right behavior is here, but, sure, why not. | |
| 106 | // See tests/ui/lint/rfc-2383-lint-reason/expect_warnings.rs. | |
| 107 | Level::Expect => true, | |
| 108 | Level::ForceWarn => { | |
| 109 | sess.dcx().span_delayed_bug( | |
| 110 | warnings_src.span(), | |
| 111 | "cannot --force-warn the `warnings` lint group", | |
| 112 | ); | |
| 113 | false | |
| 114 | } | |
| 115 | }; | |
| 116 | if respect_warnings_lint_group { | |
| 103 | 117 | level = configured_warning_level; |
| 104 | 118 | lint_id = configured_lint_id; |
| 105 | 119 | *src = warnings_src; |
| ... | ... | @@ -291,6 +305,18 @@ fn explain_lint_level_source( |
| 291 | 305 | } |
| 292 | 306 | } |
| 293 | 307 | } |
| 308 | ||
| 309 | if let Some(warnings_group) = sess | |
| 310 | .opts | |
| 311 | .lint_opts | |
| 312 | .iter() | |
| 313 | .find_map(|(opt, level)| (opt == "warnings").then_some(level)) | |
| 314 | .copied() | |
| 315 | && warnings_group >= Level::Deny | |
| 316 | && level < warnings_group | |
| 317 | { | |
| 318 | err.note_once(format!("the `{name}` lint ignores `-D warnings`")); | |
| 319 | } | |
| 294 | 320 | } |
| 295 | 321 | |
| 296 | 322 | /// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait. |
src/bootstrap/src/core/builder/cargo.rs+4| ... | ... | @@ -344,6 +344,10 @@ impl Cargo { |
| 344 | 344 | self.rustflags.arg("-Clink-arg=-gz"); |
| 345 | 345 | } |
| 346 | 346 | |
| 347 | // Ignore linker warnings for now. These are complicated to fix and don't affect the build. | |
| 348 | // FIXME: we should really investigate these... | |
| 349 | self.rustflags.arg("-Alinker-messages"); | |
| 350 | ||
| 347 | 351 | // Throughout the build Cargo can execute a number of build scripts |
| 348 | 352 | // compiling C/C++ code and we need to pass compilers, archivers, flags, etc |
| 349 | 353 | // obtained previously to those build scripts. |
src/tools/compiletest/src/directives/directive_names.rs+2| ... | ... | @@ -143,6 +143,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ |
| 143 | 143 | "ignore-x86_64", |
| 144 | 144 | "ignore-x86_64-apple-darwin", |
| 145 | 145 | "ignore-x86_64-pc-windows-gnu", |
| 146 | "ignore-x86_64-pc-windows-gnullvm", | |
| 146 | 147 | "ignore-x86_64-unknown-linux-gnu", |
| 147 | 148 | "incremental", |
| 148 | 149 | "known-bug", |
| ... | ... | @@ -268,6 +269,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ |
| 268 | 269 | "only-x86_64-apple-darwin", |
| 269 | 270 | "only-x86_64-fortanix-unknown-sgx", |
| 270 | 271 | "only-x86_64-pc-windows-gnu", |
| 272 | "only-x86_64-pc-windows-gnullvm", | |
| 271 | 273 | "only-x86_64-pc-windows-msvc", |
| 272 | 274 | "only-x86_64-unknown-linux-gnu", |
| 273 | 275 | "pp-exact", |
tests/run-make/linker-warning/deny-linker-lint.txt created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | error: linker stderr: bar | |
| 2 | | | |
| 3 | = note: requested on the command line with `-D linker-messages` | |
| 4 | ||
| 5 | error: aborting due to 1 previous error | |
| 6 |
tests/run-make/linker-warning/deny-warnings.txt created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | warning: linker stderr: bar | |
| 2 | | | |
| 3 | = note: requested on the command line with `-W linker-messages` | |
| 4 | = note: the `linker_messages` lint ignores `-D warnings` | |
| 5 | ||
| 6 | warning: 1 warning emitted | |
| 7 |
tests/run-make/linker-warning/rmake.rs+13| ... | ... | @@ -37,6 +37,19 @@ fn main() { |
| 37 | 37 | .run() |
| 38 | 38 | .assert_stderr_contains("warning: linker stdout: foo"); |
| 39 | 39 | |
| 40 | // Make sure it respects `-D linker-messages` | |
| 41 | let out = run_rustc().link_arg("run_make_warn").arg("-Dlinker-messages").run_fail(); | |
| 42 | out.assert_stderr_contains("error: linker stderr: bar"); | |
| 43 | diff() | |
| 44 | .expected_file("deny-linker-lint.txt") | |
| 45 | .actual_text("(linker warning)", out.stderr()) | |
| 46 | .run(); | |
| 47 | ||
| 48 | // Make sure that linker warnings don't fail the build when `-D warnings` is present. | |
| 49 | let out = run_rustc().link_arg("run_make_warn").arg("-Dwarnings").run(); | |
| 50 | out.assert_stderr_contains("warning: linker stderr: bar"); | |
| 51 | diff().expected_file("deny-warnings.txt").actual_text("(linker warning)", out.stderr()).run(); | |
| 52 | ||
| 40 | 53 | // Make sure we short-circuit this new path if the linker exits with an error |
| 41 | 54 | // (so the diagnostic is less verbose) |
| 42 | 55 | run_rustc().link_arg("run_make_error").run_fail().assert_stderr_contains("note: error: baz"); |
tests/ui/linking/no-libc-mingw-llvm.rs created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | //@ build-pass | |
| 2 | //@ only-x86_64-pc-windows-gnullvm | |
| 3 | #![allow(linker_messages)] | |
| 4 | #![warn(linker_info)] | |
| 5 | ||
| 6 | //~? WARN argument unused | |
| 7 | ||
| 8 | fn main() {} |
tests/ui/linking/no-libc-mingw-llvm.stderr created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | warning: clang: warning: argument unused during compilation: '-nolibc' [-Wunused-command-line-argument] | |
| 2 | ||
| 3 | | | |
| 4 | note: the lint level is defined here | |
| 5 | --> $DIR/no-libc-mingw-llvm.rs:4:9 | |
| 6 | | | |
| 7 | LL | #![warn(linker_info)] | |
| 8 | | ^^^^^^^^^^^ | |
| 9 | ||
| 10 | warning: 1 warning emitted | |
| 11 |