Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyReadabilityModule AvoidConstParamsInDecls.cpp BracesAroundStatementsCheck.cpp + CompoundStatementSizeCheck.cpp ContainerSizeEmptyCheck.cpp DeleteNullPointerCheck.cpp DeletedDefaultCheck.cpp Index: clang-tidy/readability/CompoundStatementSizeCheck.h =================================================================== --- /dev/null +++ clang-tidy/readability/CompoundStatementSizeCheck.h @@ -0,0 +1,48 @@ +//===--- CompoundStatementSizeCheck.h - clang-tidy --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_COMPOUNDSTATEMENTSIZE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_COMPOUNDSTATEMENTSIZE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Checks for large compound statements based on various metrics. +/// +/// These options are supported: +/// +/// * `LineThreshold` - flag compound statements exceeding this number of +/// lines. The default is `-1` (ignore the number of lines). +/// * `StatementThreshold` - flag compound statements exceeding this number of +/// statements. This may differ significantly from the number of lines for +/// macro-heavy code. The default is `800`. +/// * `BranchThreshold` - flag compound statements exceeding this number of +/// control statements. The default is `-1` (ignore the number of branches). +class CompoundStatementSizeCheck : public ClangTidyCheck { +public: + CompoundStatementSizeCheck(StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const unsigned LineThreshold; + const unsigned StatementThreshold; + const unsigned BranchThreshold; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_COMPOUNDSTATEMENTSIZE_H Index: clang-tidy/readability/CompoundStatementSizeCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/CompoundStatementSizeCheck.cpp @@ -0,0 +1,134 @@ +//===--- CompoundStatement.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 "CompoundStatementSizeCheck.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +class CompoundStatementASTVisitor + : public RecursiveASTVisitor { + using Base = RecursiveASTVisitor; + +public: + bool TraverseStmt(Stmt *Node) { + if (!Node) + return Base::TraverseStmt(Node); + + if (TrackedParent.back() && !isa(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; + LLVM_FALLTHROUGH; + case Stmt::CompoundStmtClass: + TrackedParent.push_back(true); + break; + default: + TrackedParent.push_back(false); + break; + } + + Base::TraverseStmt(Node); + + TrackedParent.pop_back(); + return true; + } + + bool TraverseCompoundStatement(CompoundStmt *Node) { + TrackedParent.push_back(true); + Base::TraverseCompoundStmt(Node); + TrackedParent.pop_back(); + return true; + } + + struct CompoundStatementInfo { + unsigned Lines = 0; + unsigned Statements = 0; + unsigned Branches = 0; + }; + CompoundStatementInfo Info; + std::vector TrackedParent; +}; + +CompoundStatementSizeCheck::CompoundStatementSizeCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + LineThreshold(Options.get("LineThreshold", -1U)), + StatementThreshold(Options.get("StatementThreshold", 800U)), + BranchThreshold(Options.get("BranchThreshold", -1U)) {} + +void CompoundStatementSizeCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "LineThreshold", LineThreshold); + Options.store(Opts, "StatementThreshold", StatementThreshold); + Options.store(Opts, "BranchThreshold", BranchThreshold); +} + +void CompoundStatementSizeCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + compoundStmt(unless(hasParent(functionDecl()))).bind("compound"), this); +} + +void CompoundStatementSizeCheck::check(const MatchFinder::MatchResult &Result) { + const auto *CS = Result.Nodes.getNodeAs("compound"); + + CompoundStatementASTVisitor Visitor; + Visitor.TraverseCompoundStatement(const_cast(CS)); + auto &CSI = Visitor.Info; + + if (CSI.Statements == 0) + return; + + // Count the lines including whitespace and comments. Really simple. + { + SourceManager *SM = Result.SourceManager; + CSI.Lines = SM->getSpellingLineNumber(CS->getLocEnd()) - + SM->getSpellingLineNumber(CS->getLocStart()); + } + + if (CSI.Lines > LineThreshold || CSI.Statements > StatementThreshold || + CSI.Branches > BranchThreshold) { + diag(CS->getLocStart(), + "compound statement exceeds recommended size/complexity thresholds"); + } + + if (CSI.Lines > LineThreshold) { + diag(CS->getLocStart(), + "%0 lines including whitespace and comments (threshold %1)", + DiagnosticIDs::Note) + << CSI.Lines << LineThreshold; + } + + if (CSI.Statements > StatementThreshold) { + diag(CS->getLocStart(), "%0 statements (threshold %1)", DiagnosticIDs::Note) + << CSI.Statements << StatementThreshold; + } + + if (CSI.Branches > BranchThreshold) { + diag(CS->getLocStart(), "%0 branches (threshold %1)", DiagnosticIDs::Note) + << CSI.Branches << BranchThreshold; + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/readability/IdentifierNamingCheck.h =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.h +++ clang-tidy/readability/IdentifierNamingCheck.h @@ -53,15 +53,19 @@ }; struct NamingStyle { - NamingStyle() = default; + NamingStyle() : Case(CT_AnyCase) {} - NamingStyle(llvm::Optional Case, const std::string &Prefix, + NamingStyle(CaseType Case, const std::string &Prefix, const std::string &Suffix) : Case(Case), Prefix(Prefix), Suffix(Suffix) {} - llvm::Optional Case; + CaseType Case; std::string Prefix; std::string Suffix; + + bool isSet() const { + return !(Case == CT_AnyCase && Prefix.empty() && Suffix.empty()); + } }; /// \brief Holds an identifier name check failure, tracking the kind of the @@ -97,7 +101,7 @@ void expandMacro(const Token &MacroNameTok, const MacroInfo *MI); private: - std::vector> NamingStyles; + std::vector NamingStyles; bool IgnoreFailedSplit; NamingCheckFailureMap NamingCheckFailures; }; Index: clang-tidy/readability/IdentifierNamingCheck.cpp =================================================================== --- clang-tidy/readability/IdentifierNamingCheck.cpp +++ clang-tidy/readability/IdentifierNamingCheck.cpp @@ -158,28 +158,21 @@ ClangTidyContext *Context) : ClangTidyCheck(Name, Context) { auto const fromString = [](StringRef Str) { - return llvm::StringSwitch>(Str) - .Case("aNy_CasE", CT_AnyCase) + return llvm::StringSwitch(Str) .Case("lower_case", CT_LowerCase) .Case("UPPER_CASE", CT_UpperCase) .Case("camelBack", CT_CamelBack) .Case("CamelCase", CT_CamelCase) .Case("Camel_Snake_Case", CT_CamelSnakeCase) .Case("camel_Snake_Back", CT_CamelSnakeBack) - .Default(llvm::None); + .Default(CT_AnyCase); }; for (auto const &Name : StyleNames) { - auto const caseOptional = - fromString(Options.get((Name + "Case").str(), "")); - auto prefix = Options.get((Name + "Prefix").str(), ""); - auto postfix = Options.get((Name + "Suffix").str(), ""); - - if (caseOptional || !prefix.empty() || !postfix.empty()) { - NamingStyles.push_back(NamingStyle(caseOptional, prefix, postfix)); - } else { - NamingStyles.push_back(llvm::None); - } + NamingStyles.push_back( + NamingStyle(fromString(Options.get((Name + "Case").str(), "")), + Options.get((Name + "Prefix").str(), ""), + Options.get((Name + "Suffix").str(), ""))); } IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0); @@ -208,16 +201,12 @@ }; for (size_t i = 0; i < SK_Count; ++i) { - if (NamingStyles[i]) { - if (NamingStyles[i]->Case) { - Options.store(Opts, (StyleNames[i] + "Case").str(), - toString(*NamingStyles[i]->Case)); - } - Options.store(Opts, (StyleNames[i] + "Prefix").str(), - NamingStyles[i]->Prefix); - Options.store(Opts, (StyleNames[i] + "Suffix").str(), - NamingStyles[i]->Suffix); - } + Options.store(Opts, (StyleNames[i] + "Case").str(), + toString(NamingStyles[i].Case)); + Options.store(Opts, (StyleNames[i] + "Prefix").str(), + NamingStyles[i].Prefix); + Options.store(Opts, (StyleNames[i] + "Suffix").str(), + NamingStyles[i].Suffix); } Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit); @@ -262,7 +251,7 @@ else Matches = false; - if (Style.Case && !Matchers[static_cast(*Style.Case)].match(Name)) + if (!Matchers[static_cast(Style.Case)].match(Name)) Matches = false; return Matches; @@ -364,44 +353,39 @@ return Fixup; } -static std::string -fixupWithStyle(StringRef Name, - const IdentifierNamingCheck::NamingStyle &Style) { - return Style.Prefix + - fixupWithCase(Name, Style.Case.getValueOr( - IdentifierNamingCheck::CaseType::CT_AnyCase)) + - Style.Suffix; +static std::string fixupWithStyle(StringRef Name, + IdentifierNamingCheck::NamingStyle Style) { + return Style.Prefix + fixupWithCase(Name, Style.Case) + Style.Suffix; } static StyleKind findStyleKind( const NamedDecl *D, - const std::vector> - &NamingStyles) { - if (isa(D) && NamingStyles[SK_Typedef]) + const std::vector &NamingStyles) { + if (isa(D) && NamingStyles[SK_Typedef].isSet()) return SK_Typedef; - if (isa(D) && NamingStyles[SK_TypeAlias]) + if (isa(D) && NamingStyles[SK_TypeAlias].isSet()) return SK_TypeAlias; if (const auto *Decl = dyn_cast(D)) { if (Decl->isAnonymousNamespace()) return SK_Invalid; - if (Decl->isInline() && NamingStyles[SK_InlineNamespace]) + if (Decl->isInline() && NamingStyles[SK_InlineNamespace].isSet()) return SK_InlineNamespace; - if (NamingStyles[SK_Namespace]) + if (NamingStyles[SK_Namespace].isSet()) return SK_Namespace; } - if (isa(D) && NamingStyles[SK_Enum]) + if (isa(D) && NamingStyles[SK_Enum].isSet()) return SK_Enum; if (isa(D)) { - if (NamingStyles[SK_EnumConstant]) + if (NamingStyles[SK_EnumConstant].isSet()) return SK_EnumConstant; - if (NamingStyles[SK_Constant]) + if (NamingStyles[SK_Constant].isSet()) return SK_Constant; return SK_Invalid; @@ -415,25 +399,25 @@ return SK_Invalid; if (Decl->hasDefinition() && Decl->isAbstract() && - NamingStyles[SK_AbstractClass]) + NamingStyles[SK_AbstractClass].isSet()) return SK_AbstractClass; - if (Decl->isStruct() && NamingStyles[SK_Struct]) + if (Decl->isStruct() && NamingStyles[SK_Struct].isSet()) return SK_Struct; - if (Decl->isStruct() && NamingStyles[SK_Class]) + if (Decl->isStruct() && NamingStyles[SK_Class].isSet()) return SK_Class; - if (Decl->isClass() && NamingStyles[SK_Class]) + if (Decl->isClass() && NamingStyles[SK_Class].isSet()) return SK_Class; - if (Decl->isClass() && NamingStyles[SK_Struct]) + if (Decl->isClass() && NamingStyles[SK_Struct].isSet()) return SK_Struct; - if (Decl->isUnion() && NamingStyles[SK_Union]) + if (Decl->isUnion() && NamingStyles[SK_Union].isSet()) return SK_Union; - if (Decl->isEnum() && NamingStyles[SK_Enum]) + if (Decl->isEnum() && NamingStyles[SK_Enum].isSet()) return SK_Enum; return SK_Invalid; @@ -443,23 +427,25 @@ QualType Type = Decl->getType(); if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_ConstantMember]) + NamingStyles[SK_ConstantMember].isSet()) return SK_ConstantMember; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember]) + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMember].isSet()) return SK_PrivateMember; - if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember]) + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMember].isSet()) return SK_ProtectedMember; - if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember]) + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember].isSet()) return SK_PublicMember; - if (NamingStyles[SK_Member]) + if (NamingStyles[SK_Member].isSet()) return SK_Member; return SK_Invalid; @@ -468,21 +454,21 @@ if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) return SK_ConstexprVariable; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_ConstantParameter]) + NamingStyles[SK_ConstantParameter].isSet()) return SK_ConstantParameter; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack]) + if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack].isSet()) return SK_ParameterPack; - if (NamingStyles[SK_Parameter]) + if (NamingStyles[SK_Parameter].isSet()) return SK_Parameter; return SK_Invalid; @@ -491,49 +477,51 @@ if (const auto *Decl = dyn_cast(D)) { QualType Type = Decl->getType(); - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet()) return SK_ConstexprVariable; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant]) + Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant].isSet()) return SK_ClassConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant]) + Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant].isSet()) return SK_GlobalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isStaticLocal() && NamingStyles[SK_StaticConstant]) + Decl->isStaticLocal() && NamingStyles[SK_StaticConstant].isSet()) return SK_StaticConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant]) + Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant].isSet()) return SK_LocalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant]) + Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalConstant].isSet()) return SK_LocalConstant; if (!Type.isNull() && Type.isLocalConstQualified() && - NamingStyles[SK_Constant]) + NamingStyles[SK_Constant].isSet()) return SK_Constant; - if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember]) + if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember].isSet()) return SK_ClassMember; - if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable]) + if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable].isSet()) return SK_GlobalVariable; - if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable]) + if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable].isSet()) return SK_StaticVariable; - if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable]) + if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable].isSet()) return SK_LocalVariable; - if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable]) + if (Decl->isFunctionOrMethodVarDecl() && + NamingStyles[SK_LocalVariable].isSet()) return SK_LocalVariable; - if (NamingStyles[SK_Variable]) + if (NamingStyles[SK_Variable].isSet()) return SK_Variable; return SK_Invalid; @@ -546,31 +534,33 @@ Decl->size_overridden_methods() > 0) return SK_Invalid; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod].isSet()) return SK_ConstexprMethod; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) return SK_ConstexprFunction; - if (Decl->isStatic() && NamingStyles[SK_ClassMethod]) + if (Decl->isStatic() && NamingStyles[SK_ClassMethod].isSet()) return SK_ClassMethod; - if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod]) + if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod].isSet()) return SK_VirtualMethod; - if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod]) + if (Decl->getAccess() == AS_private && + NamingStyles[SK_PrivateMethod].isSet()) return SK_PrivateMethod; - if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod]) + if (Decl->getAccess() == AS_protected && + NamingStyles[SK_ProtectedMethod].isSet()) return SK_ProtectedMethod; - if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod]) + if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod].isSet()) return SK_PublicMethod; - if (NamingStyles[SK_Method]) + if (NamingStyles[SK_Method].isSet()) return SK_Method; - if (NamingStyles[SK_Function]) + if (NamingStyles[SK_Function].isSet()) return SK_Function; return SK_Invalid; @@ -580,41 +570,41 @@ if (Decl->isMain()) return SK_Invalid; - if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction]) + if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet()) return SK_ConstexprFunction; - if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction]) + if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction].isSet()) return SK_GlobalFunction; - if (NamingStyles[SK_Function]) + if (NamingStyles[SK_Function].isSet()) return SK_Function; } if (isa(D)) { - if (NamingStyles[SK_TypeTemplateParameter]) + if (NamingStyles[SK_TypeTemplateParameter].isSet()) return SK_TypeTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { - if (NamingStyles[SK_ValueTemplateParameter]) + if (NamingStyles[SK_ValueTemplateParameter].isSet()) return SK_ValueTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; } if (isa(D)) { - if (NamingStyles[SK_TemplateTemplateParameter]) + if (NamingStyles[SK_TemplateTemplateParameter].isSet()) return SK_TemplateTemplateParameter; - if (NamingStyles[SK_TemplateParameter]) + if (NamingStyles[SK_TemplateParameter].isSet()) return SK_TemplateParameter; return SK_Invalid; @@ -817,10 +807,7 @@ if (SK == SK_Invalid) return; - if (!NamingStyles[SK]) - return; - - const NamingStyle &Style = *NamingStyles[SK]; + NamingStyle Style = NamingStyles[SK]; StringRef Name = Decl->getName(); if (matchesStyle(Name, Style)) return; @@ -853,11 +840,8 @@ void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr, const Token &MacroNameTok, const MacroInfo *MI) { - if (!NamingStyles[SK_MacroDefinition]) - return; - StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); - const NamingStyle &Style = *NamingStyles[SK_MacroDefinition]; + NamingStyle Style = NamingStyles[SK_MacroDefinition]; if (matchesStyle(Name, Style)) return; Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "AvoidConstParamsInDecls.h" #include "BracesAroundStatementsCheck.h" +#include "CompoundStatementSizeCheck.h" #include "ContainerSizeEmptyCheck.h" #include "DeleteNullPointerCheck.h" #include "DeletedDefaultCheck.h" @@ -46,6 +47,8 @@ "readability-avoid-const-params-in-decls"); CheckFactories.registerCheck( "readability-braces-around-statements"); + CheckFactories.registerCheck( + "readability-compound-statement-size"); CheckFactories.registerCheck( "readability-container-size-empty"); CheckFactories.registerCheck( Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -67,6 +67,13 @@ Allow custom memory management functions to be considered as well. +- New `readability-compound-statement-size + `_ check + + Similar to `readability-function-size + `_ check, + but works on all the compound statements, not just the functions. + - New `readability-misleading-indentation `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -134,6 +134,7 @@ performance-unnecessary-value-param readability-avoid-const-params-in-decls readability-braces-around-statements + readability-compound-statement-size readability-container-size-empty readability-delete-null-pointer readability-deleted-default Index: docs/clang-tidy/checks/readability-compound-statement-size.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/readability-compound-statement-size.rst @@ -0,0 +1,29 @@ +.. title:: clang-tidy - readability-compound-statement-size + +readability-compound-statement-size +=================================== + +Checks for compound statements based on various metrics. + +Similar to the `readability-function-size` check. It works for each compound +statement, excluding the ones directly related to the function, +i.e. it does not duplicate the readability-function-size output. + +Options +------- + +.. option:: LineThreshold + + Flag compound statements exceeding this number of lines. + The default is `-1` (ignore the number of lines). + +.. option:: StatementThreshold + + Flag compound statements exceeding this number of statements. This may + differ significantly from the number of lines for macro-heavy code. + The default is `800`. + +.. option:: BranchThreshold + + Flag compound statements exceeding this number of control statements. + The default is `-1` (ignore the number of branches). Index: test/clang-tidy/check_clang_tidy.py =================================================================== --- test/clang-tidy/check_clang_tidy.py +++ test/clang-tidy/check_clang_tidy.py @@ -60,11 +60,6 @@ if len(clang_tidy_extra_args) == 0: clang_tidy_extra_args = ['--', '--std=c++11'] if extension == '.cpp' \ else ['--'] - - # Tests should not rely on STL being available, and instead provide mock - # implementations of relevant APIs. - clang_tidy_extra_args.append('-nostdinc++') - if resource_dir is not None: clang_tidy_extra_args.append('-resource-dir=%s' % resource_dir) Index: test/clang-tidy/readability-compound-statement-size.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-compound-statement-size.cpp @@ -0,0 +1,88 @@ +// RUN: %check_clang_tidy %s readability-compound-statement-size %t -- -config='{CheckOptions: [{key: readability-compound-statement-size.LineThreshold, value: 0}, {key: readability-compound-statement-size.StatementThreshold, value: 0}, {key: readability-compound-statement-size.BranchThreshold, value: 0}]}' -- -std=c++11 + +// Bad formatting is intentional, don't run clang-format over the whole file! + +// the function's base compound statement is NOT being checked. + +void foo1() { +} + +void foo2() {;} + +void bar2() {{;}} +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-2]]:14: note: 1 statements (threshold 0) + +void foo3() { +; + +} + +void bar3() { + { + ; + + } +} +// CHECK-MESSAGES: :[[@LINE-5]]:3: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-6]]:3: note: 3 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:3: note: 1 statements (threshold 0) + +void foo4(int i) { if (i) {} else; {} +} + +void bar4(int i) { { if (i) {} else; {} +} +} +// CHECK-MESSAGES: :[[@LINE-3]]:20: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-4]]:20: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-5]]:20: note: 3 statements (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:20: note: 1 branches (threshold 0) + +void foo5(int i) {for(;i;)while(i) +do;while(i); +} + +void bar5(int i) {{for(;i;)while(i) +do;while(i); +} +} +// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-5]]:19: note: 2 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:19: note: 7 statements (threshold 0) +// CHECK-MESSAGES: :[[@LINE-7]]:19: note: 3 branches (threshold 0) + +template T foo6(T i) {return i; +} +int x = foo6(0); + +template T bar6(T i) {{return i; +} +} +int y = bar6(0); +// CHECK-MESSAGES: :[[@LINE-4]]:36: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-5]]:36: note: 1 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-6]]:36: note: 1 statements (threshold 0) + +void foo7(int p1, int p2, int p3, int p4, int p5, int p6) {;} + +void bar8() { [](){;;;;;;;;;;; if(1){} + + +}(); + + +} +// CHECK-MESSAGES: :[[@LINE-7]]:19: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-8]]:19: note: 3 lines including whitespace and comments (threshold 0) +// CHECK-MESSAGES: :[[@LINE-9]]:19: note: 13 statements (threshold 0) +// CHECK-MESSAGES: :[[@LINE-10]]:19: note: 1 branches (threshold 0) + +void foo9() { class A { void foox() {;;} }; } + +void bar9() { { class A { void barx() {{;;}} }; } } +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-2]]:15: note: 3 statements (threshold 0) +// +// CHECK-MESSAGES: :[[@LINE-4]]:40: warning: compound statement exceeds recommended size/complexity thresholds [readability-compound-statement-size] +// CHECK-MESSAGES: :[[@LINE-5]]:40: note: 2 statements (threshold 0)