diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.h @@ -0,0 +1,38 @@ +//===--- AvoidInitDefaultConstructorsCheck.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_CPPCOREGUIDELINES_AVOIDINITDEFAULTCONSTRUCTORSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDINITDEFAULTCONSTRUCTORSCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Finds constructors which just initialize class members, so they can be +/// replaced with in-class member initializers. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-avoid-init-default-constructors.html +class AvoidInitDefaultConstructorsCheck : public ClangTidyCheck { +public: + AvoidInitDefaultConstructorsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDINITDEFAULTCONSTRUCTORSCHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidInitDefaultConstructorsCheck.cpp @@ -0,0 +1,45 @@ +//===--- AvoidInitDefaultConstructorsCheck.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 "AvoidInitDefaultConstructorsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +void AvoidInitDefaultConstructorsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxConstructorDecl( + hasBody(compoundStmt(unless(hasAnySubstatement(anything())))), + isDefaultConstructor(), + unless(hasAnyConstructorInitializer(cxxCtorInitializer( + withInitializer(hasDescendant(declRefExpr(to(parmVarDecl()))))))), + hasAnyConstructorInitializer( + forField(unless(hasInClassInitializer(anything()))))) + .bind("Constructor"), + this); +} + +void AvoidInitDefaultConstructorsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedConstructor = + Result.Nodes.getNodeAs("Constructor"); + + diag(MatchedConstructor->getLocation(), + "Default constructors like %0 can be omitted. Use in-class member " + "initializers instead") + << MatchedConstructor; +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule AvoidGotoCheck.cpp + AvoidInitDefaultConstructorsCheck.cpp AvoidNonConstGlobalVariablesCheck.cpp CppCoreGuidelinesTidyModule.cpp InitVariablesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -15,6 +15,7 @@ #include "../modernize/UseOverrideCheck.h" #include "../readability/MagicNumbersCheck.h" #include "AvoidGotoCheck.h" +#include "AvoidInitDefaultConstructorsCheck.h" #include "AvoidNonConstGlobalVariablesCheck.h" #include "InitVariablesCheck.h" #include "InterfacesGlobalInitCheck.h" @@ -48,6 +49,8 @@ "cppcoreguidelines-avoid-c-arrays"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-goto"); + CheckFactories.registerCheck( + "cppcoreguidelines-avoid-init-default-constructors"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-magic-numbers"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -119,6 +119,12 @@ Finds calls to ``new`` with missing exception handler for ``std::bad_alloc``. +- New :doc:`cppcoreguidelines-avoid-init-default-constructors + ` check. + + Finds constructors which just initialize class members, so they can be + replaced with in-class member initializers. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-init-default-constructors.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-init-default-constructors.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-avoid-init-default-constructors.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - cppcoreguidelines-avoid-init-default-constructors + +cppcoreguidelines-avoid-init-default-constructors +================================================================== + +This check flags constructors which are only initializing class members. +Such constructors can be omitted, because the initialization could be +done with in-class member initializers. +Constructors implicitly generated by the compiler could be more efficient. +Furthermore, removing unnecessary code improves readability. + +.. code-block:: c++ + + class C { + int z; + + C() : z{1} {} // Can be omitted + }; + + // Better + class C { + int z{1}; + }; + +This check implements rule `C.45 `_ +of the C++ Core Guidelines. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -145,6 +145,7 @@ `concurrency-mt-unsafe `_, `concurrency-thread-canceltype-asynchronous `_, `cppcoreguidelines-avoid-goto `_, + `cppcoreguidelines-avoid-init-default-constructors `_, `cppcoreguidelines-avoid-non-const-global-variables `_, `cppcoreguidelines-init-variables `_, "Yes" `cppcoreguidelines-interfaces-global-init `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-init-default-constructors.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-init-default-constructors.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-avoid-init-default-constructors.cpp @@ -0,0 +1,66 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-avoid-init-default-constructors %t + +class NoDefaultCtor { + int z; + + NoDefaultCtor(int a) : z{1} {} +}; + +class HasBody { + int z; + + HasBody() : z{1} { ; } +}; + +class OneParameterOneInitList { + int z; + int y; + + OneParameterOneInitList(int a = 1) : z{a}, y{1} {} +}; + +// Test needed because class member initializations are listed as ``cxxCtorInitializer`` in clang's AST +class OnlyInClassMemberInit { + int z{1}; + int y = 1; + + OnlyInClassMemberInit() {} +}; + +// User may wants explicitly overwrite the class member value in the constructor +class ExplicitOverwrite { + int z{1}; + + ExplicitOverwrite() : z{2} {} +}; + +class HasBodyInit { + int z; + + HasBodyInit() { + z = 1; + } +}; + +class OneInitList { + int z; + + OneInitList() : z{1} {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Default constructors like 'OneInitList' can be omitted. Use in-class member initializers instead [cppcoreguidelines-avoid-init-default-constructors] +}; + +class OneDirectInit { + int z; + + OneDirectInit() : z(1) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Default constructors like 'OneDirectInit' can be omitted. Use in-class member initializers instead [cppcoreguidelines-avoid-init-default-constructors] +}; + +class OneInitListOneDirectInit { + int z; + int y; + int w; + + OneInitListOneDirectInit() : z{1}, y(2) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: Default constructors like 'OneInitListOneDirectInit' can be omitted. Use in-class member initializers instead [cppcoreguidelines-avoid-init-default-constructors] +};