[clang-tidy] Add modernize-use-default-member-init check

Summary: Fixes PR18858

Reviewers: alexfh, hokein, aaron.ballman

Subscribers: JDevlieghere, Eugene.Zelenko, Prazek, mgorny, cfe-commits, modocache

Differential Revision: https://reviews.llvm.org/D26750

llvm-svn: 290202
diff --git a/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
new file mode 100644
index 0000000..eb084ef
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
@@ -0,0 +1,241 @@
+//===--- UseDefaultMemberInitCheck.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 "UseDefaultMemberInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static StringRef getValueOfValueInit(const QualType InitType) {
+  switch (InitType->getScalarTypeKind()) {
+  case Type::STK_CPointer:
+  case Type::STK_BlockPointer:
+  case Type::STK_ObjCObjectPointer:
+  case Type::STK_MemberPointer:
+    return "nullptr";
+
+  case Type::STK_Bool:
+    return "false";
+
+  case Type::STK_Integral:
+    switch (InitType->getAs<BuiltinType>()->getKind()) {
+    case BuiltinType::Char_U:
+    case BuiltinType::UChar:
+    case BuiltinType::Char_S:
+    case BuiltinType::SChar:
+      return "'\\0'";
+    case BuiltinType::WChar_U:
+    case BuiltinType::WChar_S:
+      return "L'\\0'";
+    case BuiltinType::Char16:
+      return "u'\\0'";
+    case BuiltinType::Char32:
+      return "U'\\0'";
+    default:
+      return "0";
+    }
+
+  case Type::STK_Floating:
+    switch (InitType->getAs<BuiltinType>()->getKind()) {
+    case BuiltinType::Half:
+    case BuiltinType::Float:
+      return "0.0f";
+    default:
+      return "0.0";
+    }
+
+  case Type::STK_FloatingComplex:
+  case Type::STK_IntegralComplex:
+    return getValueOfValueInit(
+        InitType->getAs<ComplexType>()->getElementType());
+  }
+  llvm_unreachable("Invalid scalar type kind");
+}
+
+static bool isZero(const Expr *E) {
+  switch (E->getStmtClass()) {
+  case Stmt::CXXNullPtrLiteralExprClass:
+  case Stmt::ImplicitValueInitExprClass:
+    return true;
+  case Stmt::InitListExprClass:
+    return cast<InitListExpr>(E)->getNumInits() == 0;
+  case Stmt::CharacterLiteralClass:
+    return !cast<CharacterLiteral>(E)->getValue();
+  case Stmt::CXXBoolLiteralExprClass:
+    return !cast<CXXBoolLiteralExpr>(E)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return !cast<IntegerLiteral>(E)->getValue();
+  case Stmt::FloatingLiteralClass: {
+    llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
+    return Value.isZero() && !Value.isNegative();
+  }
+  default:
+    return false;
+  }
+}
+
+static const Expr *ignoreUnaryPlus(const Expr *E) {
+  auto *UnaryOp = dyn_cast<UnaryOperator>(E);
+  if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
+    return UnaryOp->getSubExpr();
+  return E;
+}
+
+static const Expr *getInitializer(const Expr *E) {
+  auto *InitList = dyn_cast<InitListExpr>(E);
+  if (InitList && InitList->getNumInits() == 1)
+    return InitList->getInit(0);
+  return E;
+}
+
+static bool sameValue(const Expr *E1, const Expr *E2) {
+  E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
+  E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
+
+  if (isZero(E1) && isZero(E2))
+    return true;
+
+  if (E1->getStmtClass() != E2->getStmtClass())
+    return false;
+
+  switch (E1->getStmtClass()) {
+  case Stmt::UnaryOperatorClass:
+    return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
+                     cast<UnaryOperator>(E2)->getSubExpr());
+  case Stmt::CharacterLiteralClass:
+    return cast<CharacterLiteral>(E1)->getValue() ==
+           cast<CharacterLiteral>(E2)->getValue();
+  case Stmt::CXXBoolLiteralExprClass:
+    return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
+           cast<CXXBoolLiteralExpr>(E2)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return cast<IntegerLiteral>(E1)->getValue() ==
+           cast<IntegerLiteral>(E2)->getValue();
+  case Stmt::FloatingLiteralClass:
+    return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
+        cast<FloatingLiteral>(E2)->getValue());
+  case Stmt::StringLiteralClass:
+    return cast<StringLiteral>(E1)->getString() ==
+           cast<StringLiteral>(E2)->getString();
+  case Stmt::DeclRefExprClass:
+    return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
+  default:
+    return false;
+  }
+}
+
+UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UseAssignment(Options.get("UseAssignment", 0) != 0) {}
+
+void UseDefaultMemberInitCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UseAssignment", UseAssignment);
+}
+
+AST_MATCHER(FieldDecl, hasInClassInitializer) {
+  return Node.hasInClassInitializer();
+}
+
+void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  auto Init =
+      anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
+            unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+                          hasUnaryOperand(integerLiteral())),
+            floatLiteral(),
+            unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+                          hasUnaryOperand(floatLiteral())),
+            cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
+            declRefExpr());
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          isDefaultConstructor(), unless(isInstantiated()),
+          forEachConstructorInitializer(allOf(
+              forField(unless(anyOf(isBitField(), hasInClassInitializer()))),
+              cxxCtorInitializer(isWritten(),
+                                 withInitializer(ignoringImplicit(Init)))
+                  .bind("default")))),
+      this);
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          unless(ast_matchers::isTemplateInstantiation()),
+          forEachConstructorInitializer(
+              allOf(forField(hasInClassInitializer()),
+                    cxxCtorInitializer(isWritten(),
+                                       withInitializer(ignoringImplicit(Init)))
+                        .bind("existing")))),
+      this);
+}
+
+void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Default =
+          Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
+    checkDefaultInit(Result, Default);
+  else if (const auto *Existing =
+               Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
+    checkExistingInit(Result, Existing);
+  else
+    llvm_unreachable("Bad Callback. No node provided.");
+}
+
+void UseDefaultMemberInitCheck::checkDefaultInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  SourceLocation FieldEnd =
+      Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
+                                 *Result.SourceManager, getLangOpts());
+  SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
+      Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
+  CharSourceRange InitRange =
+      CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
+
+  auto Diag =
+      diag(Field->getLocation(), "use default member initializer for %0")
+      << Field
+      << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
+      << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
+
+  if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
+    Diag << FixItHint::CreateInsertion(
+        FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
+
+  if (!UseAssignment)
+    Diag << FixItHint::CreateInsertion(FieldEnd, "}");
+
+  Diag << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+void UseDefaultMemberInitCheck::checkExistingInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
+    return;
+
+  diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
+      << Field
+      << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang