angelgarcia created this revision.
angelgarcia added a reviewer: klimek.
angelgarcia added subscribers: cfe-commits, alexfh.

Add a check that replaces empty bodies of special member functions with '= 
default;'.
For now, it is only implemented for the default constructor and the destructor, 
which are the easier cases.
The copy-constructor and the copy-assignment operator cases will be implemented 
later.

I applied this check to the llvm code base and found 627 warnings (385 in llvm, 
9 in compiler-rt, 220 in clang and 13 in clang-tools-extra).
Applying the fixes didn't break any build or test, it only caused a -Wpedantic 
warning in lib/Target/Mips/MipsOptionRecord.h:33 becaused it replaced
virtual ~MipsOptionRecord(){}; to virtual ~MipsOptionRecord()= default;;

http://reviews.llvm.org/D13871

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/UseDefaultCheck.cpp
  clang-tidy/modernize/UseDefaultCheck.h
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/modernize-use-default.rst
  test/clang-tidy/modernize-use-default.cpp

Index: test/clang-tidy/modernize-use-default.cpp
===================================================================
--- /dev/null
+++ 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 default constructor/destructor [modernize-use-default]
+// CHECK-FIXES: A::A() = default;
+A::~A() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: 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 T>
+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;
+};
Index: docs/clang-tidy/checks/modernize-use-default.rst
===================================================================
--- /dev/null
+++ 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.
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -54,6 +54,7 @@
    modernize-replace-auto-ptr
    modernize-shrink-to-fit
    modernize-use-auto
+   modernize-use-default
    modernize-use-nullptr
    modernize-use-override
    readability-braces-around-statements
Index: clang-tidy/modernize/UseDefaultCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseDefaultCheck.h
@@ -0,0 +1,50 @@
+//===--- 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 {
+
+/// \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 tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_H
+
Index: clang-tidy/modernize/UseDefaultCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseDefaultCheck.cpp
@@ -0,0 +1,60 @@
+//===--- 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 {
+
+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<CXXMethodDecl>(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<CompoundStmt>(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 default constructor/destructor")
+      << FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(Body->getLBracLoc(),
+                                         Body->getRBracLoc()),
+          "= default;");
+  // FIXME: this can generate a -Wpedantic warning if there is a semicolon after
+  // the body: "A() {};" is converted to "A() = default;;"
+}
+
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ 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 @@
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
-    CheckFactories.registerCheck<MakeUniqueCheck>(
-        "modernize-make-unique");
+    CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
     CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
     CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
         "modernize-replace-auto-ptr");
     CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
     CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
+    CheckFactories.registerCheck<UseDefaultCheck>("modernize-use-default");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
   }
@@ -45,7 +46,7 @@
     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.
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -9,6 +9,7 @@
   ReplaceAutoPtrCheck.cpp
   ShrinkToFitCheck.cpp
   UseAutoCheck.cpp
+  UseDefaultCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to