Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
99 changes: 99 additions & 0 deletions clippy_lints/src/excessive_file_length.rs
Original file line number Diff line number Diff line change
@@ -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(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use span_lint_hir_and_then here, that way you can use #[expect] & #[allow]. Now getting an HirId might be a bit tricky. If you can, maybe use the HirId of the mod item?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could probably use a Visitor to search the HIR before linting, either in check_crate + check_crate_post or in the same fn (just check_crate). From there you just store each module definition's name & HirId. There are other ways but AFAIK this should be the simplest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it'd be quicker to just use check_item + check_crate_post

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",
);
}
}
}
}
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions tests/ui-toml/excessive_file_length/clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
excessive-file-length-threshold = 10
16 changes: 16 additions & 0 deletions tests/ui-toml/excessive_file_length/excessive_file_length.rs
Original file line number Diff line number Diff line change
@@ -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
}
12 changes: 12 additions & 0 deletions tests/ui-toml/excessive_file_length/excessive_file_length.stderr
Original file line number Diff line number Diff line change
@@ -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

3 changes: 3 additions & 0 deletions tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Empty file.
Loading