diff options
author | Malcolm Parsons <malcolm.parsons@gmail.com> | 2016-12-20 21:26:07 +0000 |
---|---|---|
committer | Malcolm Parsons <malcolm.parsons@gmail.com> | 2016-12-20 21:26:07 +0000 |
commit | d5508b4e89f8e3dcbf83d9eadbb77b2420ea5202 (patch) | |
tree | 75f689e0508a9a83803942bb3ca1ef7fb3fba693 /clang-tools-extra/clang-tidy/modernize | |
parent | e3be61c1393604e9e0efa98c4c2352c05b32f61e (diff) | |
download | bcm5719-llvm-d5508b4e89f8e3dcbf83d9eadbb77b2420ea5202.tar.gz bcm5719-llvm-d5508b4e89f8e3dcbf83d9eadbb77b2420ea5202.zip |
[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
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize')
4 files changed, 290 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 6d178e0a845..22c7d8d5b1e 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangTidyModernizeModule ShrinkToFitCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp + UseDefaultMemberInitCheck.cpp UseEmplaceCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 07b450dcf50..3274f5d60b4 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -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" @@ -56,6 +57,8 @@ public: 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>( 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 00000000000..eb084efcdfa --- /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 diff --git a/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.h b/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.h new file mode 100644 index 00000000000..6d3b19b6845 --- /dev/null +++ b/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 |