Skip to content

[clang-tidy] Improve readability-function-size: count class member initializers as statements #131669

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
return true;
}

bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
if (CountMemberInitAsStmt)
++Info.Statements;

Base::TraverseConstructorInitializer(Init);
return true;
}

struct FunctionInfo {
unsigned Lines = 0;
unsigned Statements = 0;
Expand All @@ -120,6 +128,7 @@ class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
llvm::BitVector TrackedParent;
unsigned StructNesting = 0;
unsigned CurrentNestingLevel = 0;
bool CountMemberInitAsStmt;
};

} // namespace
Expand All @@ -135,7 +144,9 @@ FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
NestingThreshold(
Options.get("NestingThreshold", DefaultNestingThreshold)),
VariableThreshold(
Options.get("VariableThreshold", DefaultVariableThreshold)) {}
Options.get("VariableThreshold", DefaultVariableThreshold)),
CountMemberInitAsStmt(
Options.get("CountMemberInitAsStmt", DefaultCountMemberInitAsStmt)) {}

void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "LineThreshold", LineThreshold);
Expand All @@ -144,6 +155,7 @@ void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "ParameterThreshold", ParameterThreshold);
Options.store(Opts, "NestingThreshold", NestingThreshold);
Options.store(Opts, "VariableThreshold", VariableThreshold);
Options.store(Opts, "CountMemberInitAsStmt", CountMemberInitAsStmt);
}

void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
Expand All @@ -160,6 +172,7 @@ void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {

FunctionASTVisitor Visitor;
Visitor.Info.NestingThreshold = NestingThreshold.value_or(-1);
Visitor.CountMemberInitAsStmt = CountMemberInitAsStmt;
Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
auto &FI = Visitor.Info;

Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-tidy/readability/FunctionSizeCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class FunctionSizeCheck : public ClangTidyCheck {
const std::optional<unsigned> ParameterThreshold;
const std::optional<unsigned> NestingThreshold;
const std::optional<unsigned> VariableThreshold;
const bool CountMemberInitAsStmt;

static constexpr std::optional<unsigned> DefaultLineThreshold = std::nullopt;
static constexpr std::optional<unsigned> DefaultStatementThreshold = 800U;
Expand All @@ -58,6 +59,7 @@ class FunctionSizeCheck : public ClangTidyCheck {
std::nullopt;
static constexpr std::optional<unsigned> DefaultVariableThreshold =
std::nullopt;
static constexpr bool DefaultCountMemberInitAsStmt = true;
};

} // namespace clang::tidy::readability
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ Changes in existing checks
tolerating fix-it breaking compilation when functions is used as pointers
to avoid matching usage of functions within the current compilation unit.

- Improved :doc:`readability-function-size
<clang-tidy/checks/readability/function-size>` check by adding new option
`CountMemberInitAsStmt` that allows counting class member initializers in
constructors as statements.

- Improved :doc:`readability-math-missing-parentheses
<clang-tidy/checks/readability/math-missing-parentheses>` check by fixing
false negatives where math expressions are the operand of assignment operators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ Options
The default is `none` (ignore the number of variables).
Please note that function parameters and variables declared in lambdas,
GNU Statement Expressions, and nested class inline functions are not counted.

.. option:: CountMemberInitAsStmt

When `true`, count class member initializers in constructors as statements.
Default is `true`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// RUN: %check_clang_tidy %s readability-function-size %t -- \
// RUN: -config='{CheckOptions: { \
// RUN: readability-function-size.LineThreshold: 0, \
// RUN: readability-function-size.StatementThreshold: 0, \
// RUN: readability-function-size.BranchThreshold: 0, \
// RUN: readability-function-size.ParameterThreshold: 5, \
// RUN: readability-function-size.NestingThreshold: 2, \
// RUN: readability-function-size.VariableThreshold: 1, \
// RUN: readability-function-size.CountMemberInitAsStmt: false \
// RUN: }}'

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

void foo1() {
}

void foo2() {;}
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo2' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0)

struct A {
A(int c, int d) : a(0), b(c) { ; }
int a;
int b;
};
// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: function 'A' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-5]]:3: note: 1 statements (threshold 0)

struct B {
B(int x, int y, int z) : a(x + y * z), b(), c_a(y, z) {
;
}
int a;
int b;
A c_a;
};
// CHECK-MESSAGES: :[[@LINE-7]]:3: warning: function 'B' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: 2 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: 1 statements (threshold 0)

struct C : A, B {
// 0 statements
C() : A(0, 4), B(1, 2, 3) {}
};

template<typename T>
struct TemplateC {
// 0 statements
TemplateC() : a(3) {}
T a;
};

template<typename T>
struct TemplateD {
template<typename U>
TemplateD(U&& val) : member(val) {
;
}

T member;
};
// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: function 'TemplateD<T>' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-7]]:3: note: 2 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: 1 statements (threshold 0)

void instantiate() {
TemplateC<int> c;
TemplateD<int> d(5);
}
// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'instantiate' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 2 statements (threshold 0)
// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 2 variables (threshold 1)
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,60 @@ void variables_16() {
// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 4 statements (threshold 0)
// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 2 variables (threshold 1)

struct A {
A(int c, int d) : a(0), b(c) { ; }
int a;
int b;
};
// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: function 'A' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-5]]:3: note: 3 statements (threshold 0)

struct B {
B(int x, int y, int z) : a(x + y * z), b(), c_a(y, z) {
;
}
int a;
int b;
A c_a;
};
// CHECK-MESSAGES: :[[@LINE-7]]:3: warning: function 'B' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: 2 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-9]]:3: note: 4 statements (threshold 0)

struct C : A, B {
C() : A(0, 4), B(1, 2, 3) {}
};
// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: function 'C' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-3]]:3: note: 2 statements (threshold 0)

template<typename T>
struct TemplateC {
// 0 statements
TemplateC() : a(3) {}
T a;
};
// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: function 'TemplateC<T>' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-4]]:3: note: 1 statements (threshold 0)

template<typename T>
struct TemplateD {
template<typename U>
TemplateD(U&& val) : member(val) {
;
}

T member;
};
// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: function 'TemplateD<T>' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-7]]:3: note: 2 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-8]]:3: note: 2 statements (threshold 0)

void instantiate() {
TemplateC<int> c;
TemplateD<int> d(5);
}
// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'instantiate' exceeds recommended size/complexity thresholds [readability-function-size]
// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 2 statements (threshold 0)
// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 2 variables (threshold 1)