brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.9 KiB · cc6b7bf Raw
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