clang-tools  5.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 
20 class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
21  using Base = RecursiveASTVisitor<FunctionASTVisitor>;
22 
23 public:
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  LLVM_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 
52  return true;
53  }
54 
55  bool TraverseCompoundStmt(CompoundStmt *Node) {
56  // If this new compound statement is located in a compound statement, which
57  // is already nested NestingThreshold levels deep, record the start location
58  // of this new compound statement.
59  if (CurrentNestingLevel == Info.NestingThreshold)
60  Info.NestingThresholders.push_back(Node->getLocStart());
61 
62  ++CurrentNestingLevel;
63  Base::TraverseCompoundStmt(Node);
64  --CurrentNestingLevel;
65 
66  return true;
67  }
68 
69  bool TraverseDecl(Decl *Node) {
70  TrackedParent.push_back(false);
71  Base::TraverseDecl(Node);
72  TrackedParent.pop_back();
73  return true;
74  }
75 
76  struct FunctionInfo {
77  unsigned Lines = 0;
78  unsigned Statements = 0;
79  unsigned Branches = 0;
80  unsigned NestingThreshold = 0;
81  std::vector<SourceLocation> NestingThresholders;
82  };
84  std::vector<bool> TrackedParent;
85  unsigned CurrentNestingLevel = 0;
86 };
87 
88 FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
89  : ClangTidyCheck(Name, Context),
90  LineThreshold(Options.get("LineThreshold", -1U)),
91  StatementThreshold(Options.get("StatementThreshold", 800U)),
92  BranchThreshold(Options.get("BranchThreshold", -1U)),
93  ParameterThreshold(Options.get("ParameterThreshold", -1U)),
94  NestingThreshold(Options.get("NestingThreshold", -1U)) {}
95 
97  Options.store(Opts, "LineThreshold", LineThreshold);
98  Options.store(Opts, "StatementThreshold", StatementThreshold);
99  Options.store(Opts, "BranchThreshold", BranchThreshold);
100  Options.store(Opts, "ParameterThreshold", ParameterThreshold);
101  Options.store(Opts, "NestingThreshold", NestingThreshold);
102 }
103 
105  Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
106 }
107 
108 void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
109  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
110 
111  FunctionASTVisitor Visitor;
112  Visitor.Info.NestingThreshold = NestingThreshold;
113  Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
114  auto &FI = Visitor.Info;
115 
116  if (FI.Statements == 0)
117  return;
118 
119  // Count the lines including whitespace and comments. Really simple.
120  if (const Stmt *Body = Func->getBody()) {
121  SourceManager *SM = Result.SourceManager;
122  if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
123  FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
124  SM->getSpellingLineNumber(Body->getLocStart());
125  }
126  }
127 
128  unsigned ActualNumberParameters = Func->getNumParams();
129 
130  if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
131  FI.Branches > BranchThreshold ||
132  ActualNumberParameters > ParameterThreshold ||
133  !FI.NestingThresholders.empty()) {
134  diag(Func->getLocation(),
135  "function %0 exceeds recommended size/complexity thresholds")
136  << Func;
137  }
138 
139  if (FI.Lines > LineThreshold) {
140  diag(Func->getLocation(),
141  "%0 lines including whitespace and comments (threshold %1)",
142  DiagnosticIDs::Note)
143  << FI.Lines << LineThreshold;
144  }
145 
146  if (FI.Statements > StatementThreshold) {
147  diag(Func->getLocation(), "%0 statements (threshold %1)",
148  DiagnosticIDs::Note)
149  << FI.Statements << StatementThreshold;
150  }
151 
152  if (FI.Branches > BranchThreshold) {
153  diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
154  << FI.Branches << BranchThreshold;
155  }
156 
157  if (ActualNumberParameters > ParameterThreshold) {
158  diag(Func->getLocation(), "%0 parameters (threshold %1)",
159  DiagnosticIDs::Note)
160  << ActualNumberParameters << ParameterThreshold;
161  }
162 
163  for (const auto &CSPos : FI.NestingThresholders) {
164  diag(CSPos, "nesting level %0 starts here (threshold %1)",
165  DiagnosticIDs::Note)
166  << NestingThreshold + 1 << NestingThreshold;
167  }
168 }
169 
170 } // namespace readability
171 } // namespace tidy
172 } // namespace clang
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:275
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
SourceManager & SM
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
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
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.
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's name.
Definition: ClangTidy.cpp:416