MoveConstructorInitCheck.cpp 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. //===--- MoveConstructorInitCheck.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 "MoveConstructorInitCheck.h"
  9. #include "../utils/Matchers.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::performance {
  14. MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
  15. ClangTidyContext *Context)
  16. : ClangTidyCheck(Name, Context) {}
  17. void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
  18. Finder->addMatcher(
  19. traverse(TK_AsIs,
  20. cxxConstructorDecl(
  21. unless(isImplicit()), isMoveConstructor(),
  22. hasAnyConstructorInitializer(
  23. cxxCtorInitializer(
  24. withInitializer(cxxConstructExpr(hasDeclaration(
  25. cxxConstructorDecl(isCopyConstructor())
  26. .bind("ctor")))))
  27. .bind("move-init")))),
  28. this);
  29. }
  30. void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
  31. const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
  32. const auto *Initializer =
  33. Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
  34. // Do not diagnose if the expression used to perform the initialization is a
  35. // trivially-copyable type.
  36. QualType QT = Initializer->getInit()->getType();
  37. if (QT.isTriviallyCopyableType(*Result.Context))
  38. return;
  39. if (QT.isConstQualified())
  40. return;
  41. const auto *RD = QT->getAsCXXRecordDecl();
  42. if (RD && RD->isTriviallyCopyable())
  43. return;
  44. // Diagnose when the class type has a move constructor available, but the
  45. // ctor-initializer uses the copy constructor instead.
  46. const CXXConstructorDecl *Candidate = nullptr;
  47. for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
  48. if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
  49. !Ctor->isDeleted()) {
  50. // The type has a move constructor that is at least accessible to the
  51. // initializer.
  52. //
  53. // FIXME: Determine whether the move constructor is a viable candidate
  54. // for the ctor-initializer, perhaps provide a fix-it that suggests
  55. // using std::move().
  56. Candidate = Ctor;
  57. break;
  58. }
  59. }
  60. if (Candidate) {
  61. // There's a move constructor candidate that the caller probably intended
  62. // to call instead.
  63. diag(Initializer->getSourceLocation(),
  64. "move constructor initializes %select{class member|base class}0 by "
  65. "calling a copy constructor")
  66. << Initializer->isBaseInitializer();
  67. diag(CopyCtor->getLocation(), "copy constructor being called",
  68. DiagnosticIDs::Note);
  69. diag(Candidate->getLocation(), "candidate move constructor here",
  70. DiagnosticIDs::Note);
  71. }
  72. }
  73. } // namespace clang::tidy::performance