diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/readability/FunctionSizeCheck.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-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.cpp | 222 |
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 |