From e651309d5c5dd964d76e9c4d939310283f1a2d6c Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:55:33 +0800 Subject: [PATCH 1/5] Refactor dead code liveness plumbing --- compiler/rustc_middle/src/middle/dead_code.rs | 23 +++ compiler/rustc_middle/src/middle/mod.rs | 1 + compiler/rustc_middle/src/queries.rs | 14 +- compiler/rustc_passes/src/dead.rs | 137 ++++++++++++------ 4 files changed, 121 insertions(+), 54 deletions(-) create mode 100644 compiler/rustc_middle/src/middle/dead_code.rs diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs new file mode 100644 index 0000000000000..42b4c41fba5d3 --- /dev/null +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -0,0 +1,23 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::{DefId, LocalDefIdMap, LocalDefIdSet}; +use rustc_macros::HashStable; + +/// A single snapshot of dead-code liveness analysis state. +#[derive(Clone, Debug, HashStable)] +pub struct DeadCodeLivenessSnapshot { + pub live_symbols: LocalDefIdSet, + /// Maps each ADT to derived traits (for example `Debug` and `Clone`) that should be ignored + /// when checking for dead code diagnostics. + pub ignored_derived_traits: LocalDefIdMap>, +} + +/// Dead-code liveness data across primary and deferred seeding. +/// +/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds +/// are added. +/// `final_result` is the liveness snapshot after all seeds have been processed. +#[derive(Clone, Debug, HashStable)] +pub struct DeadCodeLivenessSummary { + pub pre_deferred_seeding: DeadCodeLivenessSnapshot, + pub final_result: DeadCodeLivenessSnapshot, +} diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs index 5739d132b66ca..7d4c34defa80d 100644 --- a/compiler/rustc_middle/src/middle/mod.rs +++ b/compiler/rustc_middle/src/middle/mod.rs @@ -1,4 +1,5 @@ pub mod codegen_fn_attrs; +pub mod dead_code; pub mod debugger_visualizer; pub mod deduced_param_attrs; pub mod dependency_format; diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index dd36cbf1b8f6f..e4eacaaa9538e 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -64,9 +64,7 @@ use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; -use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, -}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdSet, LocalModDefId}; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; use rustc_index::IndexVec; @@ -87,6 +85,7 @@ use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; +use crate::middle::dead_code::DeadCodeLivenessSummary; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; use crate::middle::deduced_param_attrs::DeducedParamAttrs; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; @@ -1199,13 +1198,8 @@ rustc_queries! { desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) } } - /// Return the live symbols in the crate for dead code check. - /// - /// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone). - query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx ( - LocalDefIdSet, - LocalDefIdMap>, - ), ErrorGuaranteed> { + /// Return dead-code liveness summary for the crate. + query live_symbols_and_ignored_derived_traits(_: ()) -> Result<&'tcx DeadCodeLivenessSummary, ErrorGuaranteed> { arena_cache desc { "finding live symbols in crate" } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d5ef9ee9335c5..11643e94c7f4e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,14 +13,15 @@ use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, QPath, find_attr}; +use rustc_hir::{self as hir, ForeignItemId, ItemId, Node, PatKind, QPath, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::middle::dead_code::{DeadCodeLivenessSnapshot, DeadCodeLivenessSummary}; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::DEAD_CODE; -use rustc_session::lint::{self, LintExpectationId}; +use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ @@ -750,23 +751,21 @@ fn has_allow_dead_code_or_lang_attr( fn maybe_record_as_seed<'tcx>( tcx: TyCtxt<'tcx>, owner_id: hir::OwnerId, - worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, + push_into_worklist: &mut impl FnMut(LocalDefId, ComesFromAllowExpect), unsolved_items: &mut Vec, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, owner_id.def_id); if let Some(comes_from_allow) = allow_dead_code { - worklist.push((owner_id.def_id, comes_from_allow)); + push_into_worklist(owner_id.def_id, comes_from_allow); } match tcx.def_kind(owner_id) { DefKind::Enum => { if let Some(comes_from_allow) = allow_dead_code { let adt = tcx.adt_def(owner_id); - worklist.extend( - adt.variants() - .iter() - .map(|variant| (variant.def_id.expect_local(), comes_from_allow)), - ); + for variant in adt.variants().iter() { + push_into_worklist(variant.def_id.expect_local(), comes_from_allow); + } } } DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => { @@ -781,7 +780,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) { - worklist.push((owner_id.def_id, comes_from_allow)); + push_into_worklist(owner_id.def_id, comes_from_allow); } // We only care about associated items of traits, @@ -802,7 +801,7 @@ fn maybe_record_as_seed<'tcx>( && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_def_id) { - worklist.push((owner_id.def_id, comes_from_allow)); + push_into_worklist(owner_id.def_id, comes_from_allow); } unsolved_items.push(owner_id.def_id); @@ -810,53 +809,61 @@ fn maybe_record_as_seed<'tcx>( } DefKind::GlobalAsm => { // global_asm! is always live. - worklist.push((owner_id.def_id, ComesFromAllowExpect::No)); + push_into_worklist(owner_id.def_id, ComesFromAllowExpect::No); } DefKind::Const { .. } => { if tcx.item_name(owner_id.def_id) == kw::Underscore { // `const _` is always live, as that syntax only exists for the side effects // of type checking and evaluating the constant expression, and marking them // as dead code would defeat that purpose. - worklist.push((owner_id.def_id, ComesFromAllowExpect::No)); + push_into_worklist(owner_id.def_id, ComesFromAllowExpect::No); } } _ => {} } } -fn create_and_seed_worklist( - tcx: TyCtxt<'_>, -) -> (Vec<(LocalDefId, ComesFromAllowExpect)>, Vec) { - let effective_visibilities = &tcx.effective_visibilities(()); - let mut unsolved_impl_item = Vec::new(); - let mut worklist = effective_visibilities - .iter() - .filter_map(|(&id, effective_vis)| { - effective_vis - .is_public_at_level(Level::Reachable) - .then_some(id) - .map(|id| (id, ComesFromAllowExpect::No)) - }) - // Seed entry point - .chain( - tcx.entry_fn(()) - .and_then(|(def_id, _)| def_id.as_local().map(|id| (id, ComesFromAllowExpect::No))), - ) - .collect::>(); +struct SeedWorklists { + worklist: Vec<(LocalDefId, ComesFromAllowExpect)>, + deferred_seeds: Vec<(LocalDefId, ComesFromAllowExpect)>, + unsolved_items: Vec, +} + +fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> SeedWorklists { + let mut unsolved_items = Vec::new(); + let mut deferred_seeds = Vec::new(); + let mut worklist = Vec::new(); + if let Some((def_id, _)) = tcx.entry_fn(()) + && let Some(local_def_id) = def_id.as_local() + { + worklist.push((local_def_id, ComesFromAllowExpect::No)); + } + + for (id, effective_vis) in tcx.effective_visibilities(()).iter() { + if effective_vis.is_public_at_level(Level::Reachable) { + deferred_seeds.push((*id, ComesFromAllowExpect::No)); + } + } + + let mut push_into_worklist = |def_id, comes_from_allow| match comes_from_allow { + ComesFromAllowExpect::Yes => deferred_seeds.push((def_id, ComesFromAllowExpect::Yes)), + ComesFromAllowExpect::No => worklist.push((def_id, ComesFromAllowExpect::No)), + }; let crate_items = tcx.hir_crate_items(()); for id in crate_items.owners() { - maybe_record_as_seed(tcx, id, &mut worklist, &mut unsolved_impl_item); + maybe_record_as_seed(tcx, id, &mut push_into_worklist, &mut unsolved_items); } - (worklist, unsolved_impl_item) + SeedWorklists { worklist, deferred_seeds, unsolved_items } } fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> Result<(LocalDefIdSet, LocalDefIdMap>), ErrorGuaranteed> { - let (worklist, mut unsolved_items) = create_and_seed_worklist(tcx); +) -> Result { + let SeedWorklists { worklist, deferred_seeds, mut unsolved_items } = + create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -869,6 +876,28 @@ fn live_symbols_and_ignored_derived_traits( ignore_variant_stack: vec![], ignored_derived_traits: Default::default(), }; + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + let pre_deferred_seeding = DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols.clone(), + ignored_derived_traits: symbol_visitor.ignored_derived_traits.clone(), + }; + + symbol_visitor.worklist.extend(deferred_seeds); + mark_live_symbols_and_ignored_derived_traits(&mut symbol_visitor, &mut unsolved_items)?; + + Ok(DeadCodeLivenessSummary { + pre_deferred_seeding, + final_result: DeadCodeLivenessSnapshot { + live_symbols: symbol_visitor.live_symbols, + ignored_derived_traits: symbol_visitor.ignored_derived_traits, + }, + }) +} + +fn mark_live_symbols_and_ignored_derived_traits( + symbol_visitor: &mut MarkSymbolVisitor<'_>, + unsolved_items: &mut Vec, +) -> Result<(), ErrorGuaranteed> { if let ControlFlow::Break(guar) = symbol_visitor.mark_live_symbols() { return Err(guar); } @@ -894,7 +923,7 @@ fn live_symbols_and_ignored_derived_traits( })); } - Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)) + Ok(()) } struct DeadItem { @@ -905,6 +934,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, live_symbols: &'tcx LocalDefIdSet, ignored_derived_traits: &'tcx LocalDefIdMap>, } @@ -948,7 +978,7 @@ impl<'tcx> DeadVisitor<'tcx> { fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option) { let hir_id = self.tcx.local_def_id_to_hir_id(id); - let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id); + let level = self.tcx.lint_level_at_node(self.target_lint, hir_id); (level.level, level.lint_id) } @@ -1107,7 +1137,7 @@ impl<'tcx> DeadVisitor<'tcx> { }; let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id); - self.tcx.emit_node_span_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag); + self.tcx.emit_node_span_lint(self.target_lint, hir_id, MultiSpan::from_spans(spans), diag); } fn warn_multiple( @@ -1174,17 +1204,36 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok((live_symbols, ignored_derived_traits)) = + let Ok(DeadCodeLivenessSummary { final_result, .. }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; }; - let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; - let module_items = tcx.hir_module_items(module); - for item in module_items.free_items() { + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + DEAD_CODE, + module, + &final_result.live_symbols, + &final_result.ignored_derived_traits, + module_items.free_items(), + module_items.foreign_items(), + ); +} + +fn lint_dead_code_or_unused_pub_items_in_binary<'tcx>( + tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, + module: LocalModDefId, + live_symbols: &'tcx LocalDefIdSet, + ignored_derived_traits: &'tcx LocalDefIdMap>, + free_items: impl Iterator, + foreign_items: impl Iterator, +) { + let mut visitor = DeadVisitor { tcx, target_lint, live_symbols, ignored_derived_traits }; + for item in free_items { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); @@ -1263,7 +1312,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { } } - for foreign_item in module_items.foreign_items() { + for foreign_item in foreign_items { visitor.check_definition(foreign_item.owner_id.def_id); } } From cc41061517ed676032d7aebf09b9d8eb1bef8043 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 19:56:03 +0800 Subject: [PATCH 2/5] Implement unused_pub_items_in_binary lint --- compiler/rustc_lint_defs/src/builtin.rs | 32 +++++++++++++++++++ compiler/rustc_middle/src/middle/dead_code.rs | 8 ++--- compiler/rustc_passes/src/dead.rs | 24 ++++++++++++-- .../allow-dead-code-transitive.rs | 14 ++++++++ .../allow-dead-code-transitive.stderr | 14 ++++++++ .../unused-pub-items-in-binary/allow-lint.rs | 12 +++++++ .../allow-lint.stderr | 14 ++++++++ .../lint/unused-pub-items-in-binary/basic.rs | 12 +++++++ .../unused-pub-items-in-binary/basic.stderr | 26 +++++++++++++++ .../unused-pub-items-in-binary/deny-lint.rs | 6 ++++ .../deny-lint.stderr | 14 ++++++++ .../interaction-allow-dead-code.rs | 7 ++++ .../interaction-allow-dead-code.stderr | 14 ++++++++ .../interaction-allow-unused-pub.rs | 7 ++++ .../interaction-allow-unused-pub.stderr | 14 ++++++++ .../interaction-deny-dead-code-only.rs | 7 ++++ .../ui/lint/unused-pub-items-in-binary/lib.rs | 7 ++++ .../unused-pub-items-in-binary/lib.stderr | 14 ++++++++ 18 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/basic.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr create mode 100644 tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/lib.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/lib.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 8af8f40d69f56..ee5a74ffd0faa 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -146,6 +146,7 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, + UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -782,6 +783,37 @@ declare_lint! { "detect unused, unexported items" } +declare_lint! { + /// The `unused_pub_items_in_binary` lint detects unused `pub` items in + /// executable crates. + /// + /// ### Example + /// + /// ```rust + /// #![deny(unused_pub_items_in_binary)] + /// + /// pub fn unused_pub_fn() {} + /// + /// fn main() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In executable crates, `pub` items are often implementation details + /// rather than part of an external API. This lint helps find those items + /// when they are never used. + /// + /// This lint only applies to executable crates. In library crates, public + /// items are considered part of the crate's API and are not reported by + /// this lint. + pub UNUSED_PUB_ITEMS_IN_BINARY, + Allow, + "detect public items in executable crates that are never used", + crate_level_only +} + declare_lint! { /// The `unused_attributes` lint detects attributes that were not used by /// the compiler. diff --git a/compiler/rustc_middle/src/middle/dead_code.rs b/compiler/rustc_middle/src/middle/dead_code.rs index 42b4c41fba5d3..e199bb37ccffa 100644 --- a/compiler/rustc_middle/src/middle/dead_code.rs +++ b/compiler/rustc_middle/src/middle/dead_code.rs @@ -11,11 +11,11 @@ pub struct DeadCodeLivenessSnapshot { pub ignored_derived_traits: LocalDefIdMap>, } -/// Dead-code liveness data across primary and deferred seeding. +/// Dead-code liveness data for both analysis phases. /// -/// `pre_deferred_seeding` is computed after the initial analysis pass but before deferred seeds -/// are added. -/// `final_result` is the liveness snapshot after all seeds have been processed. +/// `pre_deferred_seeding` is computed before reachable-public and `#[allow(dead_code)]` seeding, +/// and is used for lint `unused_pub_items_in_binary`. +/// `final_result` is the final liveness snapshot used for lint `dead_code`. #[derive(Clone, Debug, HashStable)] pub struct DeadCodeLivenessSummary { pub pre_deferred_seeding: DeadCodeLivenessSnapshot, diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 11643e94c7f4e..259395762a368 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -20,7 +20,8 @@ use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; use rustc_middle::ty::{self, AssocTag, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::lint::builtin::DEAD_CODE; +use rustc_session::config::CrateType; +use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_PUB_ITEMS_IN_BINARY}; use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; @@ -1204,7 +1205,7 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok(DeadCodeLivenessSummary { final_result, .. }) = + let Ok(DeadCodeLivenessSummary { pre_deferred_seeding, final_result }) = tcx.live_symbols_and_ignored_derived_traits(()).as_ref() else { return; @@ -1212,6 +1213,25 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let module_items = tcx.hir_module_items(module); + if tcx.crate_types().contains(&CrateType::Executable) { + let is_unused_pub = |def_id: LocalDefId| { + tcx.effective_visibilities(()).is_public_at_level(def_id, Level::Reachable) + && !pre_deferred_seeding.live_symbols.contains(&def_id) + }; + + lint_dead_code_or_unused_pub_items_in_binary( + tcx, + UNUSED_PUB_ITEMS_IN_BINARY, + module, + &pre_deferred_seeding.live_symbols, + &pre_deferred_seeding.ignored_derived_traits, + module_items.free_items().filter(|free_item| is_unused_pub(free_item.owner_id.def_id)), + module_items + .foreign_items() + .filter(|foreign_item| is_unused_pub(foreign_item.owner_id.def_id)), + ); + } + lint_dead_code_or_unused_pub_items_in_binary( tcx, DEAD_CODE, diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs new file mode 100644 index 0000000000000..0b939fd435b69 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn g() {} //~ ERROR function `g` is never used + +fn h() {} + +#[allow(dead_code)] +fn f() { + g(); + h(); +} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr new file mode 100644 index 0000000000000..4abff13557481 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr @@ -0,0 +1,14 @@ +error: function `g` is never used + --> $DIR/allow-dead-code-transitive.rs:4:8 + | +LL | pub fn g() {} + | ^ + | +note: the lint level is defined here + --> $DIR/allow-dead-code-transitive.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs new file mode 100644 index 0000000000000..af737ae6243c5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![allow(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() { + helper(); +} + +fn helper() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr new file mode 100644 index 0000000000000..ae864e50fcf22 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-lint.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/allow-lint.rs:10:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow-lint.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.rs b/tests/ui/lint/unused-pub-items-in-binary/basic.rs new file mode 100644 index 0000000000000..de76c55fbc683 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.rs @@ -0,0 +1,12 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +pub fn used_pub_fn() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() { + used_pub_fn(); +} diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr new file mode 100644 index 0000000000000..6b1c9a6588b8e --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr @@ -0,0 +1,26 @@ +error: function `unused_pub_fn` is never used + --> $DIR/basic.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/basic.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/basic.rs:8:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/basic.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs new file mode 100644 index 0000000000000..78fd6d059d8b7 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.rs @@ -0,0 +1,6 @@ +#![deny(unused_pub_items_in_binary)] +#![allow(dead_code)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr new file mode 100644 index 0000000000000..df690b046f0ac --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr @@ -0,0 +1,14 @@ +error: function `unused_pub_fn` is never used + --> $DIR/deny-lint.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny-lint.rs:1:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs new file mode 100644 index 0000000000000..aa1022b447bad --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![deny(unused_pub_items_in_binary)] + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used +fn unused_priv_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr new file mode 100644 index 0000000000000..e9d89d43bfd06 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr @@ -0,0 +1,14 @@ +error: function `unused_pub_fn` is never used + --> $DIR/interaction-allow-dead-code.rs:4:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/interaction-allow-dead-code.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs new file mode 100644 index 0000000000000..5564e3f96ccd8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.rs @@ -0,0 +1,7 @@ +#![allow(unused_pub_items_in_binary)] +#![deny(dead_code)] + +pub fn unused_pub_fn() {} +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr new file mode 100644 index 0000000000000..3c7924257967d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-unused-pub.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/interaction-allow-unused-pub.rs:5:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/interaction-allow-unused-pub.rs:2:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs new file mode 100644 index 0000000000000..0680ba7d4965d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-deny-dead-code-only.rs @@ -0,0 +1,7 @@ +//@ check-pass + +#![deny(dead_code)] + +pub fn unused_pub_fn() {} + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/lib.rs b/tests/ui/lint/unused-pub-items-in-binary/lib.rs new file mode 100644 index 0000000000000..4a51a741b2843 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/lib.rs @@ -0,0 +1,7 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] +#![crate_type = "lib"] + +pub fn unused_pub_fn() {} // Should NOT error because it's a library crate + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/lib.stderr b/tests/ui/lint/unused-pub-items-in-binary/lib.stderr new file mode 100644 index 0000000000000..c2c7fa044117d --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/lib.stderr @@ -0,0 +1,14 @@ +error: function `unused_priv_fn` is never used + --> $DIR/lib.rs:7:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lib.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From f12e960c28ac09530cf595cf4366230688ab548e Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 21:30:55 +0800 Subject: [PATCH 3/5] Emit unused attribute for unused_pub_items_in_binary used in libraries --- compiler/rustc_passes/src/check_attr.rs | 10 ++++++++ compiler/rustc_passes/src/errors.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + .../unused-pub-items-in-binary/lib.stderr | 14 ----------- .../{lib.rs => library.rs} | 4 +++- .../unused-pub-items-in-binary/library.stderr | 23 +++++++++++++++++++ 6 files changed, 39 insertions(+), 15 deletions(-) delete mode 100644 tests/ui/lint/unused-pub-items-in-binary/lib.stderr rename tests/ui/lint/unused-pub-items-in-binary/{lib.rs => library.rs} (86%) create mode 100644 tests/ui/lint/unused-pub-items-in-binary/library.stderr diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d0cd8511f09f9..78e1ba4f887ff 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1682,6 +1682,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } } + } else if hir_id == CRATE_HIR_ID + && attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) + && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item() + .is_some_and(|item| item.path == sym::unused_pub_items_in_binary) + }) + && !self.tcx.crate_types().contains(&CrateType::Executable) + { + errors::UnusedNote::NoEffectUnusedPubItemsInBinary } else if attr.has_name(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 2f2c2d0d65bf4..9fc7d0a8d5e6b 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -302,6 +302,8 @@ pub(crate) enum UnusedNote { "the `linker_messages` and `linker_info` lints can only be controlled at the root of a crate that needs to be linked" )] LinkerMessagesBinaryCrateOnly, + #[note("the `unused_pub_items_in_binary` lint has no effect in library crates")] + NoEffectUnusedPubItemsInBinary, } #[derive(Diagnostic)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 80d1c91c81ddc..b14352cd4ff2d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2187,6 +2187,7 @@ symbols! { unstable_removed, untagged_unions, unused_imports, + unused_pub_items_in_binary, unwind, unwind_attributes, unwind_safe_trait, diff --git a/tests/ui/lint/unused-pub-items-in-binary/lib.stderr b/tests/ui/lint/unused-pub-items-in-binary/lib.stderr deleted file mode 100644 index c2c7fa044117d..0000000000000 --- a/tests/ui/lint/unused-pub-items-in-binary/lib.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: function `unused_priv_fn` is never used - --> $DIR/lib.rs:7:4 - | -LL | fn unused_priv_fn() {} - | ^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/lib.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/unused-pub-items-in-binary/lib.rs b/tests/ui/lint/unused-pub-items-in-binary/library.rs similarity index 86% rename from tests/ui/lint/unused-pub-items-in-binary/lib.rs rename to tests/ui/lint/unused-pub-items-in-binary/library.rs index 4a51a741b2843..c44409d837ab5 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/lib.rs +++ b/tests/ui/lint/unused-pub-items-in-binary/library.rs @@ -1,7 +1,9 @@ #![deny(dead_code)] #![deny(unused_pub_items_in_binary)] +//~^ WARN unused attribute + #![crate_type = "lib"] -pub fn unused_pub_fn() {} // Should NOT error because it's a library crate +pub fn unused_pub_fn() {} // Should NOT error because it's a library fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used diff --git a/tests/ui/lint/unused-pub-items-in-binary/library.stderr b/tests/ui/lint/unused-pub-items-in-binary/library.stderr new file mode 100644 index 0000000000000..071da07e9b044 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.stderr @@ -0,0 +1,23 @@ +warning: unused attribute + --> $DIR/library.rs:2:1 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | + = note: the `unused_pub_items_in_binary` lint has no effect in library crates + = note: requested on the command line with `-W unused-attributes` + +error: function `unused_priv_fn` is never used + --> $DIR/library.rs:9:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/library.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + From 64d40eff1e1f013fbeff16fc95019e2f435a9102 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 6 Apr 2026 22:07:01 +0800 Subject: [PATCH 4/5] Emit a note to explain unused_pub_items_in_binary --- compiler/rustc_passes/src/dead.rs | 12 +++++++++++- compiler/rustc_passes/src/errors.rs | 10 ++++++++++ .../allow-dead-code-transitive.stderr | 1 + .../ui/lint/unused-pub-items-in-binary/basic.stderr | 1 + .../lint/unused-pub-items-in-binary/deny-lint.stderr | 1 + .../interaction-allow-dead-code.stderr | 1 + 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 259395762a368..3f00b06f40499 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -26,7 +26,8 @@ use rustc_session::lint::{self, Lint, LintExpectationId}; use rustc_span::{Symbol, kw}; use crate::errors::{ - ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment, + ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UnusedPubItemsInBinaryNote, + UselessAssignment, }; /// Any local definition that may call something in its body block should be explored. For example, @@ -983,6 +984,13 @@ impl<'tcx> DeadVisitor<'tcx> { (level.level, level.lint_id) } + fn unused_pub_items_in_binary_note(&self) -> Option { + self.target_lint + .name + .eq(UNUSED_PUB_ITEMS_IN_BINARY.name) + .then_some(UnusedPubItemsInBinaryNote) + } + // # Panics // All `dead_codes` must have the same lint level, otherwise we will intentionally ICE. // This is because we emit a multi-spanned lint using the lint level of the `dead_codes`'s @@ -1094,6 +1102,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), change_fields_suggestion: fields_suggestion, parent_info, ignored_derived_impls, @@ -1130,6 +1139,7 @@ impl<'tcx> DeadVisitor<'tcx> { descr, participle, name_list, + unused_pub_items_in_binary_note: self.unused_pub_items_in_binary_note(), parent_info, ignored_derived_impls, enum_variants_with_same_name, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 9fc7d0a8d5e6b..64c8dfc66cba9 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -950,6 +950,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] // only on DeadCodes since it's never a problem for tuple struct fields enum_variants_with_same_name: Vec>, #[subdiagnostic] @@ -973,6 +975,8 @@ pub(crate) enum MultipleDeadCodes<'tcx> { participle: &'tcx str, name_list: DiagSymbolList, #[subdiagnostic] + unused_pub_items_in_binary_note: Option, + #[subdiagnostic] change_fields_suggestion: ChangeFields, #[subdiagnostic] parent_info: Option>, @@ -981,6 +985,12 @@ pub(crate) enum MultipleDeadCodes<'tcx> { }, } +#[derive(Subdiagnostic)] +#[note( + "in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused" +)] +pub(crate) struct UnusedPubItemsInBinaryNote; + #[derive(Subdiagnostic)] #[note( "it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name" diff --git a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr index 4abff13557481..99710b18c6988 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr @@ -4,6 +4,7 @@ error: function `g` is never used LL | pub fn g() {} | ^ | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused note: the lint level is defined here --> $DIR/allow-dead-code-transitive.rs:2:9 | diff --git a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr index 6b1c9a6588b8e..240e3ae02b5e1 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/basic.stderr +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr @@ -4,6 +4,7 @@ error: function `unused_pub_fn` is never used LL | pub fn unused_pub_fn() {} | ^^^^^^^^^^^^^ | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused note: the lint level is defined here --> $DIR/basic.rs:2:9 | diff --git a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr index df690b046f0ac..75b082dc018d8 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr @@ -4,6 +4,7 @@ error: function `unused_pub_fn` is never used LL | pub fn unused_pub_fn() {} | ^^^^^^^^^^^^^ | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused note: the lint level is defined here --> $DIR/deny-lint.rs:1:9 | diff --git a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr index e9d89d43bfd06..64d938db4e880 100644 --- a/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr @@ -4,6 +4,7 @@ error: function `unused_pub_fn` is never used LL | pub fn unused_pub_fn() {} | ^^^^^^^^^^^^^ | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused note: the lint level is defined here --> $DIR/interaction-allow-dead-code.rs:2:9 | From 48cffd99848df2d1d2bddf8d056a02e4ed09938a Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 17 Apr 2026 09:25:33 +0800 Subject: [PATCH 5/5] Add test for no_mangle items --- .../no-mangle-items.rs | 14 ++++++++++ .../no-mangle-items.stderr | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs create mode 100644 tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs new file mode 100644 index 0000000000000..13db56a9e0ad8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.rs @@ -0,0 +1,14 @@ +#![deny(dead_code)] +#![deny(unused_pub_items_in_binary)] + +#[no_mangle] +pub fn pub_fn_no_mangle() {} + +pub fn unused_pub_fn() {} //~ ERROR function `unused_pub_fn` is never used + +#[no_mangle] +pub fn unused_priv_fn_no_mangle() {} + +fn unused_priv_fn() {} //~ ERROR function `unused_priv_fn` is never used + +fn main() {} diff --git a/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr new file mode 100644 index 0000000000000..18cb33abdf020 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/no-mangle-items.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/no-mangle-items.rs:7:8 + | +LL | pub fn unused_pub_fn() {} + | ^^^^^^^^^^^^^ + | + = note: in libraries, `pub` items can be used by dependent crates; in binaries, they cannot, so this `pub` item is unused +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:2:9 + | +LL | #![deny(unused_pub_items_in_binary)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function `unused_priv_fn` is never used + --> $DIR/no-mangle-items.rs:12:4 + | +LL | fn unused_priv_fn() {} + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/no-mangle-items.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 2 previous errors +