1 //===--- UseDefaultMemberInitCheck.cpp - clang-tidy------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "UseDefaultMemberInitCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Lex/Lexer.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace modernize { 19 20 namespace { 21 AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) { 22 return Node.getNumInits() == N; 23 } 24 } // namespace 25 26 static StringRef getValueOfValueInit(const QualType InitType) { 27 switch (InitType->getScalarTypeKind()) { 28 case Type::STK_CPointer: 29 case Type::STK_BlockPointer: 30 case Type::STK_ObjCObjectPointer: 31 case Type::STK_MemberPointer: 32 return "nullptr"; 33 34 case Type::STK_Bool: 35 return "false"; 36 37 case Type::STK_Integral: 38 switch (InitType->castAs<BuiltinType>()->getKind()) { 39 case BuiltinType::Char_U: 40 case BuiltinType::UChar: 41 case BuiltinType::Char_S: 42 case BuiltinType::SChar: 43 return "'\\0'"; 44 case BuiltinType::WChar_U: 45 case BuiltinType::WChar_S: 46 return "L'\\0'"; 47 case BuiltinType::Char16: 48 return "u'\\0'"; 49 case BuiltinType::Char32: 50 return "U'\\0'"; 51 default: 52 return "0"; 53 } 54 55 case Type::STK_Floating: 56 switch (InitType->castAs<BuiltinType>()->getKind()) { 57 case BuiltinType::Half: 58 case BuiltinType::Float: 59 return "0.0f"; 60 default: 61 return "0.0"; 62 } 63 64 case Type::STK_FloatingComplex: 65 case Type::STK_IntegralComplex: 66 return getValueOfValueInit( 67 InitType->castAs<ComplexType>()->getElementType()); 68 69 case Type::STK_FixedPoint: 70 switch (InitType->castAs<BuiltinType>()->getKind()) { 71 case BuiltinType::ShortAccum: 72 case BuiltinType::SatShortAccum: 73 return "0.0hk"; 74 case BuiltinType::Accum: 75 case BuiltinType::SatAccum: 76 return "0.0k"; 77 case BuiltinType::LongAccum: 78 case BuiltinType::SatLongAccum: 79 return "0.0lk"; 80 case BuiltinType::UShortAccum: 81 case BuiltinType::SatUShortAccum: 82 return "0.0uhk"; 83 case BuiltinType::UAccum: 84 case BuiltinType::SatUAccum: 85 return "0.0uk"; 86 case BuiltinType::ULongAccum: 87 case BuiltinType::SatULongAccum: 88 return "0.0ulk"; 89 case BuiltinType::ShortFract: 90 case BuiltinType::SatShortFract: 91 return "0.0hr"; 92 case BuiltinType::Fract: 93 case BuiltinType::SatFract: 94 return "0.0r"; 95 case BuiltinType::LongFract: 96 case BuiltinType::SatLongFract: 97 return "0.0lr"; 98 case BuiltinType::UShortFract: 99 case BuiltinType::SatUShortFract: 100 return "0.0uhr"; 101 case BuiltinType::UFract: 102 case BuiltinType::SatUFract: 103 return "0.0ur"; 104 case BuiltinType::ULongFract: 105 case BuiltinType::SatULongFract: 106 return "0.0ulr"; 107 default: 108 llvm_unreachable("Unhandled fixed point BuiltinType"); 109 } 110 } 111 llvm_unreachable("Invalid scalar type kind"); 112 } 113 114 static bool isZero(const Expr *E) { 115 switch (E->getStmtClass()) { 116 case Stmt::CXXNullPtrLiteralExprClass: 117 case Stmt::ImplicitValueInitExprClass: 118 return true; 119 case Stmt::InitListExprClass: 120 return cast<InitListExpr>(E)->getNumInits() == 0; 121 case Stmt::CharacterLiteralClass: 122 return !cast<CharacterLiteral>(E)->getValue(); 123 case Stmt::CXXBoolLiteralExprClass: 124 return !cast<CXXBoolLiteralExpr>(E)->getValue(); 125 case Stmt::IntegerLiteralClass: 126 return !cast<IntegerLiteral>(E)->getValue(); 127 case Stmt::FloatingLiteralClass: { 128 llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue(); 129 return Value.isZero() && !Value.isNegative(); 130 } 131 default: 132 return false; 133 } 134 } 135 136 static const Expr *ignoreUnaryPlus(const Expr *E) { 137 auto *UnaryOp = dyn_cast<UnaryOperator>(E); 138 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus) 139 return UnaryOp->getSubExpr(); 140 return E; 141 } 142 143 static const Expr *getInitializer(const Expr *E) { 144 auto *InitList = dyn_cast<InitListExpr>(E); 145 if (InitList && InitList->getNumInits() == 1) 146 return InitList->getInit(0)->IgnoreParenImpCasts(); 147 return E; 148 } 149 150 static bool sameValue(const Expr *E1, const Expr *E2) { 151 E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts())); 152 E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts())); 153 154 if (isZero(E1) && isZero(E2)) 155 return true; 156 157 if (E1->getStmtClass() != E2->getStmtClass()) 158 return false; 159 160 switch (E1->getStmtClass()) { 161 case Stmt::UnaryOperatorClass: 162 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(), 163 cast<UnaryOperator>(E2)->getSubExpr()); 164 case Stmt::CharacterLiteralClass: 165 return cast<CharacterLiteral>(E1)->getValue() == 166 cast<CharacterLiteral>(E2)->getValue(); 167 case Stmt::CXXBoolLiteralExprClass: 168 return cast<CXXBoolLiteralExpr>(E1)->getValue() == 169 cast<CXXBoolLiteralExpr>(E2)->getValue(); 170 case Stmt::IntegerLiteralClass: 171 return cast<IntegerLiteral>(E1)->getValue() == 172 cast<IntegerLiteral>(E2)->getValue(); 173 case Stmt::FloatingLiteralClass: 174 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual( 175 cast<FloatingLiteral>(E2)->getValue()); 176 case Stmt::StringLiteralClass: 177 return cast<StringLiteral>(E1)->getString() == 178 cast<StringLiteral>(E2)->getString(); 179 case Stmt::DeclRefExprClass: 180 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl(); 181 default: 182 return false; 183 } 184 } 185 186 UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name, 187 ClangTidyContext *Context) 188 : ClangTidyCheck(Name, Context), 189 UseAssignment(Options.get("UseAssignment", false)), 190 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {} 191 192 void UseDefaultMemberInitCheck::storeOptions( 193 ClangTidyOptions::OptionMap &Opts) { 194 Options.store(Opts, "UseAssignment", UseAssignment); 195 Options.store(Opts, "IgnoreMacros", IgnoreMacros); 196 } 197 198 void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) { 199 auto InitBase = 200 anyOf(stringLiteral(), characterLiteral(), integerLiteral(), 201 unaryOperator(hasAnyOperatorName("+", "-"), 202 hasUnaryOperand(integerLiteral())), 203 floatLiteral(), 204 unaryOperator(hasAnyOperatorName("+", "-"), 205 hasUnaryOperand(floatLiteral())), 206 cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(), 207 declRefExpr(to(enumConstantDecl()))); 208 209 auto Init = 210 anyOf(initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, InitBase)), 211 initCountIs(0))), 212 InitBase); 213 214 Finder->addMatcher( 215 cxxConstructorDecl( 216 isDefaultConstructor(), 217 forEachConstructorInitializer( 218 cxxCtorInitializer( 219 forField(unless(anyOf(getLangOpts().CPlusPlus20 220 ? unless(anything()) 221 : isBitField(), 222 hasInClassInitializer(anything()), 223 hasParent(recordDecl(isUnion()))))), 224 withInitializer(Init)) 225 .bind("default"))), 226 this); 227 228 Finder->addMatcher( 229 cxxConstructorDecl(forEachConstructorInitializer( 230 cxxCtorInitializer(forField(hasInClassInitializer(anything())), 231 withInitializer(Init)) 232 .bind("existing"))), 233 this); 234 } 235 236 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) { 237 if (const auto *Default = 238 Result.Nodes.getNodeAs<CXXCtorInitializer>("default")) 239 checkDefaultInit(Result, Default); 240 else if (const auto *Existing = 241 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing")) 242 checkExistingInit(Result, Existing); 243 else 244 llvm_unreachable("Bad Callback. No node provided."); 245 } 246 247 void UseDefaultMemberInitCheck::checkDefaultInit( 248 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 249 const FieldDecl *Field = Init->getAnyMember(); 250 251 SourceLocation StartLoc = Field->getBeginLoc(); 252 if (StartLoc.isMacroID() && IgnoreMacros) 253 return; 254 255 SourceLocation FieldEnd = 256 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, 257 *Result.SourceManager, getLangOpts()); 258 SourceLocation LParenEnd = Lexer::getLocForEndOfToken( 259 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts()); 260 CharSourceRange InitRange = 261 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc()); 262 263 bool ValueInit = isa<ImplicitValueInitExpr>(Init->getInit()); 264 bool CanAssign = UseAssignment && (!ValueInit || !Init->getInit()->getType()->isEnumeralType()); 265 266 auto Diag = 267 diag(Field->getLocation(), "use default member initializer for %0") 268 << Field 269 << FixItHint::CreateInsertion(FieldEnd, CanAssign ? " = " : "{") 270 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange); 271 272 if (CanAssign && ValueInit) 273 Diag << FixItHint::CreateInsertion( 274 FieldEnd, getValueOfValueInit(Init->getInit()->getType())); 275 276 if (!CanAssign) 277 Diag << FixItHint::CreateInsertion(FieldEnd, "}"); 278 279 Diag << FixItHint::CreateRemoval(Init->getSourceRange()); 280 } 281 282 void UseDefaultMemberInitCheck::checkExistingInit( 283 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 284 const FieldDecl *Field = Init->getAnyMember(); 285 286 if (!sameValue(Field->getInClassInitializer(), Init->getInit())) 287 return; 288 289 diag(Init->getSourceLocation(), "member initializer for %0 is redundant") 290 << Field 291 << FixItHint::CreateRemoval(Init->getSourceRange()); 292 } 293 294 } // namespace modernize 295 } // namespace tidy 296 } // namespace clang 297