1 //===--- AvoidConstOrRefDataMembersCheck.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 9 #include "AvoidConstOrRefDataMembersCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang::tidy::cppcoreguidelines { 16 17 static bool isCopyConstructible(CXXRecordDecl const &Node) { 18 if (Node.needsOverloadResolutionForCopyConstructor() && 19 Node.needsImplicitCopyConstructor()) { 20 // unresolved 21 for (CXXBaseSpecifier const &BS : Node.bases()) { 22 CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 23 if (BRD != nullptr && !isCopyConstructible(*BRD)) 24 return false; 25 } 26 } 27 if (Node.hasSimpleCopyConstructor()) 28 return true; 29 for (CXXConstructorDecl const *Ctor : Node.ctors()) 30 if (Ctor->isCopyConstructor()) 31 return !Ctor->isDeleted(); 32 return false; 33 } 34 35 static bool isMoveConstructible(CXXRecordDecl const &Node) { 36 if (Node.needsOverloadResolutionForMoveConstructor() && 37 Node.needsImplicitMoveConstructor()) { 38 // unresolved 39 for (CXXBaseSpecifier const &BS : Node.bases()) { 40 CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 41 if (BRD != nullptr && !isMoveConstructible(*BRD)) 42 return false; 43 } 44 } 45 if (Node.hasSimpleMoveConstructor()) 46 return true; 47 for (CXXConstructorDecl const *Ctor : Node.ctors()) 48 if (Ctor->isMoveConstructor()) 49 return !Ctor->isDeleted(); 50 return false; 51 } 52 53 static bool isCopyAssignable(CXXRecordDecl const &Node) { 54 if (Node.needsOverloadResolutionForCopyAssignment() && 55 Node.needsImplicitCopyAssignment()) { 56 // unresolved 57 for (CXXBaseSpecifier const &BS : Node.bases()) { 58 CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 59 if (BRD != nullptr && !isCopyAssignable(*BRD)) 60 return false; 61 } 62 } 63 if (Node.hasSimpleCopyAssignment()) 64 return true; 65 for (CXXMethodDecl const *Method : Node.methods()) 66 if (Method->isCopyAssignmentOperator()) 67 return !Method->isDeleted(); 68 return false; 69 } 70 71 static bool isMoveAssignable(CXXRecordDecl const &Node) { 72 if (Node.needsOverloadResolutionForMoveAssignment() && 73 Node.needsImplicitMoveAssignment()) { 74 // unresolved 75 for (CXXBaseSpecifier const &BS : Node.bases()) { 76 CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 77 if (BRD != nullptr && !isMoveAssignable(*BRD)) 78 return false; 79 } 80 } 81 if (Node.hasSimpleMoveAssignment()) 82 return true; 83 for (CXXMethodDecl const *Method : Node.methods()) 84 if (Method->isMoveAssignmentOperator()) 85 return !Method->isDeleted(); 86 return false; 87 } 88 89 namespace { 90 91 AST_MATCHER(FieldDecl, isMemberOfLambda) { 92 return Node.getParent()->isLambda(); 93 } 94 95 AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) { 96 return isCopyConstructible(Node) || isMoveConstructible(Node) || 97 isCopyAssignable(Node) || isMoveAssignable(Node); 98 } 99 100 } // namespace 101 102 void AvoidConstOrRefDataMembersCheck::registerMatchers(MatchFinder *Finder) { 103 Finder->addMatcher( 104 fieldDecl( 105 unless(isMemberOfLambda()), 106 anyOf( 107 fieldDecl(hasType(hasCanonicalType(referenceType()))).bind("ref"), 108 fieldDecl(hasType(qualType(isConstQualified()))).bind("const")), 109 hasDeclContext(cxxRecordDecl(isCopyableOrMovable()))), 110 this); 111 } 112 113 void AvoidConstOrRefDataMembersCheck::check( 114 const MatchFinder::MatchResult &Result) { 115 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("ref")) 116 diag(MatchedDecl->getLocation(), "member %0 of type %1 is a reference") 117 << MatchedDecl << MatchedDecl->getType(); 118 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("const")) 119 diag(MatchedDecl->getLocation(), "member %0 of type %1 is const qualified") 120 << MatchedDecl << MatchedDecl->getType(); 121 } 122 123 } // namespace clang::tidy::cppcoreguidelines 124