From 52f7ad0787c0fb991a94ac83e74ded1a6cfb8034 Mon Sep 17 00:00:00 2001 From: Andrew Gazelka Date: Mon, 20 Oct 2025 17:41:22 -0700 Subject: [PATCH] excessive line count --- CHANGELOG.md | 1 + clippy_config/src/conf.rs | 3 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/excessive_file_length.rs | 99 +++++++++++++++++++ clippy_lints/src/lib.rs | 2 + .../ui-toml/excessive_file_length/clippy.toml | 1 + .../excessive_file_length.rs | 16 +++ .../excessive_file_length.stderr | 12 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/explicit_write_in_test.stderr | 0 10 files changed, 138 insertions(+) create mode 100644 clippy_lints/src/excessive_file_length.rs create mode 100644 tests/ui-toml/excessive_file_length/clippy.toml create mode 100644 tests/ui-toml/excessive_file_length/excessive_file_length.rs create mode 100644 tests/ui-toml/excessive_file_length/excessive_file_length.stderr delete mode 100644 tests/ui/explicit_write_in_test.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 37d46d349667..7adfa469c04b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6266,6 +6266,7 @@ Released 2018-09-13 [`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect [`error_impl_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#error_impl_error [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence +[`excessive_file_length`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_file_length [`excessive_nesting`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 9993765b4bdc..df9c7720624f 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -657,6 +657,9 @@ define_Conf! { /// The maximum amount of nesting a block can reside in #[lints(excessive_nesting)] excessive_nesting_threshold: u64 = 0, + /// The maximum number of lines a file can have + #[lints(excessive_file_length)] + excessive_file_length_threshold: u64 = 500, /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint #[lints(large_futures)] future_size_threshold: u64 = 16 * 1024, diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 375d179681da..eb5206ec4e38 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -152,6 +152,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO, crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO, crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO, + crate::excessive_file_length::EXCESSIVE_FILE_LENGTH_INFO, crate::excessive_nesting::EXCESSIVE_NESTING_INFO, crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, diff --git a/clippy_lints/src/excessive_file_length.rs b/clippy_lints/src/excessive_file_length.rs new file mode 100644 index 000000000000..fab1e7b2e0ff --- /dev/null +++ b/clippy_lints/src/excessive_file_length.rs @@ -0,0 +1,99 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::Crate; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::impl_lint_pass; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::{FileName, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for files that exceed a configurable line count threshold. + /// + /// Note: This is a restriction lint that is allow-by-default. You need to enable it + /// explicitly in your configuration and set the threshold in clippy.toml. + /// + /// ### Why restrict this? + /// Large files can be harder to navigate and understand. They often indicate that + /// the code could benefit from being split into multiple smaller, more focused modules. + /// This improves maintainability and makes the codebase easier to understand. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// excessive-file-length-threshold = 500 + /// ``` + /// + /// If a file exceeds this threshold, the lint will suggest splitting it into + /// smaller modules. + #[clippy::version = "1.84.0"] + pub EXCESSIVE_FILE_LENGTH, + restriction, + "checks for files that exceed a configurable line count threshold" +} + +impl_lint_pass!(ExcessiveFileLength => [EXCESSIVE_FILE_LENGTH]); + +pub struct ExcessiveFileLength { + pub excessive_file_length_threshold: u64, +} + +impl ExcessiveFileLength { + pub fn new(conf: &'static Conf) -> Self { + Self { + excessive_file_length_threshold: conf.excessive_file_length_threshold, + } + } +} + +impl EarlyLintPass for ExcessiveFileLength { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &Crate) { + // Only check if threshold is set (non-zero) + if self.excessive_file_length_threshold == 0 { + return; + } + + // Get all source files for the local crate + let source_map = cx.sess().source_map(); + + // We want to check each file in the current crate + for file in source_map.files().iter() { + // Only check files from the local crate, not external dependencies + if file.cnum != LOCAL_CRATE { + continue; + } + + // Skip non-real files (generated code, etc.) + if !matches!(&file.name, FileName::Real(_)) { + continue; + } + + // Count total lines in the file + let line_count = file.count_lines() as u64; + + // Check if file exceeds threshold + if line_count > self.excessive_file_length_threshold { + // Create a span at the start of the file for the lint + let span = Span::new( + file.start_pos, + file.start_pos, + SyntaxContext::root(), + None, + ); + + span_lint_and_help( + cx, + EXCESSIVE_FILE_LENGTH, + span, + format!( + "this file has too many lines ({}/{})", + line_count, self.excessive_file_length_threshold + ), + None, + "consider splitting this file into smaller modules to improve maintainability", + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a4ad9424b3eb..2c913bacd686 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -128,6 +128,7 @@ mod error_impl_error; mod escape; mod eta_reduction; mod excessive_bools; +mod excessive_file_length; mod excessive_nesting; mod exhaustive_items; mod exit; @@ -747,6 +748,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); + store.register_early_pass(move || Box::new(excessive_file_length::ExcessiveFileLength::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); diff --git a/tests/ui-toml/excessive_file_length/clippy.toml b/tests/ui-toml/excessive_file_length/clippy.toml new file mode 100644 index 000000000000..6f21d8bff01e --- /dev/null +++ b/tests/ui-toml/excessive_file_length/clippy.toml @@ -0,0 +1 @@ +excessive-file-length-threshold = 10 diff --git a/tests/ui-toml/excessive_file_length/excessive_file_length.rs b/tests/ui-toml/excessive_file_length/excessive_file_length.rs new file mode 100644 index 000000000000..dea9ec76bd62 --- /dev/null +++ b/tests/ui-toml/excessive_file_length/excessive_file_length.rs @@ -0,0 +1,16 @@ +#![warn(clippy::excessive_file_length)] +//~^ ERROR: this file has too many lines + +// This file should trigger the lint because it has more than 10 lines +// (configured in clippy.toml) + +fn main() { + println!("Line 1"); + println!("Line 2"); + println!("Line 3"); + println!("Line 4"); + println!("Line 5"); + println!("Line 6"); + println!("Line 7"); + // This file now has more than 10 lines and should trigger the lint +} diff --git a/tests/ui-toml/excessive_file_length/excessive_file_length.stderr b/tests/ui-toml/excessive_file_length/excessive_file_length.stderr new file mode 100644 index 000000000000..a73d0d0e38b4 --- /dev/null +++ b/tests/ui-toml/excessive_file_length/excessive_file_length.stderr @@ -0,0 +1,12 @@ +error: this file has too many lines (16/10) + --> tests/ui-toml/excessive_file_length/excessive_file_length.rs:1:1 + | +LL | #![warn(clippy::excessive_file_length)] + | ^ + | + = help: consider splitting this file into smaller modules to improve maintainability + = note: `-D clippy::excessive-file-length` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::excessive_file_length)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 2d9503c5ac53..ee2515d310a3 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + excessive-file-length-threshold excessive-nesting-threshold future-size-threshold ignore-interior-mutability @@ -143,6 +144,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + excessive-file-length-threshold excessive-nesting-threshold future-size-threshold ignore-interior-mutability @@ -240,6 +242,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni enforced-import-renames enum-variant-name-threshold enum-variant-size-threshold + excessive-file-length-threshold excessive-nesting-threshold future-size-threshold ignore-interior-mutability diff --git a/tests/ui/explicit_write_in_test.stderr b/tests/ui/explicit_write_in_test.stderr deleted file mode 100644 index e69de29bb2d1..000000000000