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