Malcolm Parsons | d5508b4 | 2016-12-20 21:26:07 +0000 | [diff] [blame] | 1 | //===--- UseDefaultMemberInitCheck.cpp - clang-tidy------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #include "UseDefaultMemberInitCheck.h" |
| 11 | #include "clang/AST/ASTContext.h" |
| 12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 13 | #include "clang/Lex/Lexer.h" |
| 14 | |
| 15 | using namespace clang::ast_matchers; |
| 16 | |
| 17 | namespace clang { |
| 18 | namespace tidy { |
| 19 | namespace modernize { |
| 20 | |
| 21 | static StringRef getValueOfValueInit(const QualType InitType) { |
| 22 | switch (InitType->getScalarTypeKind()) { |
| 23 | case Type::STK_CPointer: |
| 24 | case Type::STK_BlockPointer: |
| 25 | case Type::STK_ObjCObjectPointer: |
| 26 | case Type::STK_MemberPointer: |
| 27 | return "nullptr"; |
| 28 | |
| 29 | case Type::STK_Bool: |
| 30 | return "false"; |
| 31 | |
| 32 | case Type::STK_Integral: |
| 33 | switch (InitType->getAs<BuiltinType>()->getKind()) { |
| 34 | case BuiltinType::Char_U: |
| 35 | case BuiltinType::UChar: |
| 36 | case BuiltinType::Char_S: |
| 37 | case BuiltinType::SChar: |
| 38 | return "'\\0'"; |
| 39 | case BuiltinType::WChar_U: |
| 40 | case BuiltinType::WChar_S: |
| 41 | return "L'\\0'"; |
| 42 | case BuiltinType::Char16: |
| 43 | return "u'\\0'"; |
| 44 | case BuiltinType::Char32: |
| 45 | return "U'\\0'"; |
| 46 | default: |
| 47 | return "0"; |
| 48 | } |
| 49 | |
| 50 | case Type::STK_Floating: |
| 51 | switch (InitType->getAs<BuiltinType>()->getKind()) { |
| 52 | case BuiltinType::Half: |
| 53 | case BuiltinType::Float: |
| 54 | return "0.0f"; |
| 55 | default: |
| 56 | return "0.0"; |
| 57 | } |
| 58 | |
| 59 | case Type::STK_FloatingComplex: |
| 60 | case Type::STK_IntegralComplex: |
| 61 | return getValueOfValueInit( |
| 62 | InitType->getAs<ComplexType>()->getElementType()); |
| 63 | } |
| 64 | llvm_unreachable("Invalid scalar type kind"); |
| 65 | } |
| 66 | |
| 67 | static bool isZero(const Expr *E) { |
| 68 | switch (E->getStmtClass()) { |
| 69 | case Stmt::CXXNullPtrLiteralExprClass: |
| 70 | case Stmt::ImplicitValueInitExprClass: |
| 71 | return true; |
| 72 | case Stmt::InitListExprClass: |
| 73 | return cast<InitListExpr>(E)->getNumInits() == 0; |
| 74 | case Stmt::CharacterLiteralClass: |
| 75 | return !cast<CharacterLiteral>(E)->getValue(); |
| 76 | case Stmt::CXXBoolLiteralExprClass: |
| 77 | return !cast<CXXBoolLiteralExpr>(E)->getValue(); |
| 78 | case Stmt::IntegerLiteralClass: |
| 79 | return !cast<IntegerLiteral>(E)->getValue(); |
| 80 | case Stmt::FloatingLiteralClass: { |
| 81 | llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue(); |
| 82 | return Value.isZero() && !Value.isNegative(); |
| 83 | } |
| 84 | default: |
| 85 | return false; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | static const Expr *ignoreUnaryPlus(const Expr *E) { |
| 90 | auto *UnaryOp = dyn_cast<UnaryOperator>(E); |
| 91 | if (UnaryOp && UnaryOp->getOpcode() == UO_Plus) |
| 92 | return UnaryOp->getSubExpr(); |
| 93 | return E; |
| 94 | } |
| 95 | |
| 96 | static const Expr *getInitializer(const Expr *E) { |
| 97 | auto *InitList = dyn_cast<InitListExpr>(E); |
| 98 | if (InitList && InitList->getNumInits() == 1) |
| 99 | return InitList->getInit(0); |
| 100 | return E; |
| 101 | } |
| 102 | |
| 103 | static bool sameValue(const Expr *E1, const Expr *E2) { |
| 104 | E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts())); |
| 105 | E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts())); |
| 106 | |
| 107 | if (isZero(E1) && isZero(E2)) |
| 108 | return true; |
| 109 | |
| 110 | if (E1->getStmtClass() != E2->getStmtClass()) |
| 111 | return false; |
| 112 | |
| 113 | switch (E1->getStmtClass()) { |
| 114 | case Stmt::UnaryOperatorClass: |
| 115 | return sameValue(cast<UnaryOperator>(E1)->getSubExpr(), |
| 116 | cast<UnaryOperator>(E2)->getSubExpr()); |
| 117 | case Stmt::CharacterLiteralClass: |
| 118 | return cast<CharacterLiteral>(E1)->getValue() == |
| 119 | cast<CharacterLiteral>(E2)->getValue(); |
| 120 | case Stmt::CXXBoolLiteralExprClass: |
| 121 | return cast<CXXBoolLiteralExpr>(E1)->getValue() == |
| 122 | cast<CXXBoolLiteralExpr>(E2)->getValue(); |
| 123 | case Stmt::IntegerLiteralClass: |
| 124 | return cast<IntegerLiteral>(E1)->getValue() == |
| 125 | cast<IntegerLiteral>(E2)->getValue(); |
| 126 | case Stmt::FloatingLiteralClass: |
| 127 | return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual( |
| 128 | cast<FloatingLiteral>(E2)->getValue()); |
| 129 | case Stmt::StringLiteralClass: |
| 130 | return cast<StringLiteral>(E1)->getString() == |
| 131 | cast<StringLiteral>(E2)->getString(); |
| 132 | case Stmt::DeclRefExprClass: |
| 133 | return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl(); |
| 134 | default: |
| 135 | return false; |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name, |
| 140 | ClangTidyContext *Context) |
| 141 | : ClangTidyCheck(Name, Context), |
| 142 | UseAssignment(Options.get("UseAssignment", 0) != 0) {} |
| 143 | |
| 144 | void UseDefaultMemberInitCheck::storeOptions( |
| 145 | ClangTidyOptions::OptionMap &Opts) { |
| 146 | Options.store(Opts, "UseAssignment", UseAssignment); |
| 147 | } |
| 148 | |
Malcolm Parsons | d5508b4 | 2016-12-20 21:26:07 +0000 | [diff] [blame] | 149 | void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { |
| 150 | if (!getLangOpts().CPlusPlus11) |
| 151 | return; |
| 152 | |
| 153 | auto Init = |
| 154 | anyOf(stringLiteral(), characterLiteral(), integerLiteral(), |
| 155 | unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), |
| 156 | hasUnaryOperand(integerLiteral())), |
| 157 | floatLiteral(), |
| 158 | unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), |
| 159 | hasUnaryOperand(floatLiteral())), |
| 160 | cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(), |
| 161 | declRefExpr()); |
| 162 | |
| 163 | Finder->addMatcher( |
| 164 | cxxConstructorDecl( |
| 165 | isDefaultConstructor(), unless(isInstantiated()), |
Malcolm Parsons | 0cc3051 | 2016-12-24 14:30:29 +0000 | [diff] [blame^] | 166 | forEachConstructorInitializer( |
| 167 | allOf(forField(unless(anyOf(isBitField(), |
| 168 | hasInClassInitializer(anything())))), |
| 169 | cxxCtorInitializer(isWritten(), |
| 170 | withInitializer(ignoringImplicit(Init))) |
| 171 | .bind("default")))), |
Malcolm Parsons | d5508b4 | 2016-12-20 21:26:07 +0000 | [diff] [blame] | 172 | this); |
| 173 | |
| 174 | Finder->addMatcher( |
| 175 | cxxConstructorDecl( |
| 176 | unless(ast_matchers::isTemplateInstantiation()), |
| 177 | forEachConstructorInitializer( |
Malcolm Parsons | 0cc3051 | 2016-12-24 14:30:29 +0000 | [diff] [blame^] | 178 | allOf(forField(hasInClassInitializer(anything())), |
Malcolm Parsons | d5508b4 | 2016-12-20 21:26:07 +0000 | [diff] [blame] | 179 | cxxCtorInitializer(isWritten(), |
| 180 | withInitializer(ignoringImplicit(Init))) |
| 181 | .bind("existing")))), |
| 182 | this); |
| 183 | } |
| 184 | |
| 185 | void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) { |
| 186 | if (const auto *Default = |
| 187 | Result.Nodes.getNodeAs<CXXCtorInitializer>("default")) |
| 188 | checkDefaultInit(Result, Default); |
| 189 | else if (const auto *Existing = |
| 190 | Result.Nodes.getNodeAs<CXXCtorInitializer>("existing")) |
| 191 | checkExistingInit(Result, Existing); |
| 192 | else |
| 193 | llvm_unreachable("Bad Callback. No node provided."); |
| 194 | } |
| 195 | |
| 196 | void UseDefaultMemberInitCheck::checkDefaultInit( |
| 197 | const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { |
| 198 | const FieldDecl *Field = Init->getMember(); |
| 199 | |
| 200 | SourceLocation FieldEnd = |
| 201 | Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, |
| 202 | *Result.SourceManager, getLangOpts()); |
| 203 | SourceLocation LParenEnd = Lexer::getLocForEndOfToken( |
| 204 | Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts()); |
| 205 | CharSourceRange InitRange = |
| 206 | CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc()); |
| 207 | |
| 208 | auto Diag = |
| 209 | diag(Field->getLocation(), "use default member initializer for %0") |
| 210 | << Field |
| 211 | << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{") |
| 212 | << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange); |
| 213 | |
| 214 | if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit())) |
| 215 | Diag << FixItHint::CreateInsertion( |
| 216 | FieldEnd, getValueOfValueInit(Init->getInit()->getType())); |
| 217 | |
| 218 | if (!UseAssignment) |
| 219 | Diag << FixItHint::CreateInsertion(FieldEnd, "}"); |
| 220 | |
| 221 | Diag << FixItHint::CreateRemoval(Init->getSourceRange()); |
| 222 | } |
| 223 | |
| 224 | void UseDefaultMemberInitCheck::checkExistingInit( |
| 225 | const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { |
| 226 | const FieldDecl *Field = Init->getMember(); |
| 227 | |
| 228 | if (!sameValue(Field->getInClassInitializer(), Init->getInit())) |
| 229 | return; |
| 230 | |
| 231 | diag(Init->getSourceLocation(), "member initializer for %0 is redundant") |
| 232 | << Field |
| 233 | << FixItHint::CreateRemoval(Init->getSourceRange()); |
| 234 | } |
| 235 | |
| 236 | } // namespace modernize |
| 237 | } // namespace tidy |
| 238 | } // namespace clang |