diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 3dfb86d422dd2..0a9f7803b5fa1 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -34,7 +34,7 @@ use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, ModuleConfig, TargetMachineFactoryConfig, TargetMachineFactoryFn, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::{CodegenLintLevels, CodegenResults, CompiledModule, ModuleCodegen}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError}; use rustc_metadata::EncodedMetadata; @@ -374,6 +374,7 @@ impl CodegenBackend for LlvmCodegenBackend { &self, sess: &Session, codegen_results: CodegenResults, + lint_levels: CodegenLintLevels, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { use rustc_codegen_ssa::back::link::link_binary; @@ -382,7 +383,7 @@ impl CodegenBackend for LlvmCodegenBackend { // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, outputs) + link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, lint_levels, outputs) } } diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 62db3d5a98cdd..2a84b4ea57f91 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 651485a18763c..e83381b73fb0c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -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; @@ -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; @@ -52,7 +55,7 @@ use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; use crate::{ - CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors, + CodegenLintLevels, CodegenResults, CompiledModule, CrateInfo, NativeLib, common, errors, looks_like_rust_object_file, }; @@ -70,6 +73,7 @@ pub fn link_binary( sess: &Session, archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: CodegenResults, + lint_levels: CodegenLintLevels, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); @@ -138,6 +142,7 @@ pub fn link_binary( crate_type, &out_filename, &codegen_results, + lint_levels, path.as_ref(), )?; } @@ -762,6 +767,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 @@ -772,6 +785,7 @@ fn link_natively( crate_type: CrateType, out_filename: &Path, codegen_results: &CodegenResults, + lint_levels: CodegenLintLevels, tmpdir: &Path, ) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); @@ -998,12 +1012,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 @@ -1045,8 +1059,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) = 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; diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f93cb52ea3ea2..2dfebf3e8f060 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -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 Diagnostic<'_, G> for LinkingFailed<'_> { @@ -359,7 +360,13 @@ impl 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. diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 7dc8ab38a9764..b208e16cd2682 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::Symbol; @@ -251,6 +256,7 @@ impl CodegenResults { sess: &Session, rlink_file: &Path, codegen_results: &CodegenResults, + lint_levels: CodegenLintLevels, outputs: &OutputFilenames, ) -> Result { let mut encoder = FileEncoder::new(rlink_file)?; @@ -260,6 +266,7 @@ impl CodegenResults { encoder.emit_raw_bytes(&RLINK_VERSION.to_be_bytes()); encoder.emit_str(sess.cfg_version); Encodable::encode(codegen_results, &mut encoder); + Encodable::encode(&lint_levels, &mut encoder); Encodable::encode(outputs, &mut encoder); encoder.finish().map_err(|(_path, err)| err) } @@ -267,7 +274,7 @@ impl CodegenResults { pub fn deserialize_rlink( sess: &Session, data: Vec, - ) -> Result<(Self, OutputFilenames), CodegenErrors> { + ) -> Result<(Self, CodegenLintLevels, OutputFilenames), CodegenErrors> { // The Decodable machinery is not used here because it panics if the input data is invalid // and because its internal representation may change. if !data.starts_with(RLINK_MAGIC) { @@ -298,7 +305,24 @@ impl CodegenResults { } let codegen_results = CodegenResults::decode(&mut decoder); + let lint_levels = CodegenLintLevels::decode(&mut decoder); let outputs = OutputFilenames::decode(&mut decoder); - Ok((codegen_results, outputs)) + Ok((codegen_results, lint_levels, 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, 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_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 7eab889edf05f..2118c9a1d00e6 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -19,7 +19,7 @@ use super::write::WriteBackendMethods; use crate::back::archive::ArArchiveBuilderBuilder; use crate::back::link::link_binary; use crate::back::write::TargetMachineFactoryFn; -use crate::{CodegenResults, ModuleCodegen}; +use crate::{CodegenLintLevels, CodegenResults, ModuleCodegen}; pub trait BackendTypes { type Value: CodegenObject; @@ -88,9 +88,10 @@ pub trait CodegenBackend { &self, sess: &Session, codegen_results: CodegenResults, + lint_levels: CodegenLintLevels, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, outputs) + link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, lint_levels, outputs) } /// Returns `true` if this backend can be safely called from multiple threads. diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 85eaae8a10466..fb03a26c6cade 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -645,27 +645,34 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { let rlink_data = fs::read(file).unwrap_or_else(|err| { dcx.emit_fatal(RlinkUnableToRead { err }); }); - let (codegen_results, outputs) = match CodegenResults::deserialize_rlink(sess, rlink_data) { - Ok((codegen, outputs)) => (codegen, outputs), - Err(err) => { - match err { - CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), - CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber), - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx - .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }), - CodegenErrors::RustcVersionMismatch { rustc_version } => { - dcx.emit_fatal(RLinkRustcVersionMismatch { - rustc_version, - current_version: sess.cfg_version, - }) - } - CodegenErrors::CorruptFile => { - dcx.emit_fatal(RlinkCorruptFile { file }); - } - }; - } - }; - if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() { + let (codegen_results, lint_levels, outputs) = + match CodegenResults::deserialize_rlink(sess, rlink_data) { + Ok((codegen, lints, outputs)) => (codegen, lints, outputs), + Err(err) => { + match err { + CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), + CodegenErrors::EmptyVersionNumber => { + dcx.emit_fatal(RLinkEmptyVersionNumber) + } + CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => { + dcx.emit_fatal(RLinkEncodingVersionMismatch { + version_array, + rlink_version, + }) + } + CodegenErrors::RustcVersionMismatch { rustc_version } => { + dcx.emit_fatal(RLinkRustcVersionMismatch { + rustc_version, + current_version: sess.cfg_version, + }) + } + CodegenErrors::CorruptFile => { + dcx.emit_fatal(RlinkCorruptFile { file }); + } + }; + } + }; + if compiler.codegen_backend.link(sess, codegen_results, lint_levels, &outputs).is_err() { FatalError.raise(); } } else { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index cd3a2fb70490f..c6a2ffe0379a6 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -3,8 +3,8 @@ use std::cell::{RefCell, RefMut}; use std::sync::Arc; use rustc_ast as ast; -use rustc_codegen_ssa::CodegenResults; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::{CodegenLintLevels, CodegenResults}; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{OnceLock, WorkerLocal}; @@ -117,6 +117,7 @@ impl<'tcx> Queries<'tcx> { pub struct Linker { dep_graph: DepGraph, output_filenames: Arc, + lint_levels: CodegenLintLevels, // Only present when incr. comp. is enabled. crate_hash: Option, ongoing_codegen: Box, @@ -144,6 +145,7 @@ impl Linker { Ok(Linker { dep_graph: tcx.dep_graph.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), + lint_levels: CodegenLintLevels::from_tcx(tcx), crate_hash: if tcx.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { @@ -187,6 +189,7 @@ impl Linker { sess, &rlink_file, &codegen_results, + self.lint_levels, &*self.output_filenames, ) .map_err(|error| { @@ -196,7 +199,7 @@ impl Linker { } let _timer = sess.prof.verbose_generic_activity("link_crate"); - codegen_backend.link(sess, codegen_results, &self.output_filenames) + codegen_backend.link(sess, codegen_results, self.lint_levels, &self.output_filenames) } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4b1dafbdbeea2..951a8a1861817 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -130,12 +130,17 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { !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 } @@ -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)) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 3b406c7e161a0..2f896f58aeafd 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, @@ -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. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index eb761bd6475fb..46f5bdd73e657 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 971d036fa69dd..0fe887423dd1a 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -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}; @@ -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, @@ -119,7 +119,7 @@ impl ShallowLintLevelMap { #[instrument(level = "trace", skip(self, tcx), ret)] fn probe_for_lint_level( &self, - tcx: TyCtxt<'_>, + tcx: Option>, id: LintId, start: HirId, ) -> (Option, LintLevelSource) { @@ -132,15 +132,17 @@ 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); + } } } @@ -148,15 +150,20 @@ impl ShallowLintLevelMap { } /// 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>, + 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) @@ -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. diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 0712af422adf3..e339abb3e2510 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -798,6 +798,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 with `--crate-type bin` + 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 074fe77324faf..a5a2c86b94cd4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2271,6 +2271,16 @@ 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) + }) + && hir_id != CRATE_HIR_ID + { + errors::UnusedNote::LinkerWarningsBinaryCrateOnly } 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 fbf25cb476aee..f9b79dac45225 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -762,6 +762,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 3a07c283e0ebb..ce2cdfd9b4fab 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1153,6 +1153,7 @@ symbols! { link_section, linkage, linker, + linker_messages, lint_reasons, literal, load, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 8e088682f92de..ba6af28f58a1f 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -592,6 +592,11 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // separate setting for the compiler. cargo.rustflag("-Cforce-frame-pointers=yes"); + // Ignore linker warnings on macOS for now. These are complicated to fix and don't affect the build. + if target.contains("apple-darwin") { + cargo.rustflag("-Alinker-messages"); + } + let html_root = format!("-Zcrate-attr=doc(html_root_url=\"{}/\")", builder.doc_rust_lang_org_channel(),); cargo.rustflag(&html_root); diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 494daeca96364..e09fd4e796bc3 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -319,6 +319,12 @@ impl Rustc { self } + /// Pass the `--verbose` flag. + pub fn verbose(&mut self) -> &mut Self { + self.cmd.arg("--verbose"); + self + } + /// `EXTRARSCXXFLAGS` pub fn extra_rs_cxx_flags(&mut self) -> &mut Self { // Adapted from tools.mk (trimmed): diff --git a/tests/run-make/link-args-order/rmake.rs b/tests/run-make/link-args-order/rmake.rs index b7ef8333267f2..fe0d02926eff4 100644 --- a/tests/run-make/link-args-order/rmake.rs +++ b/tests/run-make/link-args-order/rmake.rs @@ -15,8 +15,9 @@ fn main() { .link_args("b c") .link_args("d e") .link_arg("f") + .arg("--print=link-args") .run_fail() - .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); + .assert_stdout_contains(r#""a" "b" "c" "d" "e" "f""#); rustc() .input("empty.rs") .linker_flavor(linker) @@ -24,6 +25,7 @@ fn main() { .arg("-Zpre-link-args=b c") .arg("-Zpre-link-args=d e") .arg("-Zpre-link-arg=f") + .arg("--print=link-args") .run_fail() - .assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#); + .assert_stdout_contains(r#""a" "b" "c" "d" "e" "f""#); } diff --git a/tests/run-make/link-dedup/rmake.rs b/tests/run-make/link-dedup/rmake.rs index 6075f31095424..f38603dee8cbb 100644 --- a/tests/run-make/link-dedup/rmake.rs +++ b/tests/run-make/link-dedup/rmake.rs @@ -14,13 +14,13 @@ fn main() { rustc().input("depb.rs").run(); rustc().input("depc.rs").run(); - let output = rustc().input("empty.rs").cfg("bar").run_fail(); - output.assert_stderr_contains(needle_from_libs(&["testa", "testb", "testa"])); + let output = rustc().input("empty.rs").cfg("bar").arg("--print=link-args").run_fail(); + output.assert_stdout_contains(needle_from_libs(&["testa", "testb", "testa"])); - let output = rustc().input("empty.rs").run_fail(); - output.assert_stderr_contains(needle_from_libs(&["testa"])); - output.assert_stderr_not_contains(needle_from_libs(&["testb"])); - output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa", "testa"])); + let output = rustc().input("empty.rs").arg("--print=link-args").run_fail(); + output.assert_stdout_contains(needle_from_libs(&["testa"])); + output.assert_stdout_not_contains(needle_from_libs(&["testb"])); + output.assert_stdout_not_contains(needle_from_libs(&["testa", "testa", "testa"])); // Adjacent identical native libraries are no longer deduplicated if // they come from different crates (https://github.com/rust-lang/rust/pull/103311) // so the following will fail: diff --git a/tests/run-make/linker-warning/fake-linker.rs b/tests/run-make/linker-warning/fake-linker.rs new file mode 100644 index 0000000000000..30497eea2ccd9 --- /dev/null +++ b/tests/run-make/linker-warning/fake-linker.rs @@ -0,0 +1,13 @@ +fn main() { + for arg in std::env::args() { + match &*arg { + "run_make_info" => println!("foo"), + "run_make_warn" => eprintln!("warning: bar"), + "run_make_error" => { + eprintln!("error: baz"); + std::process::exit(1); + } + _ => (), + } + } +} 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/main.rs b/tests/run-make/linker-warning/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/linker-warning/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs new file mode 100644 index 0000000000000..fab4172a458ca --- /dev/null +++ b/tests/run-make/linker-warning/rmake.rs @@ -0,0 +1,45 @@ +use std::path::Path; + +use run_make_support::rfs::remove_file; +use run_make_support::{Rustc, rustc}; + +fn run_rustc() -> Rustc { + let mut rustc = rustc(); + rustc.arg("main.rs").output("main").linker("./fake-linker"); + rustc +} + +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") + .verbose() + .run_fail() + .assert_stderr_contains_regex("fake-linker.*run_make_error"); + run_rustc() + .link_arg("run_make_error") + .run_fail() + .assert_stderr_not_contains_regex("fake-linker.*run_make_error"); +} 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..e24cfa24c76cd 100644 --- a/tests/run-make/rust-lld-by-default-nightly/rmake.rs +++ b/tests/run-make/rust-lld-by-default-nightly/rmake.rs @@ -13,7 +13,7 @@ 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") + .verbose() .link_arg("-Wl,-v") .input("main.rs") .run(); @@ -25,7 +25,7 @@ fn main() { // But it can still be disabled by turning the linker feature off. let output = rustc() - .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") + .verbose() .link_arg("-Wl,-v") .arg("-Zlinker-features=-lld") .input("main.rs") @@ -38,6 +38,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..7976cd9c2c1dd 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") @@ -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..1549ec9f4cd33 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") + .arg("--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) + .arg("--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) + .arg("--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())) }