]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/functions/too_many_lines.rs
008ef661b55f20a31c4e25e1e24e3a0de4b7a94a
[rust.git] / clippy_lints / src / functions / too_many_lines.rs
1 use rustc_hir as hir;
2 use rustc_hir::intravisit::FnKind;
3 use rustc_lint::{LateContext, LintContext};
4 use rustc_middle::lint::in_external_macro;
5 use rustc_span::Span;
6
7 use clippy_utils::diagnostics::span_lint;
8 use clippy_utils::source::snippet_opt;
9
10 use super::TOO_MANY_LINES;
11
12 pub(super) fn check_fn(
13     cx: &LateContext<'_>,
14     kind: FnKind<'tcx>,
15     span: Span,
16     body: &'tcx hir::Body<'_>,
17     too_many_lines_threshold: u64,
18 ) {
19     // Closures must be contained in a parent body, which will be checked for `too_many_lines`.
20     // Don't check closures for `too_many_lines` to avoid duplicated lints.
21     if matches!(kind, FnKind::Closure) || in_external_macro(cx.sess(), span) {
22         return;
23     }
24
25     let code_snippet = match snippet_opt(cx, body.value.span) {
26         Some(s) => s,
27         _ => return,
28     };
29     let mut line_count: u64 = 0;
30     let mut in_comment = false;
31     let mut code_in_line;
32
33     let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
34         && code_snippet.as_bytes().first().copied() == Some(b'{')
35         && code_snippet.as_bytes().last().copied() == Some(b'}')
36     {
37         // Removing the braces from the enclosing block
38         &code_snippet[1..code_snippet.len() - 1]
39     } else {
40         &code_snippet
41     }
42     .trim() // Remove leading and trailing blank lines
43     .lines();
44
45     for mut line in function_lines {
46         code_in_line = false;
47         loop {
48             line = line.trim_start();
49             if line.is_empty() {
50                 break;
51             }
52             if in_comment {
53                 if let Some(i) = line.find("*/") {
54                     line = &line[i + 2..];
55                     in_comment = false;
56                     continue;
57                 }
58             } else {
59                 let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
60                 let single_idx = line.find("//").unwrap_or_else(|| line.len());
61                 code_in_line |= multi_idx > 0 && single_idx > 0;
62                 // Implies multi_idx is below line.len()
63                 if multi_idx < single_idx {
64                     line = &line[multi_idx + 2..];
65                     in_comment = true;
66                     continue;
67                 }
68             }
69             break;
70         }
71         if code_in_line {
72             line_count += 1;
73         }
74     }
75
76     if line_count > too_many_lines_threshold {
77         span_lint(
78             cx,
79             TOO_MANY_LINES,
80             span,
81             &format!(
82                 "this function has too many lines ({}/{})",
83                 line_count, too_many_lines_threshold
84             ),
85         );
86     }
87 }