baloghadamsoftware created this revision. baloghadamsoftware added reviewers: alexfh, aaron.ballman. baloghadamsoftware added a project: clang-tools-extra. Herald added subscribers: mgehre, Szelethus, rnkovacs, xazax.hun, mgorny. Herald added a project: clang.
Finds member initializations in the constructor body which can be placed into the initialization list instead. This does not only improves the readability of the code but also affects positively its performance. Class-member assignments inside a control statement or following the first control statement are ignored. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D71199 Files: clang-tools-extra/clang-tidy/readability/CMakeLists.txt clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.cpp clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.h clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/list.rst clang-tools-extra/docs/clang-tidy/checks/readability-prefer-initialization-list.rst clang-tools-extra/test/clang-tidy/checkers/readability-prefer-initialization-list.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/readability-prefer-initialization-list.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/readability-prefer-initialization-list.cpp @@ -0,0 +1,225 @@ +// RUN: %check_clang_tidy %s readability-prefer-initialization-list %t + +class Simple1 { + int n; + double x; + +public: + Simple1() { + n = 0; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: n can be initialized in the initializer list of the constructor [readability-prefer-initialization-list] + x = 0.0; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: x can be initialized in the initializer list of the constructor [readability-prefer-initialization-list] + } + + ~Simple1() = default; +}; + +class Simple2 { + int n; + double x; + +public: + Simple2() : n (0) { + x = 0.0; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: x can be initialized in the initializer list of the constructor [readability-prefer-initialization-list] + } + + ~Simple2() = default; +}; + +class Simple3 { + int n; + double x; + +public: + Simple3() : x (0.0) { + n = 0; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: n can be initialized in the initializer list of the constructor [readability-prefer-initialization-list] + } + + ~Simple3() = default; +}; + +static bool dice(); + +class Complex1 { + int n; + int m; + +public: + Complex1() : n(0) { + if (dice()) + m = 1; +// NO-MESSAGES: initialization of m is nested in a conditional expression + } + + ~Complex1() = default; +}; + +class Complex2 { + int n; + int m; + +public: + Complex2() : n(0) { + if (!dice()) + return; + m = 1; +// NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex2() = default; +}; + +class Complex3 { + int n; + int m; + +public: + Complex3() : n(0) { + while (dice()) + m = 1; +// NO-MESSAGES: initialization of m is nested in a conditional expression + } + + ~Complex3() = default; +}; + +class Complex4 { + int n; + int m; + +public: + Complex4() : n(0) { + while (!dice()) + return; + m = 1; +// NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex4() = default; +}; + +class Complex5 { + int n; + int m; + +public: + Complex5() : n(0) { + do { + m = 1; +// NO-MESSAGES: initialization of m is nested in a conditional expression + } while (dice()); + } + + ~Complex5() = default; +}; + +class Complex6 { + int n; + int m; + +public: + Complex6() : n(0) { + do { + return; + } while (!dice()); + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex6() = default; +}; + +class Complex7 { + int n; + int m; + +public: + Complex7() : n(0) { + switch (dice()) { + case 1: + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + break; + default: + break; + } + } + + ~Complex7() = default; +}; + +class Complex8 { + int n; + int m; + +public: + Complex8() : n(0) { + switch (dice()) { + case 1: + return; + break; + default: + break; + } + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex8() = default; +}; + +class E {}; +void risky(); // may throw + +class Complex9 { + int n; + int m; + +public: + Complex9() : n(0) { + try { + risky(); + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + } catch (const E& e) { + return; + } + } + + ~Complex9() = default; +}; + +class Complex10 { + int n; + int m; + +public: + Complex10() : n(0) { + try { + risky(); + } catch (const E& e) { + return; + } + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex10() = default; +}; + +class Complex11 { + int n; + int m; + +public: + Complex11() : n(0) { + return; + m = 1; + // NO-MESSAGES: initialization of m follows a conditional expression + } + + ~Complex11() = default; +}; Index: clang-tools-extra/docs/clang-tidy/checks/readability-prefer-initialization-list.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/readability-prefer-initialization-list.rst @@ -0,0 +1,36 @@ +.. title:: clang-tidy - readability-prefer-initialization-list + +readability-prefer-initialization-list +====================================== + +Finds member initializations in the constructor body which can be placed into +the initialization list instead. This does not only improves the readability of +the code but also affects positively its performance. Class-member assignments +inside a control statement or following the first control statement are ignored. + +Example: + +.. code-block:: c++ + + class C { + int n; + int m; + public: + C() { + n = 1; + if (dice()) + return; + m = 1; + } + }; + +Here ``n`` can be initialized in the construcotr list, but ``m`` not, because +its initialization follow a control statement (``if``): + +.. code-block:: c++ + + C() : n(1) { + if (dice()) + return; + m = 1; + } Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -370,6 +370,7 @@ readability-misplaced-array-index readability-named-parameter readability-non-const-parameter + readability-prefer-initialization-list readability-redundant-access-specifiers readability-redundant-control-flow readability-redundant-declaration Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -167,6 +167,12 @@ <clang-tidy/checks/modernize-use-equals-default>` fix no longer adds semicolons where they would be redundant. +- New :doc:`readability-prefer-initialization-list + <clang-tidy/checks/readability-prefer-initialization-list>` check. + + Finds member initializations in the constructor body which can be placed into + the initialization list instead. + - New :doc:`readability-redundant-access-specifiers <clang-tidy/checks/readability-redundant-access-specifiers>` check. Index: clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -28,6 +28,7 @@ #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" #include "NonConstParameterCheck.h" +#include "PreferInitializationListCheck.h" #include "RedundantAccessSpecifiersCheck.h" #include "RedundantControlFlowCheck.h" #include "RedundantDeclarationCheck.h" @@ -86,6 +87,8 @@ "readability-misleading-indentation"); CheckFactories.registerCheck<MisplacedArrayIndexCheck>( "readability-misplaced-array-index"); + CheckFactories.registerCheck<PreferInitializationListCheck>( + "readability-prefer-initialization-list"); CheckFactories.registerCheck<RedundantAccessSpecifiersCheck>( "readability-redundant-access-specifiers"); CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>( Index: clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.h @@ -0,0 +1,34 @@ +//===--- PreferInitializationListCheck.h - clang-tidy -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_PREFERINITIALIZATIONLISTCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_PREFERINITIALIZATIONLISTCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-prefer-initialization-list.html +class PreferInitializationListCheck : public ClangTidyCheck { +public: + PreferInitializationListCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_PREFERINITIALIZATIONLISTCHECK_H Index: clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/readability/PreferInitializationListCheck.cpp @@ -0,0 +1,93 @@ +//===--- PreferInitializationListCheck.cpp - clang-tidy -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "PreferInitializationListCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +static bool isControlStatement(const Stmt *S) { + return isa<IfStmt>(S) || + isa<SwitchStmt>(S) || + isa<ForStmt>(S) || + isa<WhileStmt>(S) || + isa<DoStmt>(S) || + isa<ReturnStmt>(S) || + isa<CXXTryStmt>(S); +} + +static const FieldDecl *isAssignmentToMemberOf(const RecordDecl *Rec, + const Stmt *S) { + if (const auto *BO = dyn_cast<BinaryOperator>(S)) { + if (BO->getOpcode() != BO_Assign) + return nullptr; + + const auto *ME = dyn_cast<MemberExpr>(BO->getLHS()->IgnoreParenImpCasts()); + if (!ME) + return nullptr; + + const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl()); + if (!Field) + return nullptr; + + if (isa<CXXThisExpr>(ME->getBase())) + return Field; + } else if (const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) { + if (COCE->getOperator() != OO_Equal) + return nullptr; + + const auto *ME = + dyn_cast<MemberExpr>(COCE->getArg(0)->IgnoreParenImpCasts()); + if (!ME) + return nullptr; + + const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl()); + if (!Field) + return nullptr; + + if (isa<CXXThisExpr>(ME->getBase())) + return Field; + } + + return nullptr; +} + +void PreferInitializationListCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cxxConstructorDecl().bind("ctor"), this); +} + +void PreferInitializationListCheck::check( + const MatchFinder::MatchResult &Result) { + // FIXME: Add callback implementation. + const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); + + const auto *Body = dyn_cast_or_null<CompoundStmt>(Ctor->getBody()); + if (!Body) + return; + + const CXXRecordDecl *Class = Ctor->getParent(); + + for (const auto *S: Body->body()) { + if (isControlStatement(S)) + return; + + if (const NamedDecl* Mbr = isAssignmentToMemberOf(Class, S)) { + diag(S->getBeginLoc(), "%0 can be initialized in the initializer list" + " of the constructor") << Mbr->getName(); + } + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tools-extra/clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -21,6 +21,7 @@ NamedParameterCheck.cpp NamespaceCommentCheck.cpp NonConstParameterCheck.cpp + PreferInitializationListCheck.cpp ReadabilityTidyModule.cpp RedundantAccessSpecifiersCheck.cpp RedundantControlFlowCheck.cpp
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits