CopyConstructorInitCheck.cpp 4.2 KB

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