diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b027872dd99cc..be3b3af5def41 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -148,6 +148,7 @@ declare_lint_pass! { UNUSED_MACROS, UNUSED_MACRO_RULES, UNUSED_MUT, + UNUSED_PUB_ITEMS_IN_BINARY, UNUSED_QUALIFICATIONS, UNUSED_UNSAFE, UNUSED_VARIABLES, @@ -784,6 +785,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 new file mode 100644 index 0000000000000..e199bb37ccffa --- /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 for both analysis phases. +/// +/// `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, + 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 7c6ab642b2736..14b9917744b19 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}; @@ -1203,13 +1202,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/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 06dd8cbc089b6..eba07f5b1b1f0 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1526,6 +1526,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/dead.rs b/compiler/rustc_passes/src/dead.rs index e229559ae9e5d..12aad3f193716 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,18 +13,21 @@ 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::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}; 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, @@ -819,12 +822,12 @@ fn has_allow_dead_code_or_lang_attr( fn maybe_record_as_seed<'tcx>( tcx: TyCtxt<'tcx>, owner_id: hir::OwnerId, - worklist: &mut Vec, + push_into_worklist: &mut impl FnMut(WorkItem), 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(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -835,11 +838,13 @@ fn maybe_record_as_seed<'tcx>( 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| WorkItem { - id: variant.def_id.expect_local(), - propagated: comes_from_allow, - own: comes_from_allow, - })); + for variant in adt.variants().iter() { + push_into_worklist(WorkItem { + id: variant.def_id.expect_local(), + propagated: comes_from_allow, + own: comes_from_allow, + }); + } } } DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => { @@ -854,7 +859,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(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -879,7 +884,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(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: comes_from_allow, own: comes_from_allow, @@ -891,7 +896,7 @@ fn maybe_record_as_seed<'tcx>( } DefKind::GlobalAsm => { // global_asm! is always live. - worklist.push(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -902,7 +907,7 @@ fn maybe_record_as_seed<'tcx>( // `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(WorkItem { + push_into_worklist(WorkItem { id: owner_id.def_id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, @@ -913,41 +918,55 @@ fn maybe_record_as_seed<'tcx>( } } -fn create_and_seed_worklist(tcx: TyCtxt<'_>) -> (Vec, 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| WorkItem { - id, - propagated: ComesFromAllowExpect::No, - own: ComesFromAllowExpect::No, - }) - }) - // Seed entry point - .chain(tcx.entry_fn(()).and_then(|(def_id, _)| { - def_id.as_local().map(|id| WorkItem { - id, +struct SeedWorklists { + worklist: Vec, + deferred_seeds: Vec, + 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(WorkItem { + id: local_def_id, + propagated: ComesFromAllowExpect::No, + own: ComesFromAllowExpect::No, + }); + } + + for (id, effective_vis) in tcx.effective_visibilities(()).iter() { + if effective_vis.is_public_at_level(Level::Reachable) { + deferred_seeds.push(WorkItem { + id: *id, propagated: ComesFromAllowExpect::No, own: ComesFromAllowExpect::No, - }) - })) - .collect::>(); + }); + } + } + let mut push_into_worklist = |work_item: WorkItem| match work_item.own { + ComesFromAllowExpect::Yes => deferred_seeds.push(work_item), + ComesFromAllowExpect::No => worklist.push(work_item), + }; 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, @@ -961,6 +980,28 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), propagated_comes_from_allow_expect: ComesFromAllowExpect::No, }; + 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); } @@ -988,7 +1029,7 @@ fn live_symbols_and_ignored_derived_traits( })); } - Ok((symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)) + Ok(()) } struct DeadItem { @@ -999,6 +1040,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, + target_lint: &'static Lint, live_symbols: &'tcx LocalDefIdSet, ignored_derived_traits: &'tcx LocalDefIdMap>, } @@ -1042,10 +1084,17 @@ 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) } + 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 @@ -1157,6 +1206,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, @@ -1193,6 +1243,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, @@ -1201,7 +1252,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( @@ -1268,17 +1319,55 @@ impl<'tcx> DeadVisitor<'tcx> { } fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { - let Ok((live_symbols, ignored_derived_traits)) = + let Ok(DeadCodeLivenessSummary { pre_deferred_seeding, 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() { + 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, + 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(); @@ -1357,7 +1446,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); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a916b4670fded..b463199b6b3b2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -298,6 +298,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)] @@ -920,6 +922,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] @@ -943,6 +947,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>, @@ -951,6 +957,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/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..7ea621c7bebbb 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2189,6 +2189,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/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..99710b18c6988 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/allow-dead-code-transitive.stderr @@ -0,0 +1,15 @@ +error: function `g` is never used + --> $DIR/allow-dead-code-transitive.rs:4:8 + | +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 + | +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..240e3ae02b5e1 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/basic.stderr @@ -0,0 +1,27 @@ +error: function `unused_pub_fn` is never used + --> $DIR/basic.rs:4: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/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..75b082dc018d8 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/deny-lint.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/deny-lint.rs:4: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/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..64d938db4e880 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/interaction-allow-dead-code.stderr @@ -0,0 +1,15 @@ +error: function `unused_pub_fn` is never used + --> $DIR/interaction-allow-dead-code.rs:4: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/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/library.rs b/tests/ui/lint/unused-pub-items-in-binary/library.rs new file mode 100644 index 0000000000000..c44409d837ab5 --- /dev/null +++ b/tests/ui/lint/unused-pub-items-in-binary/library.rs @@ -0,0 +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 + +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 + 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 +