brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.2 KiB · ccbc86a Raw
114 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 "CopyConstructorInitCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Lex/Lexer.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::bugprone {17 18void CopyConstructorInitCheck::registerMatchers(MatchFinder *Finder) {19  // In the future this might be extended to move constructors?20  Finder->addMatcher(21      cxxConstructorDecl(22          isCopyConstructor(),23          hasAnyConstructorInitializer(cxxCtorInitializer(24              isBaseInitializer(),25              withInitializer(cxxConstructExpr(hasDeclaration(26                  cxxConstructorDecl(isDefaultConstructor())))))),27          unless(isInstantiated()))28          .bind("ctor"),29      this);30}31 32void CopyConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {33  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");34  const std::string ParamName = Ctor->getParamDecl(0)->getNameAsString();35 36  // We want only one warning (and FixIt) for each ctor.37  std::string FixItInitList;38  bool HasRelevantBaseInit = false;39  bool ShouldNotDoFixit = false;40  bool HasWrittenInitializer = false;41  SmallVector<FixItHint, 2> SafeFixIts;42  for (const auto *Init : Ctor->inits()) {43    const bool CtorInitIsWritten = Init->isWritten();44    HasWrittenInitializer = HasWrittenInitializer || CtorInitIsWritten;45    if (!Init->isBaseInitializer())46      continue;47    const Type *BaseType = Init->getBaseClass();48    // Do not do fixits if there is a type alias involved or one of the bases49    // are explicitly initialized. In the latter case we not do fixits to avoid50    // -Wreorder warnings.51    if (const auto *TempSpecTy = dyn_cast<TemplateSpecializationType>(BaseType))52      ShouldNotDoFixit = ShouldNotDoFixit || TempSpecTy->isTypeAlias();53    ShouldNotDoFixit = ShouldNotDoFixit || isa<TypedefType>(BaseType);54    ShouldNotDoFixit = ShouldNotDoFixit || CtorInitIsWritten;55    const CXXRecordDecl *BaseClass =56        BaseType->getAsCXXRecordDecl()->getDefinition();57    if (BaseClass->field_empty() &&58        BaseClass->forallBases(59            [](const CXXRecordDecl *Class) { return Class->field_empty(); }))60      continue;61    bool NonCopyableBase = false;62    for (const auto *Ctor : BaseClass->ctors()) {63      if (Ctor->isCopyConstructor() &&64          (Ctor->getAccess() == AS_private || Ctor->isDeleted())) {65        NonCopyableBase = true;66        break;67      }68    }69    if (NonCopyableBase)70      continue;71    const auto *CExpr = dyn_cast<CXXConstructExpr>(Init->getInit());72    if (!CExpr || !CExpr->getConstructor()->isDefaultConstructor())73      continue;74    HasRelevantBaseInit = true;75    if (CtorInitIsWritten) {76      if (!ParamName.empty())77        SafeFixIts.push_back(78            FixItHint::CreateInsertion(CExpr->getEndLoc(), ParamName));79    } else {80      if (Init->getSourceLocation().isMacroID() ||81          Ctor->getLocation().isMacroID() || ShouldNotDoFixit)82        break;83      FixItInitList += BaseClass->getNameAsString();84      FixItInitList += "(" + ParamName + "), ";85    }86  }87  if (!HasRelevantBaseInit)88    return;89 90  auto Diag = diag(Ctor->getLocation(),91                   "calling a base constructor other than the copy constructor")92              << SafeFixIts;93 94  if (FixItInitList.empty() || ParamName.empty() || ShouldNotDoFixit)95    return;96 97  std::string FixItMsg{FixItInitList.substr(0, FixItInitList.size() - 2)};98  SourceLocation FixItLoc;99  // There is no initialization list in this constructor.100  if (!HasWrittenInitializer) {101    FixItLoc = Ctor->getBody()->getBeginLoc();102    FixItMsg = " : " + FixItMsg;103  } else {104    // We apply the missing ctors at the beginning of the initialization list.105    FixItLoc = (*Ctor->init_begin())->getSourceLocation();106    FixItMsg += ',';107  }108  FixItMsg += ' ';109 110  Diag << FixItHint::CreateInsertion(FixItLoc, FixItMsg);111}112 113} // namespace clang::tidy::bugprone114