annotate clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp @ 266:00f31e85ec16 default tip

Added tag current for changeset 31d058e83c98
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 14 Oct 2023 10:13:55 +0900
parents 1f2b6ac9f198
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===-- FunctionSizeCheck.cpp - clang-tidy --------------------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "FunctionSizeCheck.h"
anatofuz
parents:
diff changeset
10 #include "clang/AST/RecursiveASTVisitor.h"
anatofuz
parents:
diff changeset
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
236
c4bab56944e8 LLVM 16
kono
parents: 150
diff changeset
12 #include "llvm/ADT/BitVector.h"
150
anatofuz
parents:
diff changeset
13
anatofuz
parents:
diff changeset
14 using namespace clang::ast_matchers;
anatofuz
parents:
diff changeset
15
252
1f2b6ac9f198 LLVM16-1
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 236
diff changeset
16 namespace clang::tidy::readability {
150
anatofuz
parents:
diff changeset
17 namespace {
anatofuz
parents:
diff changeset
18
anatofuz
parents:
diff changeset
19 class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
anatofuz
parents:
diff changeset
20 using Base = RecursiveASTVisitor<FunctionASTVisitor>;
anatofuz
parents:
diff changeset
21
anatofuz
parents:
diff changeset
22 public:
anatofuz
parents:
diff changeset
23 bool VisitVarDecl(VarDecl *VD) {
anatofuz
parents:
diff changeset
24 // Do not count function params.
anatofuz
parents:
diff changeset
25 // Do not count decomposition declarations (C++17's structured bindings).
anatofuz
parents:
diff changeset
26 if (StructNesting == 0 &&
anatofuz
parents:
diff changeset
27 !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)))
anatofuz
parents:
diff changeset
28 ++Info.Variables;
anatofuz
parents:
diff changeset
29 return true;
anatofuz
parents:
diff changeset
30 }
anatofuz
parents:
diff changeset
31 bool VisitBindingDecl(BindingDecl *BD) {
anatofuz
parents:
diff changeset
32 // Do count each of the bindings (in the decomposition declaration).
anatofuz
parents:
diff changeset
33 if (StructNesting == 0)
anatofuz
parents:
diff changeset
34 ++Info.Variables;
anatofuz
parents:
diff changeset
35 return true;
anatofuz
parents:
diff changeset
36 }
anatofuz
parents:
diff changeset
37
anatofuz
parents:
diff changeset
38 bool TraverseStmt(Stmt *Node) {
anatofuz
parents:
diff changeset
39 if (!Node)
anatofuz
parents:
diff changeset
40 return Base::TraverseStmt(Node);
anatofuz
parents:
diff changeset
41
anatofuz
parents:
diff changeset
42 if (TrackedParent.back() && !isa<CompoundStmt>(Node))
anatofuz
parents:
diff changeset
43 ++Info.Statements;
anatofuz
parents:
diff changeset
44
anatofuz
parents:
diff changeset
45 switch (Node->getStmtClass()) {
anatofuz
parents:
diff changeset
46 case Stmt::IfStmtClass:
anatofuz
parents:
diff changeset
47 case Stmt::WhileStmtClass:
anatofuz
parents:
diff changeset
48 case Stmt::DoStmtClass:
anatofuz
parents:
diff changeset
49 case Stmt::CXXForRangeStmtClass:
anatofuz
parents:
diff changeset
50 case Stmt::ForStmtClass:
anatofuz
parents:
diff changeset
51 case Stmt::SwitchStmtClass:
anatofuz
parents:
diff changeset
52 ++Info.Branches;
236
c4bab56944e8 LLVM 16
kono
parents: 150
diff changeset
53 [[fallthrough]];
150
anatofuz
parents:
diff changeset
54 case Stmt::CompoundStmtClass:
anatofuz
parents:
diff changeset
55 TrackedParent.push_back(true);
anatofuz
parents:
diff changeset
56 break;
anatofuz
parents:
diff changeset
57 default:
anatofuz
parents:
diff changeset
58 TrackedParent.push_back(false);
anatofuz
parents:
diff changeset
59 break;
anatofuz
parents:
diff changeset
60 }
anatofuz
parents:
diff changeset
61
anatofuz
parents:
diff changeset
62 Base::TraverseStmt(Node);
anatofuz
parents:
diff changeset
63
anatofuz
parents:
diff changeset
64 TrackedParent.pop_back();
anatofuz
parents:
diff changeset
65
anatofuz
parents:
diff changeset
66 return true;
anatofuz
parents:
diff changeset
67 }
anatofuz
parents:
diff changeset
68
anatofuz
parents:
diff changeset
69 bool TraverseCompoundStmt(CompoundStmt *Node) {
anatofuz
parents:
diff changeset
70 // If this new compound statement is located in a compound statement, which
anatofuz
parents:
diff changeset
71 // is already nested NestingThreshold levels deep, record the start location
anatofuz
parents:
diff changeset
72 // of this new compound statement.
anatofuz
parents:
diff changeset
73 if (CurrentNestingLevel == Info.NestingThreshold)
anatofuz
parents:
diff changeset
74 Info.NestingThresholders.push_back(Node->getBeginLoc());
anatofuz
parents:
diff changeset
75
anatofuz
parents:
diff changeset
76 ++CurrentNestingLevel;
anatofuz
parents:
diff changeset
77 Base::TraverseCompoundStmt(Node);
anatofuz
parents:
diff changeset
78 --CurrentNestingLevel;
anatofuz
parents:
diff changeset
79
anatofuz
parents:
diff changeset
80 return true;
anatofuz
parents:
diff changeset
81 }
anatofuz
parents:
diff changeset
82
anatofuz
parents:
diff changeset
83 bool TraverseDecl(Decl *Node) {
anatofuz
parents:
diff changeset
84 TrackedParent.push_back(false);
anatofuz
parents:
diff changeset
85 Base::TraverseDecl(Node);
anatofuz
parents:
diff changeset
86 TrackedParent.pop_back();
anatofuz
parents:
diff changeset
87 return true;
anatofuz
parents:
diff changeset
88 }
anatofuz
parents:
diff changeset
89
anatofuz
parents:
diff changeset
90 bool TraverseLambdaExpr(LambdaExpr *Node) {
anatofuz
parents:
diff changeset
91 ++StructNesting;
anatofuz
parents:
diff changeset
92 Base::TraverseLambdaExpr(Node);
anatofuz
parents:
diff changeset
93 --StructNesting;
anatofuz
parents:
diff changeset
94 return true;
anatofuz
parents:
diff changeset
95 }
anatofuz
parents:
diff changeset
96
anatofuz
parents:
diff changeset
97 bool TraverseCXXRecordDecl(CXXRecordDecl *Node) {
anatofuz
parents:
diff changeset
98 ++StructNesting;
anatofuz
parents:
diff changeset
99 Base::TraverseCXXRecordDecl(Node);
anatofuz
parents:
diff changeset
100 --StructNesting;
anatofuz
parents:
diff changeset
101 return true;
anatofuz
parents:
diff changeset
102 }
anatofuz
parents:
diff changeset
103
anatofuz
parents:
diff changeset
104 bool TraverseStmtExpr(StmtExpr *SE) {
anatofuz
parents:
diff changeset
105 ++StructNesting;
anatofuz
parents:
diff changeset
106 Base::TraverseStmtExpr(SE);
anatofuz
parents:
diff changeset
107 --StructNesting;
anatofuz
parents:
diff changeset
108 return true;
anatofuz
parents:
diff changeset
109 }
anatofuz
parents:
diff changeset
110
anatofuz
parents:
diff changeset
111 struct FunctionInfo {
anatofuz
parents:
diff changeset
112 unsigned Lines = 0;
anatofuz
parents:
diff changeset
113 unsigned Statements = 0;
anatofuz
parents:
diff changeset
114 unsigned Branches = 0;
anatofuz
parents:
diff changeset
115 unsigned NestingThreshold = 0;
anatofuz
parents:
diff changeset
116 unsigned Variables = 0;
anatofuz
parents:
diff changeset
117 std::vector<SourceLocation> NestingThresholders;
anatofuz
parents:
diff changeset
118 };
anatofuz
parents:
diff changeset
119 FunctionInfo Info;
236
c4bab56944e8 LLVM 16
kono
parents: 150
diff changeset
120 llvm::BitVector TrackedParent;
150
anatofuz
parents:
diff changeset
121 unsigned StructNesting = 0;
anatofuz
parents:
diff changeset
122 unsigned CurrentNestingLevel = 0;
anatofuz
parents:
diff changeset
123 };
anatofuz
parents:
diff changeset
124
anatofuz
parents:
diff changeset
125 } // namespace
anatofuz
parents:
diff changeset
126
anatofuz
parents:
diff changeset
127 FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
anatofuz
parents:
diff changeset
128 : ClangTidyCheck(Name, Context),
anatofuz
parents:
diff changeset
129 LineThreshold(Options.get("LineThreshold", -1U)),
anatofuz
parents:
diff changeset
130 StatementThreshold(Options.get("StatementThreshold", 800U)),
anatofuz
parents:
diff changeset
131 BranchThreshold(Options.get("BranchThreshold", -1U)),
anatofuz
parents:
diff changeset
132 ParameterThreshold(Options.get("ParameterThreshold", -1U)),
anatofuz
parents:
diff changeset
133 NestingThreshold(Options.get("NestingThreshold", -1U)),
anatofuz
parents:
diff changeset
134 VariableThreshold(Options.get("VariableThreshold", -1U)) {}
anatofuz
parents:
diff changeset
135
anatofuz
parents:
diff changeset
136 void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
anatofuz
parents:
diff changeset
137 Options.store(Opts, "LineThreshold", LineThreshold);
anatofuz
parents:
diff changeset
138 Options.store(Opts, "StatementThreshold", StatementThreshold);
anatofuz
parents:
diff changeset
139 Options.store(Opts, "BranchThreshold", BranchThreshold);
anatofuz
parents:
diff changeset
140 Options.store(Opts, "ParameterThreshold", ParameterThreshold);
anatofuz
parents:
diff changeset
141 Options.store(Opts, "NestingThreshold", NestingThreshold);
anatofuz
parents:
diff changeset
142 Options.store(Opts, "VariableThreshold", VariableThreshold);
anatofuz
parents:
diff changeset
143 }
anatofuz
parents:
diff changeset
144
anatofuz
parents:
diff changeset
145 void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
anatofuz
parents:
diff changeset
146 // Lambdas ignored - historically considered part of enclosing function.
anatofuz
parents:
diff changeset
147 // FIXME: include them instead? Top-level lambdas are currently never counted.
anatofuz
parents:
diff changeset
148 Finder->addMatcher(functionDecl(unless(isInstantiated()),
anatofuz
parents:
diff changeset
149 unless(cxxMethodDecl(ofClass(isLambda()))))
anatofuz
parents:
diff changeset
150 .bind("func"),
anatofuz
parents:
diff changeset
151 this);
anatofuz
parents:
diff changeset
152 }
anatofuz
parents:
diff changeset
153
anatofuz
parents:
diff changeset
154 void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
anatofuz
parents:
diff changeset
155 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
anatofuz
parents:
diff changeset
156
anatofuz
parents:
diff changeset
157 FunctionASTVisitor Visitor;
anatofuz
parents:
diff changeset
158 Visitor.Info.NestingThreshold = NestingThreshold;
anatofuz
parents:
diff changeset
159 Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
anatofuz
parents:
diff changeset
160 auto &FI = Visitor.Info;
anatofuz
parents:
diff changeset
161
anatofuz
parents:
diff changeset
162 if (FI.Statements == 0)
anatofuz
parents:
diff changeset
163 return;
anatofuz
parents:
diff changeset
164
anatofuz
parents:
diff changeset
165 // Count the lines including whitespace and comments. Really simple.
anatofuz
parents:
diff changeset
166 if (const Stmt *Body = Func->getBody()) {
anatofuz
parents:
diff changeset
167 SourceManager *SM = Result.SourceManager;
anatofuz
parents:
diff changeset
168 if (SM->isWrittenInSameFile(Body->getBeginLoc(), Body->getEndLoc())) {
anatofuz
parents:
diff changeset
169 FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) -
anatofuz
parents:
diff changeset
170 SM->getSpellingLineNumber(Body->getBeginLoc());
anatofuz
parents:
diff changeset
171 }
anatofuz
parents:
diff changeset
172 }
anatofuz
parents:
diff changeset
173
anatofuz
parents:
diff changeset
174 unsigned ActualNumberParameters = Func->getNumParams();
anatofuz
parents:
diff changeset
175
anatofuz
parents:
diff changeset
176 if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
anatofuz
parents:
diff changeset
177 FI.Branches > BranchThreshold ||
anatofuz
parents:
diff changeset
178 ActualNumberParameters > ParameterThreshold ||
anatofuz
parents:
diff changeset
179 !FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) {
anatofuz
parents:
diff changeset
180 diag(Func->getLocation(),
anatofuz
parents:
diff changeset
181 "function %0 exceeds recommended size/complexity thresholds")
anatofuz
parents:
diff changeset
182 << Func;
anatofuz
parents:
diff changeset
183 }
anatofuz
parents:
diff changeset
184
anatofuz
parents:
diff changeset
185 if (FI.Lines > LineThreshold) {
anatofuz
parents:
diff changeset
186 diag(Func->getLocation(),
anatofuz
parents:
diff changeset
187 "%0 lines including whitespace and comments (threshold %1)",
anatofuz
parents:
diff changeset
188 DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
189 << FI.Lines << LineThreshold;
anatofuz
parents:
diff changeset
190 }
anatofuz
parents:
diff changeset
191
anatofuz
parents:
diff changeset
192 if (FI.Statements > StatementThreshold) {
anatofuz
parents:
diff changeset
193 diag(Func->getLocation(), "%0 statements (threshold %1)",
anatofuz
parents:
diff changeset
194 DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
195 << FI.Statements << StatementThreshold;
anatofuz
parents:
diff changeset
196 }
anatofuz
parents:
diff changeset
197
anatofuz
parents:
diff changeset
198 if (FI.Branches > BranchThreshold) {
anatofuz
parents:
diff changeset
199 diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
200 << FI.Branches << BranchThreshold;
anatofuz
parents:
diff changeset
201 }
anatofuz
parents:
diff changeset
202
anatofuz
parents:
diff changeset
203 if (ActualNumberParameters > ParameterThreshold) {
anatofuz
parents:
diff changeset
204 diag(Func->getLocation(), "%0 parameters (threshold %1)",
anatofuz
parents:
diff changeset
205 DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
206 << ActualNumberParameters << ParameterThreshold;
anatofuz
parents:
diff changeset
207 }
anatofuz
parents:
diff changeset
208
anatofuz
parents:
diff changeset
209 for (const auto &CSPos : FI.NestingThresholders) {
anatofuz
parents:
diff changeset
210 diag(CSPos, "nesting level %0 starts here (threshold %1)",
anatofuz
parents:
diff changeset
211 DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
212 << NestingThreshold + 1 << NestingThreshold;
anatofuz
parents:
diff changeset
213 }
anatofuz
parents:
diff changeset
214
anatofuz
parents:
diff changeset
215 if (FI.Variables > VariableThreshold) {
anatofuz
parents:
diff changeset
216 diag(Func->getLocation(), "%0 variables (threshold %1)",
anatofuz
parents:
diff changeset
217 DiagnosticIDs::Note)
anatofuz
parents:
diff changeset
218 << FI.Variables << VariableThreshold;
anatofuz
parents:
diff changeset
219 }
anatofuz
parents:
diff changeset
220 }
anatofuz
parents:
diff changeset
221
252
1f2b6ac9f198 LLVM16-1
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 236
diff changeset
222 } // namespace clang::tidy::readability