Skip to content

Commit

Permalink
[clang-tidy] readability-function-size: add NestingThreshold param.
Browse files Browse the repository at this point in the history
Summary:
Finds compound statements which create next nesting level after `NestingThreshold` and emits a warning.
Do note that it warns about each compound statement that breaches the threshold, but not any of it's sub-statements, to have readable warnings.

I was able to find only one coding style referencing nesting:
  - https://www.kernel.org/doc/html/v4.10/process/coding-style.html#indentation
     > In short, 8-char indents make things easier to read, and have the added benefit of warning you when you’re nesting your functions too deep.

This seems too basic, i'm not sure what else to test. Are more tests needed?

Reviewers: alexfh, aaron.ballman, sbenza

Reviewed By: alexfh, aaron.ballman

Subscribers: xazax.hun

Tags: #clang-tools-extra

Differential Revision: https://reviews.llvm.org/D32942

llvm-svn: 305082
  • Loading branch information
LebedevRI committed Jun 9, 2017
1 parent 70db424 commit a1cee29
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 10 deletions.
34 changes: 28 additions & 6 deletions clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp
Expand Up @@ -38,6 +38,13 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
++Info.Branches;
// fallthrough
case Stmt::CompoundStmtClass:
// 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->getLocStart());

++CurrentNestingLevel;
TrackedParent.push_back(true);
break;
default:
Expand All @@ -47,7 +54,10 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {

Base::TraverseStmt(Node);

if (TrackedParent.back())
--CurrentNestingLevel;
TrackedParent.pop_back();

return true;
}

Expand All @@ -59,27 +69,31 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
}

struct FunctionInfo {
FunctionInfo() : Lines(0), Statements(0), Branches(0) {}
unsigned Lines;
unsigned Statements;
unsigned Branches;
unsigned Lines = 0;
unsigned Statements = 0;
unsigned Branches = 0;
unsigned NestingThreshold = 0;
std::vector<SourceLocation> NestingThresholders;
};
FunctionInfo Info;
std::vector<bool> TrackedParent;
unsigned CurrentNestingLevel = 0;
};

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)) {}
ParameterThreshold(Options.get("ParameterThreshold", -1U)),
NestingThreshold(Options.get("NestingThreshold", -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);
}

void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
Expand All @@ -90,6 +104,7 @@ 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;

Expand All @@ -109,7 +124,8 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {

if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
FI.Branches > BranchThreshold ||
ActualNumberParameters > ParameterThreshold) {
ActualNumberParameters > ParameterThreshold ||
!FI.NestingThresholders.empty()) {
diag(Func->getLocation(),
"function %0 exceeds recommended size/complexity thresholds")
<< Func;
Expand Down Expand Up @@ -138,6 +154,12 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
DiagnosticIDs::Note)
<< ActualNumberParameters << ParameterThreshold;
}

for (const auto &CSPos : FI.NestingThresholders) {
diag(CSPos, "nesting level %0 starts here (threshold %1)",
DiagnosticIDs::Note)
<< NestingThreshold + 1 << NestingThreshold;
}
}

} // namespace readability
Expand Down
9 changes: 7 additions & 2 deletions clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h
Expand Up @@ -27,8 +27,12 @@ namespace readability {
/// macro-heavy code. The default is `800`.
/// * `BranchThreshold` - flag functions exceeding this number of control
/// statements. The default is `-1` (ignore the number of branches).
/// * `ParameterThreshold` - flag functions having a high number of parameters.
/// The default is `6`.
/// * `ParameterThreshold` - flag functions having a high number of
/// parameters. The default is `-1` (ignore the number of parameters).
/// * `NestingThreshold` - flag compound statements which create next nesting
/// level after `NestingThreshold`. This may differ significantly from the
/// expected value for macro-heavy code. The default is `-1` (ignore the
/// nesting level).
class FunctionSizeCheck : public ClangTidyCheck {
public:
FunctionSizeCheck(StringRef Name, ClangTidyContext *Context);
Expand All @@ -42,6 +46,7 @@ class FunctionSizeCheck : public ClangTidyCheck {
const unsigned StatementThreshold;
const unsigned BranchThreshold;
const unsigned ParameterThreshold;
const unsigned NestingThreshold;
};

} // namespace readability
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -111,6 +111,11 @@ Improvements to clang-tidy
Finds possible inefficient vector operations in for loops that may cause
unnecessary memory reallocations.

- Added `NestingThreshold` to `readability-function-size
<http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check

Finds compound statements which create next nesting level after `NestingThreshold` and emits a warning.

- Added `ParameterThreshold` to `readability-function-size
<http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check

Expand Down
Expand Up @@ -28,5 +28,11 @@ Options

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

.. option:: NestingThreshold

Flag compound statements which create next nesting level after
`NestingThreshold`. This may differ significantly from the expected value
for macro-heavy code. The default is `-1` (ignore the nesting level).
32 changes: 31 additions & 1 deletion clang-tools-extra/test/clang-tidy/readability-function-size.cpp
@@ -1,4 +1,4 @@
// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}, {key: readability-function-size.ParameterThreshold, value: 5}]}' -- -std=c++11
// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}, {key: readability-function-size.ParameterThreshold, value: 5}, {key: readability-function-size.NestingThreshold, value: 2}]}' -- -std=c++11

// Bad formatting is intentional, don't run clang-format over the whole file!

Expand Down Expand Up @@ -59,3 +59,33 @@ void bar2() { class A { void barx() {;;} }; }
//
// CHECK-MESSAGES: :[[@LINE-4]]:30: warning: function 'barx' exceeds recommended size/complexity
// CHECK-MESSAGES: :[[@LINE-5]]:30: note: 2 statements (threshold 0)

#define macro() {int x; {int y; {int z;}}}

void baz0() { // 1
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'baz0' exceeds recommended size/complexity
// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 9 statements (threshold 0)
int a;
{ // 2
int b;
{ // 3
// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
int c;
{ // 4
int d;
}
}
}
{ // 2
int e;
}
{ // 2
{ // 3
// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
int j;
}
}
macro()
// CHECK-MESSAGES: :[[@LINE-1]]:3: note: nesting level 3 starts here (threshold 2)
// CHECK-MESSAGES: :[[@LINE-27]]:25: note: expanded from macro 'macro'
}

0 comments on commit a1cee29

Please sign in to comment.