Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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
- Loading branch information
Showing
9 changed files
with
906 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
241 changes: 241 additions & 0 deletions
241
clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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
45
clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
clang-tools-extra/docs/clang-tidy/checks/modernize-use-default-member-init.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}; |
Oops, something went wrong.