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/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/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)