diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt index 2cea862..f812976 100644 --- a/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tidy/modernize/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_library(clangTidyModernizeModule ReplaceAutoPtrCheck.cpp ShrinkToFitCheck.cpp UseAutoCheck.cpp + UseDefaultCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp diff --git a/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tidy/modernize/ModernizeTidyModule.cpp index b780cb8..d7463ab 100644 --- a/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -16,6 +16,7 @@ #include "ReplaceAutoPtrCheck.h" #include "ShrinkToFitCheck.h" #include "UseAutoCheck.h" +#include "UseDefaultCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" @@ -29,13 +30,13 @@ class ModernizeModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck("modernize-loop-convert"); - CheckFactories.registerCheck( - "modernize-make-unique"); + CheckFactories.registerCheck("modernize-make-unique"); CheckFactories.registerCheck("modernize-pass-by-value"); CheckFactories.registerCheck( "modernize-replace-auto-ptr"); CheckFactories.registerCheck("modernize-shrink-to-fit"); CheckFactories.registerCheck("modernize-use-auto"); + CheckFactories.registerCheck("modernize-use-default"); CheckFactories.registerCheck("modernize-use-nullptr"); CheckFactories.registerCheck("modernize-use-override"); } @@ -45,7 +46,7 @@ public: auto &Opts = Options.CheckOptions; Opts["modernize-loop-convert.MinConfidence"] = "reasonable"; Opts["modernize-loop-convert.NamingStyle"] = "CamelCase"; - Opts["modernize-pass-by-value.IncludeStyle"] = "llvm"; // Also: "google". + Opts["modernize-pass-by-value.IncludeStyle"] = "llvm"; // Also: "google". Opts["modernize-replace-auto-ptr.IncludeStyle"] = "llvm"; // Also: "google". // Comma-separated list of macros that behave like NULL. diff --git a/clang-tidy/modernize/UseDefaultCheck.cpp b/clang-tidy/modernize/UseDefaultCheck.cpp new file mode 100644 index 0000000..829371b --- /dev/null +++ b/clang-tidy/modernize/UseDefaultCheck.cpp @@ -0,0 +1,63 @@ +//===--- UseDefaultCheck.cpp - clang-tidy----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "UseDefaultCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static const char CtorDtor[] = "CtorDtorDecl"; + +void UseDefaultCheck::registerMatchers(MatchFinder *Finder) { + if (getLangOpts().CPlusPlus) { + Finder->addMatcher( + cxxConstructorDecl(isDefinition(), + unless(hasAnyConstructorInitializer(anything())), + parameterCountIs(0)) + .bind(CtorDtor), + this); + Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(CtorDtor), this); + } +} + +void UseDefaultCheck::check(const MatchFinder::MatchResult &Result) { + // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl. + const auto *CtorDtorDecl = Result.Nodes.getNodeAs(CtorDtor); + + // Discard explicitly deleted/defaulted constructors/destructors, those that + // are not user-provided (automatically generated constructor/destructor), and + // those with non-empty bodies. + if (CtorDtorDecl->isDeleted() || CtorDtorDecl->isExplicitlyDefaulted() || + !CtorDtorDecl->isUserProvided() || !CtorDtorDecl->hasTrivialBody()) + return; + + const auto *Body = dyn_cast(CtorDtorDecl->getBody()); + // This should never happen, since 'hasTrivialBody' checks that this is + // actually a CompoundStmt. + assert(Body && "Definition body is not a CompoundStmt"); + + diag(CtorDtorDecl->getLocStart(), + "use '= default' to define a trivial " + + std::string(dyn_cast(CtorDtorDecl) + ? "default constructor" + : "destructor")) + << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Body->getLBracLoc(), + Body->getRBracLoc()), + "= default;"); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/modernize/UseDefaultCheck.h b/clang-tidy/modernize/UseDefaultCheck.h new file mode 100644 index 0000000..74a9b5e --- /dev/null +++ b/clang-tidy/modernize/UseDefaultCheck.h @@ -0,0 +1,51 @@ +//===--- UseDefaultCheck.h - clang-tidy--------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// \brief Replace default bodies of special member functions with '= default;'. +/// \code +/// struct A { +/// A() {} +/// ~A(); +/// }; +/// A::~A() {} +/// \endcode +/// Is converted to: +/// \code +/// struct A { +/// A() = default; +/// ~A(); +/// }; +/// A::~A() = default; +/// \endcode +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default.html +class UseDefaultCheck : public ClangTidyCheck { +public: + UseDefaultCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H + diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst index d1dda8e..a2e4de2 100644 --- a/docs/clang-tidy/checks/list.rst +++ b/docs/clang-tidy/checks/list.rst @@ -55,6 +55,7 @@ List of clang-tidy Checks modernize-replace-auto-ptr modernize-shrink-to-fit modernize-use-auto + modernize-use-default modernize-use-nullptr modernize-use-override readability-braces-around-statements diff --git a/docs/clang-tidy/checks/modernize-use-default.rst b/docs/clang-tidy/checks/modernize-use-default.rst new file mode 100644 index 0000000..c52cf89 --- /dev/null +++ b/docs/clang-tidy/checks/modernize-use-default.rst @@ -0,0 +1,27 @@ +modernize-use-default +===================== + +This check replaces default bodies of special member functions with ``= +default;``. The explicitly defaulted function declarations enable more +opportunities in optimization, because the compiler might treat explicitly +defaulted functions as trivial. + +.. code-block:: c++ + + struct A { + A() {} + ~A(); + }; + A::~A() {} + + // becomes + + struct A { + A() = default; + ~A(); + }; + A::~A() = default; + +.. note:: + Copy-constructor, copy-assignment operator, move-constructor and + move-assignment operator are not supported yet. diff --git a/test/clang-tidy/modernize-use-default.cpp b/test/clang-tidy/modernize-use-default.cpp new file mode 100644 index 0000000..9e4a551 --- /dev/null +++ b/test/clang-tidy/modernize-use-default.cpp @@ -0,0 +1,137 @@ +// RUN: %python %S/check_clang_tidy.py %s modernize-use-default %t + +class A { +public: + A(); + ~A(); +}; + +A::A() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor [modernize-use-default] +// CHECK-FIXES: A::A() = default; +A::~A() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial destructor [modernize-use-default] +// CHECK-FIXES: A::~A() = default; + +// Inline definitions. +class B { +public: + B() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: B() = default; + ~B() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~B() = default; +}; + +void f(); + +class C { +public: + // Non-empty constructor body. + C() { f(); } + // Non-empty destructor body. + ~C() { f(); } +}; + +class D { +public: + // Constructor with initializer. + D() : Field(5) {} + // Constructor with arguments. + D(int Arg1, int Arg2) {} + int Field; +}; + +// Private constructor/destructor. +class E { + E() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: E() = default; + ~E() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~E() = default; +}; + +// struct. +struct F { + F() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: F() = default; + ~F() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: F() = default; +}; + +// Deleted constructor/destructor. +class G { +public: + G() = delete; + ~G() = delete; +}; + +// Do not remove other keywords. +class H { +public: + explicit H() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: explicit H() = default; + virtual ~H() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: virtual ~H() = default; +}; + +// Nested class. +struct I { + struct II { + II() {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' + // CHECK-FIXES: II() = default; + ~II() {} + // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' + // CHECK-FIXES: ~II() = default; + }; + int Int; +}; + +// Class template. +template +class J { +public: + J() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: J() = default; + ~J() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~J() = default; +}; + +// Non user-provided constructor/destructor. +struct K { + int Int; +}; +void g() { + K *PtrK = new K(); + PtrK->~K(); + delete PtrK; +} + +// Already using default. +struct L { + L() = default; + ~L() = default; +}; +struct M { + M(); + ~M(); +}; +M::M() = default; +M::~M() = default; + +// Delegating constructor and overriden destructor. +struct N : H { + N() : H() {} + ~N() override {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: ~N() override = default; +};