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( 211 allOf(initCountIs(1), hasInit(0, ignoringImplicit(InitBase))), 212 initCountIs(0))), 213 InitBase); 214 215 Finder->addMatcher( 216 cxxConstructorDecl( 217 isDefaultConstructor(), unless(isInstantiated()), 218 forEachConstructorInitializer( 219 cxxCtorInitializer( 220 forField(unless(anyOf(getLangOpts().CPlusPlus20 221 ? unless(anything()) 222 : isBitField(), 223 hasInClassInitializer(anything()), 224 hasParent(recordDecl(isUnion()))))), 225 isWritten(), withInitializer(ignoringImplicit(Init))) 226 .bind("default"))), 227 this); 228 229 Finder->addMatcher( 230 cxxConstructorDecl( 231 unless(ast_matchers::isTemplateInstantiation()), 232 forEachConstructorInitializer( 233 cxxCtorInitializer(forField(hasInClassInitializer(anything())), 234 isWritten(), 235 withInitializer(ignoringImplicit(Init))) 236 .bind("existing"))), 237 this); 238 } 239 240 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) { 241 if (const auto *Default = 242 Result.Nodes.getNodeAs<CXXCtorInitializer>("default")) 243 checkDefaultInit(Result, Default); 244 else if (const auto *Existing = 245 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing")) 246 checkExistingInit(Result, Existing); 247 else 248 llvm_unreachable("Bad Callback. No node provided."); 249 } 250 251 void UseDefaultMemberInitCheck::checkDefaultInit( 252 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 253 const FieldDecl *Field = Init->getAnyMember(); 254 255 SourceLocation StartLoc = Field->getBeginLoc(); 256 if (StartLoc.isMacroID() && IgnoreMacros) 257 return; 258 259 SourceLocation FieldEnd = 260 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0, 261 *Result.SourceManager, getLangOpts()); 262 SourceLocation LParenEnd = Lexer::getLocForEndOfToken( 263 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts()); 264 CharSourceRange InitRange = 265 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc()); 266 267 bool ValueInit = isa<ImplicitValueInitExpr>(Init->getInit()); 268 bool CanAssign = UseAssignment && (!ValueInit || !Init->getInit()->getType()->isEnumeralType()); 269 270 auto Diag = 271 diag(Field->getLocation(), "use default member initializer for %0") 272 << Field 273 << FixItHint::CreateInsertion(FieldEnd, CanAssign ? " = " : "{") 274 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange); 275 276 if (CanAssign && ValueInit) 277 Diag << FixItHint::CreateInsertion( 278 FieldEnd, getValueOfValueInit(Init->getInit()->getType())); 279 280 if (!CanAssign) 281 Diag << FixItHint::CreateInsertion(FieldEnd, "}"); 282 283 Diag << FixItHint::CreateRemoval(Init->getSourceRange()); 284 } 285 286 void UseDefaultMemberInitCheck::checkExistingInit( 287 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) { 288 const FieldDecl *Field = Init->getAnyMember(); 289 290 if (!sameValue(Field->getInClassInitializer(), Init->getInit())) 291 return; 292 293 diag(Init->getSourceLocation(), "member initializer for %0 is redundant") 294 << Field 295 << FixItHint::CreateRemoval(Init->getSourceRange()); 296 } 297 298 } // namespace modernize 299 } // namespace tidy 300 } // namespace clang 301