lebedev.ri created this revision.
lebedev.ri added a project: clang-tools-extra.
Herald added a subscriber: mgorny.

This check is quite similar to the readability-function-size
check. But it is more generic. 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.

The rationale behind the check is the same as behind
readability-function-size check, it may make sense to be able
to control sizes of the compound statements, not just the
size of the functions.

Eventually, i would like these two checkers to be one, and
handle more cases and be more configurable, e.g. to be able to
apply such a check to the 'True' block of 'If' and so on.

But since this is my first attempt at any llvm/clang patch, 
this is what it is. Please do review carefully.


https://reviews.llvm.org/D31252

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/CompoundStatementSizeCheck.cpp
  clang-tidy/readability/CompoundStatementSizeCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-compound-statement-size.rst
  docs/clang-tidy/checks/readability-function-size.rst
  test/clang-tidy/readability-compound-statement-size.cpp

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 <typename T> T foo6(T i) {return i;
+}
+int x = foo6(0);
+
+template <typename T> 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)
Index: docs/clang-tidy/checks/readability-function-size.rst
===================================================================
--- docs/clang-tidy/checks/readability-function-size.rst
+++ docs/clang-tidy/checks/readability-function-size.rst
@@ -28,5 +28,5 @@
 
 .. option:: ParameterThreshold
 
-   Flag functions that exceed a specified number of parameters. The default 
+   Flag functions that exceed a specified number of parameters. The default
    is `-1` (ignore the number of parameters).
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: 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: 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<BracesAroundStatementsCheck>(
         "readability-braces-around-statements");
+    CheckFactories.registerCheck<CompoundStatementSizeCheck>(
+        "readability-compound-statement-size");
     CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
         "readability-container-size-empty");
     CheckFactories.registerCheck<DeleteNullPointerCheck>(
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,135 @@
+//===--- 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<CompoundStatementASTVisitor> {
+  using Base = RecursiveASTVisitor<CompoundStatementASTVisitor>;
+
+public:
+  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 TraverseCompoundStatement(CompoundStmt *Node) {
+    TrackedParent.push_back(true);
+    Base::TraverseCompoundStmt(Node);
+    TrackedParent.pop_back();
+    return true;
+  }
+
+  struct CompoundStatementInfo {
+    CompoundStatementInfo() : Lines(0), Statements(0), Branches(0) {}
+    unsigned Lines;
+    unsigned Statements;
+    unsigned Branches;
+  };
+  CompoundStatementInfo Info;
+  std::vector<bool> 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<CompoundStmt>("compound");
+
+  CompoundStatementASTVisitor Visitor;
+  Visitor.TraverseCompoundStatement(const_cast<CompoundStmt *>(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/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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to