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 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(S) || + isa(S) || + isa(S) || + isa(S) || + isa(S) || + isa(S) || + isa(S); +} + +static const FieldDecl *isAssignmentToMemberOf(const RecordDecl *Rec, + const Stmt *S) { + if (const auto *BO = dyn_cast(S)) { + if (BO->getOpcode() != BO_Assign) + return nullptr; + + const auto *ME = dyn_cast(BO->getLHS()->IgnoreParenImpCasts()); + if (!ME) + return nullptr; + + const auto *Field = dyn_cast(ME->getMemberDecl()); + if (!Field) + return nullptr; + + if (isa(ME->getBase())) + return Field; + } else if (const auto *COCE = dyn_cast(S)) { + if (COCE->getOperator() != OO_Equal) + return nullptr; + + const auto *ME = + dyn_cast(COCE->getArg(0)->IgnoreParenImpCasts()); + if (!ME) + return nullptr; + + const auto *Field = dyn_cast(ME->getMemberDecl()); + if (!Field) + return nullptr; + + if (isa(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("ctor"); + + const auto *Body = dyn_cast_or_null(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/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( "readability-misplaced-array-index"); + CheckFactories.registerCheck( + "readability-prefer-initialization-list"); CheckFactories.registerCheck( "readability-redundant-access-specifiers"); CheckFactories.registerCheck( Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -167,6 +167,12 @@ ` fix no longer adds semicolons where they would be redundant. +- New :doc:`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 ` check. 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/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/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; +};