10 #include "clang/AST/RecursiveASTVisitor.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
17 namespace readability {
20 class FunctionASTVisitor :
public RecursiveASTVisitor<FunctionASTVisitor> {
21 using Base = RecursiveASTVisitor<FunctionASTVisitor>;
24 bool VisitVarDecl(VarDecl *VD) {
28 !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)))
32 bool VisitBindingDecl(BindingDecl *BD) {
39 bool TraverseStmt(Stmt *Node) {
41 return Base::TraverseStmt(Node);
46 switch (Node->getStmtClass()) {
47 case Stmt::IfStmtClass:
48 case Stmt::WhileStmtClass:
49 case Stmt::DoStmtClass:
50 case Stmt::CXXForRangeStmtClass:
51 case Stmt::ForStmtClass:
52 case Stmt::SwitchStmtClass:
55 case Stmt::CompoundStmtClass:
63 Base::TraverseStmt(Node);
70 bool TraverseCompoundStmt(CompoundStmt *Node) {
75 Info.NestingThresholders.push_back(Node->getBeginLoc());
78 Base::TraverseCompoundStmt(Node);
84 bool TraverseDecl(
Decl *Node) {
86 Base::TraverseDecl(Node);
91 bool TraverseLambdaExpr(LambdaExpr *Node) {
93 Base::TraverseLambdaExpr(Node);
98 bool TraverseCXXRecordDecl(CXXRecordDecl *Node) {
100 Base::TraverseCXXRecordDecl(Node);
105 bool TraverseStmtExpr(StmtExpr *SE) {
107 Base::TraverseStmtExpr(SE);
112 struct FunctionInfo {
130 LineThreshold(Options.get(
"LineThreshold", -1U)),
131 StatementThreshold(Options.get(
"StatementThreshold", 800U)),
132 BranchThreshold(Options.get(
"BranchThreshold", -1U)),
133 ParameterThreshold(Options.get(
"ParameterThreshold", -1U)),
135 VariableThreshold(Options.get(
"VariableThreshold", -1U)) {}
139 Options.
store(Opts,
"StatementThreshold", StatementThreshold);
140 Options.
store(Opts,
"BranchThreshold", BranchThreshold);
141 Options.
store(Opts,
"ParameterThreshold", ParameterThreshold);
142 Options.
store(Opts,
"NestingThreshold", NestingThreshold);
143 Options.
store(Opts,
"VariableThreshold", VariableThreshold);
149 Finder->addMatcher(functionDecl(unless(isInstantiated()),
150 unless(cxxMethodDecl(ofClass(isLambda()))))
156 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>(
"func");
158 FunctionASTVisitor Visitor;
159 Visitor.Info.NestingThreshold = NestingThreshold;
160 Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
161 auto &FI = Visitor.Info;
163 if (FI.Statements == 0)
167 if (
const Stmt *Body = Func->getBody()) {
168 SourceManager *SM = Result.SourceManager;
169 if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getEndLoc())) {
170 FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) -
171 SM->getSpellingLineNumber(Body->getBeginLoc());
175 unsigned ActualNumberParameters = Func->getNumParams();
177 if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
178 FI.Branches > BranchThreshold ||
179 ActualNumberParameters > ParameterThreshold ||
180 !FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) {
181 diag(Func->getLocation(),
182 "function %0 exceeds recommended size/complexity thresholds")
186 if (FI.Lines > LineThreshold) {
187 diag(Func->getLocation(),
188 "%0 lines including whitespace and comments (threshold %1)",
190 << FI.Lines << LineThreshold;
193 if (FI.Statements > StatementThreshold) {
194 diag(Func->getLocation(),
"%0 statements (threshold %1)",
196 << FI.Statements << StatementThreshold;
199 if (FI.Branches > BranchThreshold) {
200 diag(Func->getLocation(),
"%0 branches (threshold %1)", DiagnosticIDs::Note)
201 << FI.Branches << BranchThreshold;
204 if (ActualNumberParameters > ParameterThreshold) {
205 diag(Func->getLocation(),
"%0 parameters (threshold %1)",
207 << ActualNumberParameters << ParameterThreshold;
210 for (
const auto &CSPos : FI.NestingThresholders) {
211 diag(CSPos,
"nesting level %0 starts here (threshold %1)",
213 << NestingThreshold + 1 << NestingThreshold;
216 if (FI.Variables > VariableThreshold) {
217 diag(Func->getLocation(),
"%0 variables (threshold %1)",
219 << FI.Variables << VariableThreshold;