aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp')
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
new file mode 100644
index 0000000000..3c803959ca
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp
@@ -0,0 +1,222 @@
+//===-- FunctionSizeCheck.cpp - clang-tidy --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionSizeCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/BitVector.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+namespace {
+
+class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
+ using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+public:
+ bool VisitVarDecl(VarDecl *VD) {
+ // Do not count function params.
+ // Do not count decomposition declarations (C++17's structured bindings).
+ if (StructNesting == 0 &&
+ !(isa<ParmVarDecl>(VD) || isa<DecompositionDecl>(VD)))
+ ++Info.Variables;
+ return true;
+ }
+ bool VisitBindingDecl(BindingDecl *BD) {
+ // Do count each of the bindings (in the decomposition declaration).
+ if (StructNesting == 0)
+ ++Info.Variables;
+ return true;
+ }
+
+ 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 TraverseCompoundStmt(CompoundStmt *Node) {
+ // If this new compound statement is located in a compound statement, which
+ // is already nested NestingThreshold levels deep, record the start location
+ // of this new compound statement.
+ if (CurrentNestingLevel == Info.NestingThreshold)
+ Info.NestingThresholders.push_back(Node->getBeginLoc());
+
+ ++CurrentNestingLevel;
+ Base::TraverseCompoundStmt(Node);
+ --CurrentNestingLevel;
+
+ return true;
+ }
+
+ bool TraverseDecl(Decl *Node) {
+ TrackedParent.push_back(false);
+ Base::TraverseDecl(Node);
+ TrackedParent.pop_back();
+ return true;
+ }
+
+ bool TraverseLambdaExpr(LambdaExpr *Node) {
+ ++StructNesting;
+ Base::TraverseLambdaExpr(Node);
+ --StructNesting;
+ return true;
+ }
+
+ bool TraverseCXXRecordDecl(CXXRecordDecl *Node) {
+ ++StructNesting;
+ Base::TraverseCXXRecordDecl(Node);
+ --StructNesting;
+ return true;
+ }
+
+ bool TraverseStmtExpr(StmtExpr *SE) {
+ ++StructNesting;
+ Base::TraverseStmtExpr(SE);
+ --StructNesting;
+ return true;
+ }
+
+ struct FunctionInfo {
+ unsigned Lines = 0;
+ unsigned Statements = 0;
+ unsigned Branches = 0;
+ unsigned NestingThreshold = 0;
+ unsigned Variables = 0;
+ std::vector<SourceLocation> NestingThresholders;
+ };
+ FunctionInfo Info;
+ llvm::BitVector TrackedParent;
+ unsigned StructNesting = 0;
+ unsigned CurrentNestingLevel = 0;
+};
+
+} // namespace
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ LineThreshold(Options.get("LineThreshold", -1U)),
+ StatementThreshold(Options.get("StatementThreshold", 800U)),
+ BranchThreshold(Options.get("BranchThreshold", -1U)),
+ ParameterThreshold(Options.get("ParameterThreshold", -1U)),
+ NestingThreshold(Options.get("NestingThreshold", -1U)),
+ VariableThreshold(Options.get("VariableThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "LineThreshold", LineThreshold);
+ Options.store(Opts, "StatementThreshold", StatementThreshold);
+ Options.store(Opts, "BranchThreshold", BranchThreshold);
+ Options.store(Opts, "ParameterThreshold", ParameterThreshold);
+ Options.store(Opts, "NestingThreshold", NestingThreshold);
+ Options.store(Opts, "VariableThreshold", VariableThreshold);
+}
+
+void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
+ // Lambdas ignored - historically considered part of enclosing function.
+ // FIXME: include them instead? Top-level lambdas are currently never counted.
+ Finder->addMatcher(functionDecl(unless(isInstantiated()),
+ unless(cxxMethodDecl(ofClass(isLambda()))))
+ .bind("func"),
+ this);
+}
+
+void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+ FunctionASTVisitor Visitor;
+ Visitor.Info.NestingThreshold = NestingThreshold;
+ 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->getBeginLoc(), Body->getEndLoc())) {
+ FI.Lines = SM->getSpellingLineNumber(Body->getEndLoc()) -
+ SM->getSpellingLineNumber(Body->getBeginLoc());
+ }
+ }
+
+ unsigned ActualNumberParameters = Func->getNumParams();
+
+ if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+ FI.Branches > BranchThreshold ||
+ ActualNumberParameters > ParameterThreshold ||
+ !FI.NestingThresholders.empty() || FI.Variables > VariableThreshold) {
+ 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;
+ }
+
+ if (ActualNumberParameters > ParameterThreshold) {
+ diag(Func->getLocation(), "%0 parameters (threshold %1)",
+ DiagnosticIDs::Note)
+ << ActualNumberParameters << ParameterThreshold;
+ }
+
+ for (const auto &CSPos : FI.NestingThresholders) {
+ diag(CSPos, "nesting level %0 starts here (threshold %1)",
+ DiagnosticIDs::Note)
+ << NestingThreshold + 1 << NestingThreshold;
+ }
+
+ if (FI.Variables > VariableThreshold) {
+ diag(Func->getLocation(), "%0 variables (threshold %1)",
+ DiagnosticIDs::Note)
+ << FI.Variables << VariableThreshold;
+ }
+}
+
+} // namespace clang::tidy::readability