| //===--- 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), |
| IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {} |
| |
| void UseDefaultMemberInitCheck::storeOptions( |
| ClangTidyOptions::OptionMap &Opts) { |
| Options.store(Opts, "UseAssignment", UseAssignment); |
| Options.store(Opts, "IgnoreMacros", IgnoreMacros); |
| } |
| |
| 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(to(enumConstantDecl()))); |
| |
| Finder->addMatcher( |
| cxxConstructorDecl( |
| isDefaultConstructor(), unless(isInstantiated()), |
| forEachConstructorInitializer( |
| cxxCtorInitializer( |
| forField(unless(anyOf(getLangOpts().CPlusPlus2a |
| ? unless(anything()) |
| : isBitField(), |
| hasInClassInitializer(anything()), |
| hasParent(recordDecl(isUnion()))))), |
| isWritten(), withInitializer(ignoringImplicit(Init))) |
| .bind("default"))), |
| this); |
| |
| Finder->addMatcher( |
| cxxConstructorDecl( |
| unless(ast_matchers::isTemplateInstantiation()), |
| forEachConstructorInitializer( |
| cxxCtorInitializer(forField(hasInClassInitializer(anything())), |
| 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->getAnyMember(); |
| |
| SourceLocation StartLoc = Field->getLocStart(); |
| if (StartLoc.isMacroID() && IgnoreMacros) |
| return; |
| |
| 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 |