Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b89deb4
Fix LegacyKeyValueFormat report from docker build: arm
homersimpsons Mar 3, 2026
eef3c4d
move never type tests to subdirectories and add some comments
WaffleLapkin Nov 13, 2025
a995d48
Split `is_msvc_link_exe` into a function
jyn514 Dec 12, 2025
bbc45c1
Split `report_linker_output` into its own function
jyn514 Dec 12, 2025
20404bf
Split out linker-info from linker-messages
jyn514 Dec 12, 2025
15e839e
Fallback to fat LTO for -Clto=thin in cg_gcc
bjorn3 Mar 6, 2026
05174fb
tidy never type `issue-*` tests
WaffleLapkin Nov 13, 2025
46cedc3
Use eager formatting in `#[derive(Subdiagnostic)]`
JonathanBrouwer Mar 6, 2026
2b0552f
Add new eager formatting API
JonathanBrouwer Mar 6, 2026
c2f1e9d
Remove stored args from diagnostics
JonathanBrouwer Mar 6, 2026
828c0c0
Remove `remove_arg` from diagnostics
JonathanBrouwer Mar 6, 2026
43221b4
Remove `eagerly_format` from `Diag`
JonathanBrouwer Mar 6, 2026
8c87d07
Remove `eagerly_format` from `DiagCtxt`
JonathanBrouwer Mar 6, 2026
10eb844
Remove `eagerly_format_to_string` from `DiagCtxt`
JonathanBrouwer Mar 6, 2026
e58ddb5
Fix `variable_references` logic to catch all variable references
JonathanBrouwer Mar 6, 2026
4e0b644
Fix newly detected subdiagnostics using variables from parent
JonathanBrouwer Mar 6, 2026
7baf2be
Fix uitests
JonathanBrouwer Mar 7, 2026
783c9d3
Rollup merge of #149937 - jyn514:linker-info, r=mati865
JonathanBrouwer Mar 7, 2026
feeeea9
Rollup merge of #153503 - bjorn3:cg_gcc_thin_lto_fallback, r=antoyo
JonathanBrouwer Mar 7, 2026
cecc5e7
Rollup merge of #153346 - WaffleLapkin:sometimes-test, r=jieyouxu
JonathanBrouwer Mar 7, 2026
ec5e4aa
Rollup merge of #153371 - homersimpsons:chore/fix-LegacyKeyValueForma…
JonathanBrouwer Mar 7, 2026
f540b27
Rollup merge of #153508 - JonathanBrouwer:improved_eager_format, r=Gu…
JonathanBrouwer Mar 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::collections::BTreeMap;

use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::formatting::DiagMessageAddArg;
use rustc_errors::{Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, listify, msg};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{
Expand Down Expand Up @@ -1309,12 +1310,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
&& !spans.is_empty()
{
let mut span: MultiSpan = spans.clone().into();
err.arg("ty", param_ty.to_string());
let msg = err.dcx.eagerly_format_to_string(
msg!("`{$ty}` is made to be an `FnOnce` closure here"),
err.args.iter(),
);
err.remove_arg("ty");
let msg = msg!("`{$ty}` is made to be an `FnOnce` closure here")
.arg("ty", param_ty.to_string())
.format();
for sp in spans {
span.push_span_label(sp, msg.clone());
}
Expand Down
28 changes: 14 additions & 14 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rustc_errors::codes::*;
use rustc_errors::formatting::DiagMessageAddArg;
use rustc_errors::{
Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans,
Subdiagnostic, msg,
Expand Down Expand Up @@ -763,15 +764,17 @@ pub(crate) struct FormatUnusedArg {
// form of diagnostic.
impl Subdiagnostic for FormatUnusedArg {
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
diag.arg("named", self.named);
let msg = diag.eagerly_format(msg!(
"{$named ->
[true] named argument
*[false] argument
} never used"
));
diag.remove_arg("named");
diag.span_label(self.span, msg);
diag.span_label(
self.span,
msg!(
"{$named ->
[true] named argument
*[false] argument
} never used"
)
.arg("named", self.named)
.format(),
);
}
}

Expand Down Expand Up @@ -946,17 +949,14 @@ pub(crate) struct AsmClobberNoReg {

impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AsmClobberNoReg {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
// eager translation as `span_labels` takes `AsRef<str>`
let lbl1 = dcx.eagerly_format_to_string(msg!("clobber_abi"), [].into_iter());
let lbl2 = dcx.eagerly_format_to_string(msg!("generic outputs"), [].into_iter());
Diag::new(
dcx,
level,
msg!("asm with `clobber_abi` must specify explicit registers for outputs"),
)
.with_span(self.spans.clone())
.with_span_labels(self.clobbers, &lbl1)
.with_span_labels(self.spans, &lbl2)
.with_span_labels(self.clobbers, "clobber_abi")
.with_span_labels(self.spans, "generic outputs")
}
}

Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_codegen_llvm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use std::ffi::CString;
use std::path::Path;

use rustc_data_structures::small_c_str::SmallCStr;
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, msg};
use rustc_errors::{
Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, format_diag_message, msg,
};
use rustc_macros::Diagnostic;
use rustc_span::Span;

Expand All @@ -24,7 +26,7 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
let diag: Diag<'_, G> = self.0.into_diag(dcx, level);
let (message, _) = diag.messages.first().expect("`LlvmError` with no message");
let message = dcx.eagerly_format_to_string(message.clone(), diag.args.iter());
let message = format_diag_message(message, &diag.args);
Diag::new(
dcx,
level,
Expand Down
194 changes: 147 additions & 47 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use rustc_errors::DiagCtxtHandle;
use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::attrs::NativeLibKind;
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_lint_defs::builtin::LINKER_INFO;
use rustc_macros::Diagnostic;
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
use rustc_metadata::{
Expand Down Expand Up @@ -60,7 +61,8 @@ use super::rpath::{self, RPathConfig};
use super::{apple, versioned_llvm_target};
use crate::base::needs_allocator_shim_for_linking;
use crate::{
CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, looks_like_rust_object_file,
CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors,
looks_like_rust_object_file,
};

pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
Expand Down Expand Up @@ -672,6 +674,147 @@ struct LinkerOutput {
inner: String,
}

fn is_msvc_link_exe(sess: &Session) -> bool {
let (linker_path, flavor) = linker_and_flavor(sess);
sess.target.is_like_msvc
&& flavor == LinkerFlavor::Msvc(Lld::No)
// Match exactly "link.exe"
&& linker_path.to_str() == Some("link.exe")
}

fn is_macos_ld(sess: &Session) -> bool {
let (_, flavor) = linker_and_flavor(sess);
sess.target.is_like_darwin && matches!(flavor, LinkerFlavor::Darwin(_, Lld::No))
}

fn is_windows_gnu_ld(sess: &Session) -> bool {
let (_, flavor) = linker_and_flavor(sess);
sess.target.is_like_windows
&& !sess.target.is_like_msvc
&& matches!(flavor, LinkerFlavor::Gnu(_, Lld::No))
}

fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) {
let mut escaped_stderr = escape_string(&stderr);
let mut escaped_stdout = escape_string(&stdout);
let mut linker_info = String::new();

info!("linker stderr:\n{}", &escaped_stderr);
info!("linker stdout:\n{}", &escaped_stdout);

fn for_each(bytes: &[u8], mut f: impl FnMut(&str, &mut String)) -> String {
let mut output = String::new();
if let Ok(str) = str::from_utf8(bytes) {
info!("line: {str}");
output = String::with_capacity(str.len());
for line in str.lines() {
f(line.trim(), &mut output);
}
}
escape_string(output.trim().as_bytes())
}

if is_msvc_link_exe(sess) {
info!("inferred MSVC link.exe");

escaped_stdout = for_each(&stdout, |line, output| {
// 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 line.starts_with(" Creating library")
|| line.starts_with("Generating code")
|| line.starts_with("Finished generating code")
{
linker_info += line;
linker_info += "\r\n";
} else {
*output += line;
*output += "\r\n"
}
});
} else if is_macos_ld(sess) {
info!("inferred macOS LD");

// FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113
let deployment_mismatch = |line: &str| {
line.starts_with("ld: warning: object file (")
&& line.contains("was built for newer 'macOS' version")
&& line.contains("than being linked")
};
// FIXME: This is a real warning we would like to show, but it hits too many crates
// to want to turn it on immediately.
let search_path = |line: &str| {
line.starts_with("ld: warning: search path '") && line.ends_with("' not found")
};
escaped_stderr = for_each(&stderr, |line, output| {
// This duplicate library warning is just not helpful at all.
if line.starts_with("ld: warning: ignoring duplicate libraries: ")
|| deployment_mismatch(line)
|| search_path(line)
{
linker_info += line;
linker_info += "\n";
} else {
*output += line;
*output += "\n"
}
});
} else if is_windows_gnu_ld(sess) {
info!("inferred Windows GNU LD");

let mut saw_exclude_symbol = false;
// See https://github.com/rust-lang/rust/issues/112368.
// FIXME: maybe check that binutils is older than 2.40 before downgrading this warning?
let exclude_symbols = |line: &str| {
line.starts_with("Warning: .drectve `-exclude-symbols:")
&& line.ends_with("' unrecognized")
};
escaped_stderr = for_each(&stderr, |line, output| {
if exclude_symbols(line) {
saw_exclude_symbol = true;
linker_info += line;
linker_info += "\n";
} else if saw_exclude_symbol && line == "Warning: corrupt .drectve at end of def file" {
linker_info += line;
linker_info += "\n";
} else {
*output += line;
*output += "\n"
}
});
}

let lint_msg = |msg| {
diag_lint_level(
sess,
LINKER_MESSAGES,
levels.linker_messages,
None,
LinkerOutput { inner: msg },
);
};
let lint_info = |msg| {
diag_lint_level(sess, LINKER_INFO, levels.linker_info, None, LinkerOutput { inner: msg });
};

if !escaped_stderr.is_empty() {
// We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present.
escaped_stderr =
escaped_stderr.strip_prefix("warning: ").unwrap_or(&escaped_stderr).to_owned();
// Windows GNU LD prints uppercase Warning
escaped_stderr = escaped_stderr
.strip_prefix("Warning: ")
.unwrap_or(&escaped_stderr)
.replace(": warning: ", ": ");
lint_msg(format!("linker stderr: {escaped_stderr}"));
}
if !escaped_stdout.is_empty() {
lint_msg(format!("linker stdout: {}", escaped_stdout))
}
if !linker_info.is_empty() {
lint_info(linker_info);
}
}

/// 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 @@ -860,11 +1003,6 @@ 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);
Expand All @@ -884,7 +1022,7 @@ fn link_natively(
if let Some(code) = prog.status.code() {
// 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) {
if is_msvc_link_exe(sess) && (code < 1000 || code > 9999) {
let is_vs_installed = find_msvc_tools::find_vs_version().is_ok();
let has_linker =
find_msvc_tools::find_tool(sess.target.arch.desc(), "link.exe")
Expand Down Expand Up @@ -916,46 +1054,8 @@ fn link_natively(
sess.dcx().abort_if_errors();
}

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 = crate_info.lint_levels.linker_messages;
let lint = |msg| {
diag_lint_level(sess, LINKER_MESSAGES, level, None, LinkerOutput { inner: msg });
};

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))
}
info!("reporting linker output: flavor={flavor:?}");
report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr);
}
Err(e) => {
let linker_not_found = e.kind() == io::ErrorKind::NotFound;
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use rustc_data_structures::unord::UnordMap;
use rustc_hir::CRATE_HIR_ID;
use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind};
use rustc_hir::def_id::CrateNum;
use rustc_lint_defs::builtin::LINKER_INFO;
use rustc_macros::{Decodable, Encodable};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::WorkProduct;
Expand Down Expand Up @@ -364,10 +365,14 @@ impl CompiledModules {
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
pub struct CodegenLintLevels {
linker_messages: LevelAndSource,
linker_info: LevelAndSource,
}

impl CodegenLintLevels {
pub fn from_tcx(tcx: TyCtxt<'_>) -> Self {
Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) }
Self {
linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID),
linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID),
}
}
}
5 changes: 3 additions & 2 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ fn build_error_for_const_call<'tcx>(
}
}
CallKind::FnCall { fn_trait_id, self_ty } => {
let kind = ccx.const_kind();
let note = match self_ty.kind() {
FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id);
Expand All @@ -274,8 +275,8 @@ fn build_error_for_const_call<'tcx>(

Some(errors::NonConstClosureNote::FnDef { span })
}
FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
Closure(..) => Some(errors::NonConstClosureNote::Closure),
FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr { kind }),
Closure(..) => Some(errors::NonConstClosureNote::Closure { kind }),
_ => None,
};

Expand Down
Loading
Loading