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 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(to(enumConstantDecl()))); 162 163 Finder->addMatcher( 164 cxxConstructorDecl( 165 isDefaultConstructor(), unless(isInstantiated()), 166 forEachConstructorInitializer( 167 allOf(forField(unless(anyOf(isBitField(), 168 hasInClassInitializer(anything())))), 169 cxxCtorInitializer(isWritten(), 170 withInitializer(ignoringImplicit(Init))) 171 .bind("default")))), 172 this); 173 174 Finder->addMatcher( 175 cxxConstructorDecl( 176 unless(ast_matchers::isTemplateInstantiation()), 177 forEachConstructorInitializer( 178 allOf(forField(hasInClassInitializer(anything())), 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 239