summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
blob: 54bdea7ea25d634bde692af85f925652f9ca4d64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_span::Span;

use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet_opt;

use super::TOO_MANY_LINES;

pub(super) fn check_fn(
    cx: &LateContext<'_>,
    kind: FnKind<'_>,
    span: Span,
    body: &hir::Body<'_>,
    too_many_lines_threshold: u64,
) {
    // Closures must be contained in a parent body, which will be checked for `too_many_lines`.
    // Don't check closures for `too_many_lines` to avoid duplicated lints.
    if matches!(kind, FnKind::Closure) || in_external_macro(cx.sess(), span) {
        return;
    }

    let code_snippet = match snippet_opt(cx, body.value.span) {
        Some(s) => s,
        _ => return,
    };
    let mut line_count: u64 = 0;
    let mut in_comment = false;
    let mut code_in_line;

    let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
        && code_snippet.as_bytes().first().copied() == Some(b'{')
        && code_snippet.as_bytes().last().copied() == Some(b'}')
    {
        // Removing the braces from the enclosing block
        &code_snippet[1..code_snippet.len() - 1]
    } else {
        &code_snippet
    }
    .trim() // Remove leading and trailing blank lines
    .lines();

    for mut line in function_lines {
        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;
                // Implies multi_idx is below line.len()
                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 > too_many_lines_threshold {
        span_lint(
            cx,
            TOO_MANY_LINES,
            span,
            &format!(
                "this function has too many lines ({}/{})",
                line_count, too_many_lines_threshold
            ),
        );
    }
}