Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ document.

[500e0ff...master](https://github.com/rust-lang/rust-clippy/compare/500e0ff...master)

### New Lints

* Added [`too_many_lines_in_file`] to `restriction`
[#16675](https://github.com/rust-lang/rust-clippy/pull/16675)

## Rust 1.94

Current stable, released 2026-03-05
Expand Down Expand Up @@ -7168,6 +7173,7 @@ Released 2018-09-13
[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
[`too_many_lines_in_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines_in_file
[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
Expand Down Expand Up @@ -7400,6 +7406,7 @@ Released 2018-09-13
[`suppress-restriction-lint-in-const`]: https://doc.rust-lang.org/clippy/lint_configuration.html#suppress-restriction-lint-in-const
[`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
[`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
[`too-many-lines-in-file-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-in-file-threshold
[`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
[`trait-assoc-item-kinds-order`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trait-assoc-item-kinds-order
[`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
Expand Down
10 changes: 10 additions & 0 deletions book/src/lint_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,16 @@ The maximum number of argument a function or method can have
* [`too_many_arguments`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)


## `too-many-lines-in-file-threshold`
The maximum number of lines a source file can have

**Default Value:** `1000`

---
**Affected lints:**
* [`too_many_lines_in_file`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines_in_file)


## `too-many-lines-threshold`
The maximum number of lines a function or method can have

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 @@ -882,6 +882,9 @@ define_Conf! {
/// The maximum number of argument a function or method can have
#[lints(too_many_arguments)]
too_many_arguments_threshold: u64 = 7,
/// The maximum number of lines a source file can have
#[lints(too_many_lines_in_file)]
too_many_lines_in_file_threshold: u64 = 1000,
/// The maximum number of lines a function or method can have
#[lints(too_many_lines)]
too_many_lines_threshold: u64 = 100,
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 @@ -719,6 +719,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::time_subtraction::UNCHECKED_TIME_SUBTRACTION_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,
crate::too_many_lines_in_file::TOO_MANY_LINES_IN_FILE_INFO,
crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ mod tests_outside_test_module;
mod time_subtraction;
mod to_digit_is_some;
mod to_string_trait_impl;
mod too_many_lines_in_file;
mod toplevel_ref_arg;
mod trailing_empty_array;
mod trait_bounds;
Expand Down Expand Up @@ -517,6 +518,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
Box::new(|| Box::new(byte_char_slices::ByteCharSlice)),
Box::new(|| Box::new(cfg_not_test::CfgNotTest)),
Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())),
Box::new(move || Box::new(too_many_lines_in_file::TooManyLinesInFile::new(conf))),
// add early passes here, used by `cargo dev new_lint`
];
store.early_passes.extend(early_lints);
Expand Down
114 changes: 114 additions & 0 deletions clippy_lints/src/too_many_lines_in_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
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 source files that have a large number of lines of code.
/// Blank lines and lines containing only comments are not counted.
///
/// ### Why restrict this?
/// Large files are harder to navigate and understand. They often indicate
/// that a module has too many responsibilities and should be split into
/// smaller, more focused modules.
///
/// ### Example
/// A file with more lines than the configured threshold will trigger this lint.
/// The fix is to split it into smaller modules.
///
/// ### Configuration
/// The maximum number of lines is configured with `too-many-lines-in-file-threshold`
/// (default: `1000`).
#[clippy::version = "1.97.0"]
pub TOO_MANY_LINES_IN_FILE,
restriction,
"files with too many lines of code"
}

impl_lint_pass!(TooManyLinesInFile => [TOO_MANY_LINES_IN_FILE]);

pub struct TooManyLinesInFile {
threshold: u64,
}

impl TooManyLinesInFile {
pub fn new(conf: &'static Conf) -> Self {
Self {
threshold: conf.too_many_lines_in_file_threshold,
}
}
}

impl EarlyLintPass for TooManyLinesInFile {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
let source_map = cx.sess().source_map();
for file in source_map.files().iter() {
if file.cnum != LOCAL_CRATE {
continue;
}
if !matches!(file.name, FileName::Real(_)) {
continue;
}
let Some(src) = file.src.as_deref() else {
continue;
};

let mut line_count: u64 = 0;
let mut in_comment = false;
let mut threshold_exceeded_offset: Option<u32> = None;
let mut byte_offset: u32 = 0;

for mut line in src.lines() {
let line_start_offset = byte_offset;
byte_offset = byte_offset
.saturating_add(u32::try_from(line.len()).unwrap_or(u32::MAX))
.saturating_add(1); // +1 for newline
let mut code_in_line = false;
loop {
line = line.trim_start();
if line.is_empty() {
break;
}
if in_comment {
if let Some(i) = line.find("*/") {
line = &line[i + 2..];
in_comment = false;
continue;
}
} else {
let multi_idx = line.find("/*").unwrap_or(line.len());
let single_idx = line.find("//").unwrap_or(line.len());
code_in_line |= multi_idx > 0 && single_idx > 0;
if multi_idx < single_idx {
line = &line[multi_idx + 2..];
in_comment = true;
continue;
}
}
break;
}
if code_in_line {
line_count += 1;
if line_count == self.threshold + 1 {
threshold_exceeded_offset = Some(line_start_offset);
}
}
}

if line_count > self.threshold {
let start = file.start_pos + rustc_span::BytePos(threshold_exceeded_offset.unwrap_or(0));
let span = Span::new(start, start, SyntaxContext::root(), None);
span_lint(
cx,
TOO_MANY_LINES_IN_FILE,
span,
format!("this file has too many lines ({line_count}/{})", self.threshold),
);
}
}
}
}
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 @@ -83,6 +83,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
third-party
too-large-for-stack
too-many-arguments-threshold
too-many-lines-in-file-threshold
too-many-lines-threshold
trait-assoc-item-kinds-order
trivial-copy-size-limit
Expand Down Expand Up @@ -184,6 +185,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
third-party
too-large-for-stack
too-many-arguments-threshold
too-many-lines-in-file-threshold
too-many-lines-threshold
trait-assoc-item-kinds-order
trivial-copy-size-limit
Expand Down Expand Up @@ -285,6 +287,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
third-party
too-large-for-stack
too-many-arguments-threshold
too-many-lines-in-file-threshold
too-many-lines-threshold
trait-assoc-item-kinds-order
trivial-copy-size-limit
Expand Down
1 change: 1 addition & 0 deletions tests/ui-toml/too_many_lines_in_file/clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
too-many-lines-in-file-threshold = 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![warn(clippy::too_many_lines_in_file)]

fn a() {}
fn b() {}
fn c() {}
//~^ too_many_lines_in_file
fn d() {}

fn main() {}
11 changes: 11 additions & 0 deletions tests/ui-toml/too_many_lines_in_file/too_many_lines_in_file.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: this file has too many lines (6/3)
--> tests/ui-toml/too_many_lines_in_file/too_many_lines_in_file.rs:5:1
|
LL | fn c() {}
| ^
|
= note: `-D clippy::too-many-lines-in-file` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::too_many_lines_in_file)]`

error: aborting due to 1 previous error