Skip to content

Commit

Permalink
[clang-tidy] Add modernize-use-default-member-init check
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pepsiman committed Dec 20, 2016
1 parent e3be61c commit d5508b4
Show file tree
Hide file tree
Showing 9 changed files with 906 additions and 0 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
Expand Up @@ -16,6 +16,7 @@ add_clang_library(clangTidyModernizeModule
ShrinkToFitCheck.cpp
UseAutoCheck.cpp
UseBoolLiteralsCheck.cpp
UseDefaultMemberInitCheck.cpp
UseEmplaceCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
Expand Down
Expand Up @@ -22,6 +22,7 @@
#include "ShrinkToFitCheck.h"
#include "UseAutoCheck.h"
#include "UseBoolLiteralsCheck.h"
#include "UseDefaultMemberInitCheck.h"
#include "UseEmplaceCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
Expand Down Expand Up @@ -56,6 +57,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
CheckFactories.registerCheck<UseBoolLiteralsCheck>(
"modernize-use-bool-literals");
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
"modernize-use-default-member-init");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
Expand Down
241 changes: 241 additions & 0 deletions 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
45 changes: 45 additions & 0 deletions clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.h
@@ -0,0 +1,45 @@
//===--- UseDefaultMemberInitCheck.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_MEMBER_INIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H

#include "../ClangTidy.h"

namespace clang {
namespace tidy {
namespace modernize {

/// Convert a default constructor's member initializers into default member
/// initializers. Remove member initializers that are the same as a default
/// member initializer.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html
class UseDefaultMemberInitCheck : public ClangTidyCheck {
public:
UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;

private:
void checkDefaultInit(const ast_matchers::MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init);
void checkExistingInit(const ast_matchers::MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init);

const bool UseAssignment;
};

} // namespace modernize
} // namespace tidy
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Expand Up @@ -108,6 +108,12 @@ Improvements to clang-tidy

- The modernize-use-default check has been renamed to `modernize-use-equals-default
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html>`_.

- New `modernize-use-default-member-init
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html>`_ check

Converts a default constructor's member initializers into default member initializers.
Removes member initializers that are the same as a default member initializer.

- New `modernize-use-equals-delete
<http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html>`_ check
Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Expand Up @@ -110,6 +110,7 @@ Clang-Tidy Checks
modernize-shrink-to-fit
modernize-use-auto
modernize-use-bool-literals
modernize-use-default-member-init
modernize-use-emplace
modernize-use-equals-default
modernize-use-equals-delete
Expand Down
@@ -0,0 +1,49 @@
.. title:: clang-tidy - modernize-use-default-member-init

modernize-use-default-member-init
=================================

This check converts a default constructor's member initializers into the new
default member initializers in C++11. Other member initializers that match the
default member initializer are removed. This can reduce repeated code or allow
use of '= default'.

.. code-block:: c++

struct A {
A() : i(5), j(10.0) {}
A(int i) : i(i), j(10.0) {}
int i;
double j;
};

// becomes

struct A {
A() {}
A(int i) : i(i) {}
int i{5};
double j{10.0};
};

.. note::
Only converts member initializers for built-in types, enums, and pointers.
The `readability-redundant-member-init` check will remove redundant member
initializers for classes.

Options
-------

.. option:: UseAssignment

If this option is set to non-zero (default is `0`), the check will initialise
members with an assignment. For example:

.. code-block:: c++

struct A {
A() {}
A(int i) : i(i) {}
int i = 5;
double j = 10.0;
};

0 comments on commit d5508b4

Please sign in to comment.