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 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", 1) != 0) {} 144 145 void UseDefaultMemberInitCheck::storeOptions( 146 ClangTidyOptions::OptionMap &Opts) { 147 Options.store(Opts, "UseAssignment", UseAssignment); 148 Options.store(Opts, "IgnoreMacros", IgnoreMacros); 149 } 150 151 void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { 152 if (!getLangOpts().CPlusPlus11) 153 return; 154 155 auto Init = 156 anyOf(stringLiteral(), characterLiteral(), integerLiteral(), 157 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), 158 hasUnaryOperand(integerLiteral())), 159 floatLiteral(), 160 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")), 161 hasUnaryOperand(floatLiteral())), 162 cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(), 163 declRefExpr(to(enumConstantDecl()))); 164 165 Finder->addMatcher( 166 cxxConstructorDecl( 167 isDefaultConstructor(), unless(isInstantiated()), 168 forEachConstructorInitializer( 169 allOf(forField(unless(anyOf(isBitField(), 170 hasInClassInitializer(anything())))), 171 cxxCtorInitializer(isWritten(), 172 withInitializer(ignoringImplicit(Init))) 173 .bind("default")))), 174 this); 175 176 Finder->addMatcher( 177 cxxConstructorDecl( 178 unless(ast_matchers::isTemplateInstantiation()), 179 forEachConstructorInitializer( 180 allOf(forField(hasInClassInitializer(anything())), 181 cxxCtorInitializer(isWritten(), 182 withInitializer(ignoringImplicit(Init))) 183 .bind("existing")))), 184 this); 185 } 186 187 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) { 188 if (const auto *Default = 189 Result.Nodes.getNodeAs<CXXCtorInitializer>("default")) 190 checkDefaultInit(Result, Default); 191 else if (const auto *Existing = 192 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing")) 193 checkExistingInit(Result, Existing); 194 else 195 llvm_unreachable("Bad Callback. No node provided."); 196 } 197 198 void UseDefaultMemberInitCheck::checkDefaultInit( 199 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 200 const FieldDecl *Field = Init->getMember(); 201 202 SourceLocation StartLoc = Field->getLocStart(); 203 if (StartLoc.isMacroID() && IgnoreMacros) 204 return; 205 206 SourceLocation FieldEnd = 207 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, 208 *Result.SourceManager, getLangOpts()); 209 SourceLocation LParenEnd = Lexer::getLocForEndOfToken( 210 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts()); 211 CharSourceRange InitRange = 212 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc()); 213 214 auto Diag = 215 diag(Field->getLocation(), "use default member initializer for %0") 216 << Field 217 << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{") 218 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange); 219 220 if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit())) 221 Diag << FixItHint::CreateInsertion( 222 FieldEnd, getValueOfValueInit(Init->getInit()->getType())); 223 224 if (!UseAssignment) 225 Diag << FixItHint::CreateInsertion(FieldEnd, "}"); 226 227 Diag << FixItHint::CreateRemoval(Init->getSourceRange()); 228 } 229 230 void UseDefaultMemberInitCheck::checkExistingInit( 231 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 232 const FieldDecl *Field = Init->getMember(); 233 234 if (!sameValue(Field->getInClassInitializer(), Init->getInit())) 235 return; 236 237 diag(Init->getSourceLocation(), "member initializer for %0 is redundant") 238 << Field 239 << FixItHint::CreateRemoval(Init->getSourceRange()); 240 } 241 242 } // namespace modernize 243 } // namespace tidy 244 } // namespace clang 245