xref: /llvm-project/clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp (revision b9ea09c4451cb8f33ffd41d22b7dd10e8a68c5c8)
1 //===--- FunctionSize.cpp - clang-tidy ------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "FunctionSizeCheck.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace tidy {
17 namespace readability {
18 
19 FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
20     : ClangTidyCheck(Name, Context),
21       LineThreshold(Options.get("LineThreshold", -1U)),
22       StatementThreshold(Options.get("StatementThreshold", 800U)),
23       BranchThreshold(Options.get("BranchThreshold", -1U)) {}
24 
25 void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
26   Options.store(Opts, "LineThreshold", LineThreshold);
27   Options.store(Opts, "StatementThreshold", StatementThreshold);
28   Options.store(Opts, "BranchThreshold", BranchThreshold);
29 }
30 
31 void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
32   Finder->addMatcher(
33       functionDecl(
34           unless(isInstantiated()),
35           forEachDescendant(
36               stmt(unless(compoundStmt()),
37                    hasParent(stmt(anyOf(compoundStmt(), ifStmt(),
38                                         anyOf(whileStmt(), doStmt(),
39                                               cxxForRangeStmt(), forStmt())))))
40                   .bind("stmt"))).bind("func"),
41       this);
42 }
43 
44 void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
45   const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
46 
47   FunctionInfo &FI = FunctionInfos[Func];
48 
49   // Count the lines including whitespace and comments. Really simple.
50   if (!FI.Lines) {
51     if (const Stmt *Body = Func->getBody()) {
52       SourceManager *SM = Result.SourceManager;
53       if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
54         FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
55                    SM->getSpellingLineNumber(Body->getLocStart());
56       }
57     }
58   }
59 
60   const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");
61   ++FI.Statements;
62 
63   // TODO: switch cases, gotos
64   if (isa<IfStmt>(Statement) || isa<WhileStmt>(Statement) ||
65       isa<ForStmt>(Statement) || isa<SwitchStmt>(Statement) ||
66       isa<DoStmt>(Statement) || isa<CXXForRangeStmt>(Statement))
67     ++FI.Branches;
68 }
69 
70 void FunctionSizeCheck::onEndOfTranslationUnit() {
71   // If we're above the limit emit a warning.
72   for (const auto &P : FunctionInfos) {
73     const FunctionInfo &FI = P.second;
74     if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
75         FI.Branches > BranchThreshold) {
76       diag(P.first->getLocation(),
77            "function '%0' exceeds recommended size/complexity thresholds")
78           << P.first->getNameAsString();
79     }
80 
81     if (FI.Lines > LineThreshold) {
82       diag(P.first->getLocation(),
83            "%0 lines including whitespace and comments (threshold %1)",
84            DiagnosticIDs::Note)
85           << FI.Lines << LineThreshold;
86     }
87 
88     if (FI.Statements > StatementThreshold) {
89       diag(P.first->getLocation(), "%0 statements (threshold %1)",
90            DiagnosticIDs::Note)
91           << FI.Statements << StatementThreshold;
92     }
93 
94     if (FI.Branches > BranchThreshold) {
95       diag(P.first->getLocation(), "%0 branches (threshold %1)",
96            DiagnosticIDs::Note)
97           << FI.Branches << BranchThreshold;
98     }
99   }
100 
101   FunctionInfos.clear();
102 }
103 
104 } // namespace readability
105 } // namespace tidy
106 } // namespace clang
107