clang-tools  6.0.0
FunctionSizeCheck.cpp
Go to the documentation of this file.
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/AST/RecursiveASTVisitor.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace readability {
19 namespace {
20 
21 class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
22  using Base = RecursiveASTVisitor<FunctionASTVisitor>;
23 
24 public:
25  bool TraverseStmt(Stmt *Node) {
26  if (!Node)
27  return Base::TraverseStmt(Node);
28 
29  if (TrackedParent.back() && !isa<CompoundStmt>(Node))
30  ++Info.Statements;
31 
32  switch (Node->getStmtClass()) {
33  case Stmt::IfStmtClass:
34  case Stmt::WhileStmtClass:
35  case Stmt::DoStmtClass:
36  case Stmt::CXXForRangeStmtClass:
37  case Stmt::ForStmtClass:
38  case Stmt::SwitchStmtClass:
39  ++Info.Branches;
40  LLVM_FALLTHROUGH;
41  case Stmt::CompoundStmtClass:
42  TrackedParent.push_back(true);
43  break;
44  default:
45  TrackedParent.push_back(false);
46  break;
47  }
48 
49  Base::TraverseStmt(Node);
50 
51  TrackedParent.pop_back();
52 
53  return true;
54  }
55 
56  bool TraverseCompoundStmt(CompoundStmt *Node) {
57  // If this new compound statement is located in a compound statement, which
58  // is already nested NestingThreshold levels deep, record the start location
59  // of this new compound statement.
60  if (CurrentNestingLevel == Info.NestingThreshold)
61  Info.NestingThresholders.push_back(Node->getLocStart());
62 
64  Base::TraverseCompoundStmt(Node);
66 
67  return true;
68  }
69 
70  bool TraverseDecl(Decl *Node) {
71  TrackedParent.push_back(false);
72  Base::TraverseDecl(Node);
73  TrackedParent.pop_back();
74  return true;
75  }
76 
77  struct FunctionInfo {
78  unsigned Lines = 0;
79  unsigned Statements = 0;
80  unsigned Branches = 0;
81  unsigned NestingThreshold = 0;
82  std::vector<SourceLocation> NestingThresholders;
83  };
84  FunctionInfo Info;
85  std::vector<bool> TrackedParent;
86  unsigned CurrentNestingLevel = 0;
87 };
88 
89 } // namespace
90 
91 FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
92  : ClangTidyCheck(Name, Context),
93  LineThreshold(Options.get("LineThreshold", -1U)),
94  StatementThreshold(Options.get("StatementThreshold", 800U)),
95  BranchThreshold(Options.get("BranchThreshold", -1U)),
96  ParameterThreshold(Options.get("ParameterThreshold", -1U)),
97  NestingThreshold(Options.get("NestingThreshold", -1U)) {}
98 
100  Options.store(Opts, "LineThreshold", LineThreshold);
101  Options.store(Opts, "StatementThreshold", StatementThreshold);
102  Options.store(Opts, "BranchThreshold", BranchThreshold);
103  Options.store(Opts, "ParameterThreshold", ParameterThreshold);
104  Options.store(Opts, "NestingThreshold", NestingThreshold);
105 }
106 
107 void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
108  Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
109 }
110 
111 void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
112  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
113 
114  FunctionASTVisitor Visitor;
115  Visitor.Info.NestingThreshold = NestingThreshold;
116  Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
117  auto &FI = Visitor.Info;
118 
119  if (FI.Statements == 0)
120  return;
121 
122  // Count the lines including whitespace and comments. Really simple.
123  if (const Stmt *Body = Func->getBody()) {
124  SourceManager *SM = Result.SourceManager;
125  if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
126  FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
127  SM->getSpellingLineNumber(Body->getLocStart());
128  }
129  }
130 
131  unsigned ActualNumberParameters = Func->getNumParams();
132 
133  if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
134  FI.Branches > BranchThreshold ||
135  ActualNumberParameters > ParameterThreshold ||
136  !FI.NestingThresholders.empty()) {
137  diag(Func->getLocation(),
138  "function %0 exceeds recommended size/complexity thresholds")
139  << Func;
140  }
141 
142  if (FI.Lines > LineThreshold) {
143  diag(Func->getLocation(),
144  "%0 lines including whitespace and comments (threshold %1)",
145  DiagnosticIDs::Note)
146  << FI.Lines << LineThreshold;
147  }
148 
149  if (FI.Statements > StatementThreshold) {
150  diag(Func->getLocation(), "%0 statements (threshold %1)",
151  DiagnosticIDs::Note)
152  << FI.Statements << StatementThreshold;
153  }
154 
155  if (FI.Branches > BranchThreshold) {
156  diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
157  << FI.Branches << BranchThreshold;
158  }
159 
160  if (ActualNumberParameters > ParameterThreshold) {
161  diag(Func->getLocation(), "%0 parameters (threshold %1)",
162  DiagnosticIDs::Note)
163  << ActualNumberParameters << ParameterThreshold;
164  }
165 
166  for (const auto &CSPos : FI.NestingThresholders) {
167  diag(CSPos, "nesting level %0 starts here (threshold %1)",
168  DiagnosticIDs::Note)
169  << NestingThreshold + 1 << NestingThreshold;
170  }
171 }
172 
173 } // namespace readability
174 } // namespace tidy
175 } // namespace clang
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:449
StringHandle Name
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
std::vector< bool > TrackedParent
std::vector< SourceLocation > NestingThresholders
unsigned Branches
std::map< std::string, std::string > OptionMap
FunctionInfo Info
unsigned CurrentNestingLevel
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
unsigned Statements
unsigned NestingThreshold
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416
unsigned Lines