blob: 138240a3d8738e8933cd940e60262244eb5b2b9c [file] [log] [blame]
Benjamin Kramer6e195422014-09-15 12:48:25 +00001//===--- 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
Alexander Kornienko1b677db2015-03-09 12:18:39 +000010#include "FunctionSizeCheck.h"
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +000011#include "clang/AST/RecursiveASTVisitor.h"
Benjamin Kramer6e195422014-09-15 12:48:25 +000012#include "clang/ASTMatchers/ASTMatchFinder.h"
13
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace tidy {
Alexander Kornienko35ddae42014-10-15 10:51:57 +000018namespace readability {
Benjamin Kramer6e195422014-09-15 12:48:25 +000019
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +000020class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
21 using Base = RecursiveASTVisitor<FunctionASTVisitor>;
22
23public:
24 bool TraverseStmt(Stmt *Node) {
25 if (!Node)
26 return Base::TraverseStmt(Node);
27
28 if (TrackedParent.back() && !isa<CompoundStmt>(Node))
29 ++Info.Statements;
30
31 switch (Node->getStmtClass()) {
32 case Stmt::IfStmtClass:
33 case Stmt::WhileStmtClass:
34 case Stmt::DoStmtClass:
35 case Stmt::CXXForRangeStmtClass:
36 case Stmt::ForStmtClass:
37 case Stmt::SwitchStmtClass:
38 ++Info.Branches;
39 // fallthrough
40 case Stmt::CompoundStmtClass:
41 TrackedParent.push_back(true);
42 break;
43 default:
44 TrackedParent.push_back(false);
45 break;
46 }
47
48 Base::TraverseStmt(Node);
49
50 TrackedParent.pop_back();
51 return true;
52 }
53
54 bool TraverseDecl(Decl *Node) {
55 TrackedParent.push_back(false);
56 Base::TraverseDecl(Node);
57 TrackedParent.pop_back();
58 return true;
59 }
60
61 struct FunctionInfo {
62 FunctionInfo() : Lines(0), Statements(0), Branches(0) {}
63 unsigned Lines;
64 unsigned Statements;
65 unsigned Branches;
66 };
67 FunctionInfo Info;
68 std::vector<bool> TrackedParent;
69};
70
Benjamin Kramer6e195422014-09-15 12:48:25 +000071FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
72 : ClangTidyCheck(Name, Context),
73 LineThreshold(Options.get("LineThreshold", -1U)),
74 StatementThreshold(Options.get("StatementThreshold", 800U)),
Alexander Kornienko91086442017-03-01 10:17:32 +000075 BranchThreshold(Options.get("BranchThreshold", -1U)),
76 ParameterThreshold(Options.get("ParameterThreshold", 6)) {}
Benjamin Kramer6e195422014-09-15 12:48:25 +000077
78void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
79 Options.store(Opts, "LineThreshold", LineThreshold);
80 Options.store(Opts, "StatementThreshold", StatementThreshold);
81 Options.store(Opts, "BranchThreshold", BranchThreshold);
Alexander Kornienko91086442017-03-01 10:17:32 +000082 Options.store(Opts, "ParameterThreshold", ParameterThreshold);
Benjamin Kramer6e195422014-09-15 12:48:25 +000083}
84
85void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +000086 Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
Benjamin Kramer6e195422014-09-15 12:48:25 +000087}
88
89void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
90 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
91
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +000092 FunctionASTVisitor Visitor;
93 Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
94 auto &FI = Visitor.Info;
95
96 if (FI.Statements == 0)
97 return;
Benjamin Kramer6e195422014-09-15 12:48:25 +000098
99 // Count the lines including whitespace and comments. Really simple.
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +0000100 if (const Stmt *Body = Func->getBody()) {
101 SourceManager *SM = Result.SourceManager;
102 if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
103 FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
104 SM->getSpellingLineNumber(Body->getLocStart());
Benjamin Kramer6e195422014-09-15 12:48:25 +0000105 }
106 }
107
Alexander Kornienko91086442017-03-01 10:17:32 +0000108 unsigned ActualNumberParameters = Func->getNumParams();
109
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +0000110 if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
Alexander Kornienko91086442017-03-01 10:17:32 +0000111 FI.Branches > BranchThreshold ||
112 ActualNumberParameters > ParameterThreshold) {
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +0000113 diag(Func->getLocation(),
114 "function %0 exceeds recommended size/complexity thresholds")
115 << Func;
Benjamin Kramer6e195422014-09-15 12:48:25 +0000116 }
117
Samuel Benzaquen7663d3b2016-05-25 16:19:23 +0000118 if (FI.Lines > LineThreshold) {
119 diag(Func->getLocation(),
120 "%0 lines including whitespace and comments (threshold %1)",
121 DiagnosticIDs::Note)
122 << FI.Lines << LineThreshold;
123 }
124
125 if (FI.Statements > StatementThreshold) {
126 diag(Func->getLocation(), "%0 statements (threshold %1)",
127 DiagnosticIDs::Note)
128 << FI.Statements << StatementThreshold;
129 }
130
131 if (FI.Branches > BranchThreshold) {
132 diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
133 << FI.Branches << BranchThreshold;
134 }
Alexander Kornienko91086442017-03-01 10:17:32 +0000135
136 if (ActualNumberParameters > ParameterThreshold) {
137 diag(Func->getLocation(), "%0 parameters (threshold %1)",
138 DiagnosticIDs::Note)
139 << ActualNumberParameters << ParameterThreshold;
140 }
Benjamin Kramer6e195422014-09-15 12:48:25 +0000141}
142
Alexander Kornienko35ddae42014-10-15 10:51:57 +0000143} // namespace readability
Benjamin Kramer6e195422014-09-15 12:48:25 +0000144} // namespace tidy
145} // namespace clang