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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6702,6 +6702,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 Expand Up @@ -7485,6 +7486,7 @@ Released 2018-09-13
[`enforced-import-renames`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforced-import-renames
[`enum-variant-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-name-threshold
[`enum-variant-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enum-variant-size-threshold
[`excessive-file-length-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-file-length-threshold
[`excessive-nesting-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#excessive-nesting-threshold
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
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 @@ -682,6 +682,16 @@ The maximum size of an enum's variant to avoid box suggestion
* [`large_enum_variant`](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)


## `excessive-file-length-threshold`
The maximum number of code lines (excluding comments and blanks) a file can have

**Default Value:** `500`

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


## `excessive-nesting-threshold`
The maximum amount of nesting a block can reside in

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 @@ -677,6 +677,9 @@ define_Conf! {
/// The maximum size of an enum's variant to avoid box suggestion
#[lints(large_enum_variant)]
enum_variant_size_threshold: u64 = 200,
/// The maximum number of code lines (excluding comments and blanks) a file can have
#[lints(excessive_file_length)]
excessive_file_length_threshold: u64 = 500,
/// The maximum amount of nesting a block can reside in
#[lints(excessive_nesting)]
excessive_nesting_threshold: u64 = 0,
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 @@ -156,6 +156,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
135 changes: 135 additions & 0 deletions clippy_lints/src/excessive_file_length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::tokenize_with_text;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::{HirId, Mod};
use rustc_lexer::TokenKind;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{FileName, Span};

declare_clippy_lint! {
/// ### What it does
/// Checks for source files that exceed a configurable number of lines of code.
///
/// ### Why restrict this?
/// Long source files can be difficult to navigate, understand, and maintain.
/// Splitting them into smaller, focused modules encourages better code
/// organization.
///
/// ### Example
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// excessive-file-length-threshold = 500
/// ```
///
/// A file exceeding the threshold should be refactored by extracting
/// logical sections into separate modules.
#[clippy::version = "1.96.0"]
pub EXCESSIVE_FILE_LENGTH,
restriction,
"source file exceeds a configurable line count"
}

impl_lint_pass!(ExcessiveFileLength => [EXCESSIVE_FILE_LENGTH]);

struct FileInfo {
hir_id: HirId,
span: Span,
code_lines: u64,
}

pub struct ExcessiveFileLength {
threshold: u64,
files: FxIndexMap<FileName, FileInfo>,
}

impl ExcessiveFileLength {
pub fn new(conf: &'static Conf) -> Self {
Self {
threshold: conf.excessive_file_length_threshold,
files: FxIndexMap::default(),
}
}
}

/// Counts lines that contain at least one code token, skipping lines that are
/// blank or only contain comments. Uses `rustc_lexer` for correct tokenization.
fn count_code_lines(src: &str) -> u64 {
let mut lines_with_code = vec![false; src.lines().count()];
let mut current_line = 0;

for (kind, text, _) in tokenize_with_text(src) {
let is_code = !matches!(
kind,
TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
);

if is_code {
lines_with_code[current_line] = true;
}

current_line += text.bytes().filter(|&b| b == b'\n').count();
}

lines_with_code.iter().filter(|&&has_code| has_code).count() as u64
}

impl<'tcx> LateLintPass<'tcx> for ExcessiveFileLength {
fn check_mod(&mut self, cx: &LateContext<'_>, module: &Mod<'_>, hir_id: HirId) {
if self.threshold == 0 {
return;
}

let span = module.spans.inner_span;
if span.from_expansion() {
return;
}

let source_map = cx.sess().source_map();
let file = source_map.lookup_source_file(span.lo());
if file.cnum != LOCAL_CRATE {
return;
}

let name = file.name.clone();
if !matches!(name, FileName::Real(_)) {
return;
}

if self.files.contains_key(&name) {
return;
}

let code_lines = file.src.as_ref().map_or(0, |src| count_code_lines(src));

self.files.entry(name).or_insert(FileInfo {
hir_id,
span,
code_lines,
});
}

fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
for info in self.files.values() {
if info.code_lines > self.threshold {
let over = info.code_lines - self.threshold;
span_lint_hir_and_then(
cx,
EXCESSIVE_FILE_LENGTH,
info.hir_id,
info.span,
format!(
"file has {} code lines ({over} over the limit of {})",
info.code_lines, self.threshold,
),
|diag| {
diag.help("consider splitting this file into smaller modules");
},
);
}
}
}
}
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,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 @@ -869,6 +870,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))),
Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)),
Box::new(|_| Box::new(manual_assert_eq::ManualAssertEq)),
Box::new(move |_| Box::new(excessive_file_length::ExcessiveFileLength::new(conf))),
// add late passes here, used by `cargo dev new_lint`
];
store.late_passes.extend(late_lints);
Expand Down
13 changes: 13 additions & 0 deletions tests/ui-toml/excessive_file_length/allow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@check-pass
#![allow(clippy::excessive_file_length)]
fn main() {}
fn one() {}
fn two() {}
fn three() {}
fn four() {}
fn five() {}
fn six() {}
fn seven() {}
fn eight() {}
fn nine() {}
fn ten() {}
19 changes: 19 additions & 0 deletions tests/ui-toml/excessive_file_length/below_threshold.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//@check-pass
// This file has many total lines but only a few code lines.
// Comment lines and blank lines should not be counted.
#![warn(clippy::excessive_file_length)]

// more comments
// to pad the total line count

fn main() {}

fn one() {}

fn two() {}

// even more comments here
// and here
// and here

fn three() {}
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
12 changes: 12 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,12 @@
#![warn(clippy::excessive_file_length)] //~ excessive_file_length
fn main() {}
fn one() {}
fn two() {}
fn three() {}
fn four() {}
fn five() {}
fn six() {}
fn seven() {}
fn eight() {}
fn nine() {}
fn ten() {}
18 changes: 18 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,18 @@
error: file has 12 code lines (2 over the limit of 10)
--> tests/ui-toml/excessive_file_length/excessive_file_length.rs:1:1
|
LL | / #![warn(clippy::excessive_file_length)]
LL | | fn main() {}
LL | | fn one() {}
LL | | fn two() {}
... |
LL | | fn nine() {}
LL | | fn ten() {}
| |___________^
|
= help: consider splitting this file into smaller modules
= 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

13 changes: 13 additions & 0 deletions tests/ui-toml/excessive_file_length/expect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@check-pass
#![expect(clippy::excessive_file_length)]
fn main() {}
fn one() {}
fn two() {}
fn three() {}
fn four() {}
fn five() {}
fn six() {}
fn seven() {}
fn eight() {}
fn nine() {}
fn ten() {}
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 @@ -49,6 +49,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 @@ -150,6 +151,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 @@ -251,6 +253,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
Loading