353 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "UseDefaultMemberInitCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Expr.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Lex/Lexer.h"15#include "llvm/ADT/TypeSwitch.h"16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::modernize {20 21static bool isExprAllowedInMemberInit(const Expr *E) {22 if (!E)23 return false;24 return llvm::TypeSwitch<const Expr *, bool>(E)25 .Case<IntegerLiteral, FloatingLiteral, CXXBoolLiteralExpr,26 CXXNullPtrLiteralExpr, CharacterLiteral, StringLiteral>(27 [](const auto *) { return true; })28 .Case<ImplicitValueInitExpr>([](const auto *) { return true; })29 .Case<ParenExpr>([](const ParenExpr *PE) {30 return isExprAllowedInMemberInit(PE->getSubExpr());31 })32 .Case<UnaryOperator>([](const UnaryOperator *UO) {33 return isExprAllowedInMemberInit(UO->getSubExpr());34 })35 .Case<BinaryOperator>([](const BinaryOperator *BO) {36 return isExprAllowedInMemberInit(BO->getLHS()) &&37 isExprAllowedInMemberInit(BO->getRHS());38 })39 .Case<CastExpr>([](const CastExpr *CE) {40 return isExprAllowedInMemberInit(CE->getSubExpr());41 })42 .Case<DeclRefExpr>([](const DeclRefExpr *DRE) {43 if (const ValueDecl *D = DRE->getDecl()) {44 if (isa<EnumConstantDecl>(D))45 return true;46 if (const auto *VD = dyn_cast<VarDecl>(D))47 return VD->isConstexpr() || VD->getStorageClass() == SC_Static;48 }49 return false;50 })51 .Default(false);52}53 54namespace {55 56AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {57 return Node.getNumInits() == N;58}59 60AST_MATCHER(Expr, allowedInitExpr) { return isExprAllowedInMemberInit(&Node); }61 62} // namespace63 64static StringRef getValueOfValueInit(const QualType InitType) {65 switch (InitType->getScalarTypeKind()) {66 case Type::STK_CPointer:67 case Type::STK_BlockPointer:68 case Type::STK_ObjCObjectPointer:69 case Type::STK_MemberPointer:70 return "nullptr";71 72 case Type::STK_Bool:73 return "false";74 75 case Type::STK_Integral:76 switch (InitType->castAs<BuiltinType>()->getKind()) {77 case BuiltinType::Char_U:78 case BuiltinType::UChar:79 case BuiltinType::Char_S:80 case BuiltinType::SChar:81 return "'\\0'";82 case BuiltinType::WChar_U:83 case BuiltinType::WChar_S:84 return "L'\\0'";85 case BuiltinType::Char16:86 return "u'\\0'";87 case BuiltinType::Char32:88 return "U'\\0'";89 default:90 return "0";91 }92 93 case Type::STK_Floating:94 switch (InitType->castAs<BuiltinType>()->getKind()) {95 case BuiltinType::Half:96 case BuiltinType::Float:97 return "0.0f";98 default:99 return "0.0";100 }101 102 case Type::STK_FloatingComplex:103 case Type::STK_IntegralComplex:104 return getValueOfValueInit(105 InitType->castAs<ComplexType>()->getElementType());106 107 case Type::STK_FixedPoint:108 switch (InitType->castAs<BuiltinType>()->getKind()) {109 case BuiltinType::ShortAccum:110 case BuiltinType::SatShortAccum:111 return "0.0hk";112 case BuiltinType::Accum:113 case BuiltinType::SatAccum:114 return "0.0k";115 case BuiltinType::LongAccum:116 case BuiltinType::SatLongAccum:117 return "0.0lk";118 case BuiltinType::UShortAccum:119 case BuiltinType::SatUShortAccum:120 return "0.0uhk";121 case BuiltinType::UAccum:122 case BuiltinType::SatUAccum:123 return "0.0uk";124 case BuiltinType::ULongAccum:125 case BuiltinType::SatULongAccum:126 return "0.0ulk";127 case BuiltinType::ShortFract:128 case BuiltinType::SatShortFract:129 return "0.0hr";130 case BuiltinType::Fract:131 case BuiltinType::SatFract:132 return "0.0r";133 case BuiltinType::LongFract:134 case BuiltinType::SatLongFract:135 return "0.0lr";136 case BuiltinType::UShortFract:137 case BuiltinType::SatUShortFract:138 return "0.0uhr";139 case BuiltinType::UFract:140 case BuiltinType::SatUFract:141 return "0.0ur";142 case BuiltinType::ULongFract:143 case BuiltinType::SatULongFract:144 return "0.0ulr";145 default:146 llvm_unreachable("Unhandled fixed point BuiltinType");147 }148 }149 llvm_unreachable("Invalid scalar type kind");150}151 152static bool isZero(const Expr *E) {153 switch (E->getStmtClass()) {154 case Stmt::CXXNullPtrLiteralExprClass:155 case Stmt::ImplicitValueInitExprClass:156 return true;157 case Stmt::InitListExprClass:158 return cast<InitListExpr>(E)->getNumInits() == 0;159 case Stmt::CharacterLiteralClass:160 return !cast<CharacterLiteral>(E)->getValue();161 case Stmt::CXXBoolLiteralExprClass:162 return !cast<CXXBoolLiteralExpr>(E)->getValue();163 case Stmt::IntegerLiteralClass:164 return !cast<IntegerLiteral>(E)->getValue();165 case Stmt::FloatingLiteralClass: {166 const llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();167 return Value.isZero() && !Value.isNegative();168 }169 default:170 return false;171 }172}173 174static const Expr *ignoreUnaryPlus(const Expr *E) {175 auto *UnaryOp = dyn_cast<UnaryOperator>(E);176 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)177 return UnaryOp->getSubExpr();178 return E;179}180 181static const Expr *getInitializer(const Expr *E) {182 auto *InitList = dyn_cast<InitListExpr>(E);183 if (InitList && InitList->getNumInits() == 1)184 return InitList->getInit(0)->IgnoreParenImpCasts();185 return E;186}187 188static bool sameValue(const Expr *E1, const Expr *E2) {189 E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));190 E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));191 192 if (isZero(E1) && isZero(E2))193 return true;194 195 if (E1->getStmtClass() != E2->getStmtClass())196 return false;197 198 switch (E1->getStmtClass()) {199 case Stmt::UnaryOperatorClass:200 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),201 cast<UnaryOperator>(E2)->getSubExpr());202 case Stmt::BinaryOperatorClass: {203 const auto *BinOp1 = cast<BinaryOperator>(E1);204 const auto *BinOp2 = cast<BinaryOperator>(E2);205 return BinOp1->getOpcode() == BinOp2->getOpcode() &&206 sameValue(BinOp1->getLHS(), BinOp2->getLHS()) &&207 sameValue(BinOp1->getRHS(), BinOp2->getRHS());208 }209 case Stmt::CharacterLiteralClass:210 return cast<CharacterLiteral>(E1)->getValue() ==211 cast<CharacterLiteral>(E2)->getValue();212 case Stmt::CXXBoolLiteralExprClass:213 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==214 cast<CXXBoolLiteralExpr>(E2)->getValue();215 case Stmt::IntegerLiteralClass:216 return cast<IntegerLiteral>(E1)->getValue() ==217 cast<IntegerLiteral>(E2)->getValue();218 case Stmt::FloatingLiteralClass:219 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(220 cast<FloatingLiteral>(E2)->getValue());221 case Stmt::StringLiteralClass:222 return cast<StringLiteral>(E1)->getString() ==223 cast<StringLiteral>(E2)->getString();224 case Stmt::DeclRefExprClass:225 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();226 case Stmt::CStyleCastExprClass:227 case Stmt::CXXStaticCastExprClass:228 case Stmt::CXXFunctionalCastExprClass:229 return sameValue(cast<ExplicitCastExpr>(E1)->getSubExpr(),230 cast<ExplicitCastExpr>(E2)->getSubExpr());231 default:232 return false;233 }234}235 236UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,237 ClangTidyContext *Context)238 : ClangTidyCheck(Name, Context),239 UseAssignment(Options.get("UseAssignment", false)),240 IgnoreMacros(Options.get("IgnoreMacros", true)) {}241 242void UseDefaultMemberInitCheck::storeOptions(243 ClangTidyOptions::OptionMap &Opts) {244 Options.store(Opts, "UseAssignment", UseAssignment);245 Options.store(Opts, "IgnoreMacros", IgnoreMacros);246}247 248void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {249 auto Init = anyOf(250 initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, allowedInitExpr())),251 initCountIs(0), hasType(arrayType()))),252 allowedInitExpr());253 254 Finder->addMatcher(255 cxxConstructorDecl(forEachConstructorInitializer(256 cxxCtorInitializer(257 forField(unless(anyOf(258 getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),259 hasInClassInitializer(anything()),260 hasParent(recordDecl(isUnion()))))),261 withInitializer(Init))262 .bind("default"))),263 this);264 265 Finder->addMatcher(266 cxxConstructorDecl(forEachConstructorInitializer(267 cxxCtorInitializer(forField(hasInClassInitializer(anything())),268 withInitializer(Init))269 .bind("existing"))),270 this);271}272 273void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {274 if (const auto *Default =275 Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))276 checkDefaultInit(Result, Default);277 else if (const auto *Existing =278 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))279 checkExistingInit(Result, Existing);280 else281 llvm_unreachable("Bad Callback. No node provided.");282}283 284void UseDefaultMemberInitCheck::checkDefaultInit(285 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {286 const FieldDecl *Field = Init->getAnyMember();287 288 // Check whether we have multiple hand-written constructors and bomb out, as289 // it is hard to reconcile their sets of member initializers.290 const auto *ClassDecl = cast<CXXRecordDecl>(Field->getParent());291 if (llvm::count_if(ClassDecl->decls(), [](const Decl *D) {292 if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))293 D = FTD->getTemplatedDecl();294 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))295 return !Ctor->isCopyOrMoveConstructor();296 return false;297 }) > 1)298 return;299 300 const SourceLocation StartLoc = Field->getBeginLoc();301 if (StartLoc.isMacroID() && IgnoreMacros)302 return;303 304 const SourceLocation FieldEnd =305 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,306 *Result.SourceManager, getLangOpts());307 const SourceLocation LParenEnd = Lexer::getLocForEndOfToken(308 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());309 const CharSourceRange InitRange =310 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());311 312 const Expr *InitExpression = Init->getInit();313 const QualType InitType = InitExpression->getType();314 315 const bool ValueInit =316 isa<ImplicitValueInitExpr>(InitExpression) && !isa<ArrayType>(InitType);317 const bool CanAssign =318 UseAssignment && (!ValueInit || !InitType->isEnumeralType());319 const bool NeedsBraces = !CanAssign || isa<ArrayType>(InitType);320 321 auto Diag =322 diag(Field->getLocation(), "use default member initializer for %0")323 << Field;324 325 if (CanAssign)326 Diag << FixItHint::CreateInsertion(FieldEnd, " = ");327 if (NeedsBraces)328 Diag << FixItHint::CreateInsertion(FieldEnd, "{");329 330 if (CanAssign && ValueInit)331 Diag << FixItHint::CreateInsertion(FieldEnd, getValueOfValueInit(InitType));332 else333 Diag << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);334 335 if (NeedsBraces)336 Diag << FixItHint::CreateInsertion(FieldEnd, "}");337 338 Diag << FixItHint::CreateRemoval(Init->getSourceRange());339}340 341void UseDefaultMemberInitCheck::checkExistingInit(342 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {343 const FieldDecl *Field = Init->getAnyMember();344 345 if (!sameValue(Field->getInClassInitializer(), Init->getInit()))346 return;347 348 diag(Init->getSourceLocation(), "member initializer for %0 is redundant")349 << Field << FixItHint::CreateRemoval(Init->getSourceRange());350}351 352} // namespace clang::tidy::modernize353