mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-29 00:08:59 -04:00

Summary: Use a recursive visitor instead of forEachDescendant() matcher. The latter requires several layers of virtual function calls for each node and it is more expensive than the visitor. Benchmark results show improvement of ~6% walltime in clang-tidy. Reviewers: alexfh Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D20597 llvm-svn: 270714
135 lines
3.9 KiB
C++
135 lines
3.9 KiB
C++
//===--- FunctionSize.cpp - clang-tidy ------------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "FunctionSizeCheck.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace readability {
|
|
|
|
class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
|
|
using Base = RecursiveASTVisitor<FunctionASTVisitor>;
|
|
|
|
public:
|
|
bool TraverseStmt(Stmt *Node) {
|
|
if (!Node)
|
|
return Base::TraverseStmt(Node);
|
|
|
|
if (TrackedParent.back() && !isa<CompoundStmt>(Node))
|
|
++Info.Statements;
|
|
|
|
switch (Node->getStmtClass()) {
|
|
case Stmt::IfStmtClass:
|
|
case Stmt::WhileStmtClass:
|
|
case Stmt::DoStmtClass:
|
|
case Stmt::CXXForRangeStmtClass:
|
|
case Stmt::ForStmtClass:
|
|
case Stmt::SwitchStmtClass:
|
|
++Info.Branches;
|
|
// fallthrough
|
|
case Stmt::CompoundStmtClass:
|
|
TrackedParent.push_back(true);
|
|
break;
|
|
default:
|
|
TrackedParent.push_back(false);
|
|
break;
|
|
}
|
|
|
|
Base::TraverseStmt(Node);
|
|
|
|
TrackedParent.pop_back();
|
|
return true;
|
|
}
|
|
|
|
bool TraverseDecl(Decl *Node) {
|
|
TrackedParent.push_back(false);
|
|
Base::TraverseDecl(Node);
|
|
TrackedParent.pop_back();
|
|
return true;
|
|
}
|
|
|
|
struct FunctionInfo {
|
|
FunctionInfo() : Lines(0), Statements(0), Branches(0) {}
|
|
unsigned Lines;
|
|
unsigned Statements;
|
|
unsigned Branches;
|
|
};
|
|
FunctionInfo Info;
|
|
std::vector<bool> TrackedParent;
|
|
};
|
|
|
|
FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
|
|
: ClangTidyCheck(Name, Context),
|
|
LineThreshold(Options.get("LineThreshold", -1U)),
|
|
StatementThreshold(Options.get("StatementThreshold", 800U)),
|
|
BranchThreshold(Options.get("BranchThreshold", -1U)) {}
|
|
|
|
void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|
Options.store(Opts, "LineThreshold", LineThreshold);
|
|
Options.store(Opts, "StatementThreshold", StatementThreshold);
|
|
Options.store(Opts, "BranchThreshold", BranchThreshold);
|
|
}
|
|
|
|
void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
|
|
Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
|
|
}
|
|
|
|
void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
|
|
|
|
FunctionASTVisitor Visitor;
|
|
Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
|
|
auto &FI = Visitor.Info;
|
|
|
|
if (FI.Statements == 0)
|
|
return;
|
|
|
|
// Count the lines including whitespace and comments. Really simple.
|
|
if (const Stmt *Body = Func->getBody()) {
|
|
SourceManager *SM = Result.SourceManager;
|
|
if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
|
|
FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
|
|
SM->getSpellingLineNumber(Body->getLocStart());
|
|
}
|
|
}
|
|
|
|
if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
|
|
FI.Branches > BranchThreshold) {
|
|
diag(Func->getLocation(),
|
|
"function %0 exceeds recommended size/complexity thresholds")
|
|
<< Func;
|
|
}
|
|
|
|
if (FI.Lines > LineThreshold) {
|
|
diag(Func->getLocation(),
|
|
"%0 lines including whitespace and comments (threshold %1)",
|
|
DiagnosticIDs::Note)
|
|
<< FI.Lines << LineThreshold;
|
|
}
|
|
|
|
if (FI.Statements > StatementThreshold) {
|
|
diag(Func->getLocation(), "%0 statements (threshold %1)",
|
|
DiagnosticIDs::Note)
|
|
<< FI.Statements << StatementThreshold;
|
|
}
|
|
|
|
if (FI.Branches > BranchThreshold) {
|
|
diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
|
|
<< FI.Branches << BranchThreshold;
|
|
}
|
|
}
|
|
|
|
} // namespace readability
|
|
} // namespace tidy
|
|
} // namespace clang
|