10#include "clang/AST/RecursiveASTVisitor.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "llvm/ADT/BitVector.h"
23 bool VisitVarDecl(VarDecl *VD) {
27 !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)))
31 bool VisitBindingDecl(BindingDecl *BD) {
38 bool TraverseStmt(Stmt *Node) {
40 return Base::TraverseStmt(Node);
45 switch (
Node->getStmtClass()) {
46 case Stmt::IfStmtClass:
47 case Stmt::WhileStmtClass:
48 case Stmt::DoStmtClass:
49 case Stmt::CXXForRangeStmtClass:
50 case Stmt::ForStmtClass:
51 case Stmt::SwitchStmtClass:
54 case Stmt::CompoundStmtClass:
62 Base::TraverseStmt(Node);
69 bool TraverseCompoundStmt(CompoundStmt *Node) {
73 if (CurrentNestingLevel ==
Info.NestingThreshold)
74 Info.NestingThresholders.push_back(
Node->getBeginLoc());
76 ++CurrentNestingLevel;
77 Base::TraverseCompoundStmt(Node);
78 --CurrentNestingLevel;
83 bool TraverseDecl(
Decl *Node) {
85 Base::TraverseDecl(Node);
90 bool TraverseLambdaExpr(LambdaExpr *Node) {
92 Base::TraverseLambdaExpr(Node);
97 bool TraverseCXXRecordDecl(CXXRecordDecl *Node) {
99 Base::TraverseCXXRecordDecl(Node);
104 bool TraverseStmtExpr(StmtExpr *SE) {
106 Base::TraverseStmtExpr(SE);
111 struct FunctionInfo {
122 unsigned CurrentNestingLevel = 0;
129 LineThreshold(Options.get(
"LineThreshold", DefaultLineThreshold)),
131 Options.get(
"StatementThreshold", DefaultStatementThreshold)),
132 BranchThreshold(Options.get(
"BranchThreshold", DefaultBranchThreshold)),
134 Options.get(
"ParameterThreshold", DefaultParameterThreshold)),
136 Options.get(
"NestingThreshold", DefaultNestingThreshold)),
138 Options.get(
"VariableThreshold", DefaultVariableThreshold)) {}
142 Options.
store(Opts,
"StatementThreshold", StatementThreshold);
143 Options.
store(Opts,
"BranchThreshold", BranchThreshold);
144 Options.
store(Opts,
"ParameterThreshold", ParameterThreshold);
145 Options.
store(Opts,
"NestingThreshold", NestingThreshold);
146 Options.
store(Opts,
"VariableThreshold", VariableThreshold);
152 Finder->addMatcher(functionDecl(unless(isInstantiated()),
153 unless(cxxMethodDecl(ofClass(isLambda()))))
159 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
161 FunctionASTVisitor Visitor;
162 Visitor.Info.NestingThreshold = NestingThreshold.value_or(-1);
163 Visitor.TraverseDecl(
const_cast<FunctionDecl *
>(Func));
164 auto &FI = Visitor.Info;
166 if (FI.Statements == 0)
170 if (
const Stmt *Body = Func->getBody()) {
171 SourceManager *SM = Result.SourceManager;
172 if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getEndLoc())) {
173 FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) -
174 SM->getSpellingLineNumber(Body->getBeginLoc());
178 unsigned ActualNumberParameters = Func->getNumParams();
180 if ((LineThreshold && FI.Lines > LineThreshold) ||
181 (StatementThreshold && FI.Statements > StatementThreshold) ||
182 (BranchThreshold && FI.Branches > BranchThreshold) ||
183 (ParameterThreshold && ActualNumberParameters > ParameterThreshold) ||
184 !FI.NestingThresholders.empty() ||
185 (VariableThreshold && FI.Variables > VariableThreshold)) {
186 diag(Func->getLocation(),
187 "function %0 exceeds recommended size/complexity thresholds")
191 if (LineThreshold && FI.Lines > LineThreshold) {
192 diag(Func->getLocation(),
193 "%0 lines including whitespace and comments (threshold %1)",
195 << FI.Lines << LineThreshold.value();
198 if (StatementThreshold && FI.Statements > StatementThreshold) {
199 diag(Func->getLocation(),
"%0 statements (threshold %1)",
201 << FI.Statements << StatementThreshold.value();
204 if (BranchThreshold && FI.Branches > BranchThreshold) {
205 diag(Func->getLocation(),
"%0 branches (threshold %1)", DiagnosticIDs::Note)
206 << FI.Branches << BranchThreshold.value();
209 if (ParameterThreshold && ActualNumberParameters > ParameterThreshold) {
210 diag(Func->getLocation(),
"%0 parameters (threshold %1)",
212 << ActualNumberParameters << ParameterThreshold.value();
215 for (
const auto &CSPos : FI.NestingThresholders) {
216 diag(CSPos,
"nesting level %0 starts here (threshold %1)",
218 << NestingThreshold.value() + 1 << NestingThreshold.value();
221 if (VariableThreshold && FI.Variables > VariableThreshold) {
222 diag(Func->getLocation(),
"%0 variables (threshold %1)",
224 << FI.Variables << VariableThreshold.value();
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
unsigned NestingThreshold
std::vector< SourceLocation > NestingThresholders
llvm::BitVector TrackedParent
::clang::DynTypedNode Node
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
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 storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
llvm::StringMap< ClangTidyValue > OptionMap