19ae5896dSCarlos Galvez //===--- AvoidConstOrRefDataMembersCheck.cpp - clang-tidy -----------------===// 29ae5896dSCarlos Galvez // 39ae5896dSCarlos Galvez // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 49ae5896dSCarlos Galvez // See https://llvm.org/LICENSE.txt for license information. 59ae5896dSCarlos Galvez // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 69ae5896dSCarlos Galvez // 79ae5896dSCarlos Galvez //===----------------------------------------------------------------------===// 89ae5896dSCarlos Galvez 99ae5896dSCarlos Galvez #include "AvoidConstOrRefDataMembersCheck.h" 109ae5896dSCarlos Galvez #include "clang/AST/ASTContext.h" 119ae5896dSCarlos Galvez #include "clang/ASTMatchers/ASTMatchFinder.h" 129ae5896dSCarlos Galvez 139ae5896dSCarlos Galvez using namespace clang::ast_matchers; 149ae5896dSCarlos Galvez 157d2ea6c4SCarlos Galvez namespace clang::tidy::cppcoreguidelines { 16*605b8dadSCongcong Cai 17*605b8dadSCongcong Cai static bool isCopyConstructible(CXXRecordDecl const &Node) { 18*605b8dadSCongcong Cai if (Node.needsOverloadResolutionForCopyConstructor() && 19*605b8dadSCongcong Cai Node.needsImplicitCopyConstructor()) { 20*605b8dadSCongcong Cai // unresolved 21*605b8dadSCongcong Cai for (CXXBaseSpecifier const &BS : Node.bases()) { 22*605b8dadSCongcong Cai CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 23*605b8dadSCongcong Cai if (BRD != nullptr && !isCopyConstructible(*BRD)) 24*605b8dadSCongcong Cai return false; 25*605b8dadSCongcong Cai } 26*605b8dadSCongcong Cai } 27*605b8dadSCongcong Cai if (Node.hasSimpleCopyConstructor()) 28*605b8dadSCongcong Cai return true; 29*605b8dadSCongcong Cai for (CXXConstructorDecl const *Ctor : Node.ctors()) 30*605b8dadSCongcong Cai if (Ctor->isCopyConstructor()) 31*605b8dadSCongcong Cai return !Ctor->isDeleted(); 32*605b8dadSCongcong Cai return false; 33*605b8dadSCongcong Cai } 34*605b8dadSCongcong Cai 35*605b8dadSCongcong Cai static bool isMoveConstructible(CXXRecordDecl const &Node) { 36*605b8dadSCongcong Cai if (Node.needsOverloadResolutionForMoveConstructor() && 37*605b8dadSCongcong Cai Node.needsImplicitMoveConstructor()) { 38*605b8dadSCongcong Cai // unresolved 39*605b8dadSCongcong Cai for (CXXBaseSpecifier const &BS : Node.bases()) { 40*605b8dadSCongcong Cai CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 41*605b8dadSCongcong Cai if (BRD != nullptr && !isMoveConstructible(*BRD)) 42*605b8dadSCongcong Cai return false; 43*605b8dadSCongcong Cai } 44*605b8dadSCongcong Cai } 45*605b8dadSCongcong Cai if (Node.hasSimpleMoveConstructor()) 46*605b8dadSCongcong Cai return true; 47*605b8dadSCongcong Cai for (CXXConstructorDecl const *Ctor : Node.ctors()) 48*605b8dadSCongcong Cai if (Ctor->isMoveConstructor()) 49*605b8dadSCongcong Cai return !Ctor->isDeleted(); 50*605b8dadSCongcong Cai return false; 51*605b8dadSCongcong Cai } 52*605b8dadSCongcong Cai 53*605b8dadSCongcong Cai static bool isCopyAssignable(CXXRecordDecl const &Node) { 54*605b8dadSCongcong Cai if (Node.needsOverloadResolutionForCopyAssignment() && 55*605b8dadSCongcong Cai Node.needsImplicitCopyAssignment()) { 56*605b8dadSCongcong Cai // unresolved 57*605b8dadSCongcong Cai for (CXXBaseSpecifier const &BS : Node.bases()) { 58*605b8dadSCongcong Cai CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 59*605b8dadSCongcong Cai if (BRD != nullptr && !isCopyAssignable(*BRD)) 60*605b8dadSCongcong Cai return false; 61*605b8dadSCongcong Cai } 62*605b8dadSCongcong Cai } 63*605b8dadSCongcong Cai if (Node.hasSimpleCopyAssignment()) 64*605b8dadSCongcong Cai return true; 65*605b8dadSCongcong Cai for (CXXMethodDecl const *Method : Node.methods()) 66*605b8dadSCongcong Cai if (Method->isCopyAssignmentOperator()) 67*605b8dadSCongcong Cai return !Method->isDeleted(); 68*605b8dadSCongcong Cai return false; 69*605b8dadSCongcong Cai } 70*605b8dadSCongcong Cai 71*605b8dadSCongcong Cai static bool isMoveAssignable(CXXRecordDecl const &Node) { 72*605b8dadSCongcong Cai if (Node.needsOverloadResolutionForMoveAssignment() && 73*605b8dadSCongcong Cai Node.needsImplicitMoveAssignment()) { 74*605b8dadSCongcong Cai // unresolved 75*605b8dadSCongcong Cai for (CXXBaseSpecifier const &BS : Node.bases()) { 76*605b8dadSCongcong Cai CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); 77*605b8dadSCongcong Cai if (BRD != nullptr && !isMoveAssignable(*BRD)) 78*605b8dadSCongcong Cai return false; 79*605b8dadSCongcong Cai } 80*605b8dadSCongcong Cai } 81*605b8dadSCongcong Cai if (Node.hasSimpleMoveAssignment()) 82*605b8dadSCongcong Cai return true; 83*605b8dadSCongcong Cai for (CXXMethodDecl const *Method : Node.methods()) 84*605b8dadSCongcong Cai if (Method->isMoveAssignmentOperator()) 85*605b8dadSCongcong Cai return !Method->isDeleted(); 86*605b8dadSCongcong Cai return false; 87*605b8dadSCongcong Cai } 88*605b8dadSCongcong Cai 893fd42130SCarlos Galvez namespace { 903fd42130SCarlos Galvez 913fd42130SCarlos Galvez AST_MATCHER(FieldDecl, isMemberOfLambda) { 923fd42130SCarlos Galvez return Node.getParent()->isLambda(); 933fd42130SCarlos Galvez } 943fd42130SCarlos Galvez 95b70e6e96SCarlos Galvez AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) { 96*605b8dadSCongcong Cai return isCopyConstructible(Node) || isMoveConstructible(Node) || 97*605b8dadSCongcong Cai isCopyAssignable(Node) || isMoveAssignable(Node); 98b70e6e96SCarlos Galvez } 99b70e6e96SCarlos Galvez 1003fd42130SCarlos Galvez } // namespace 1019ae5896dSCarlos Galvez 1029ae5896dSCarlos Galvez void AvoidConstOrRefDataMembersCheck::registerMatchers(MatchFinder *Finder) { 103b70e6e96SCarlos Galvez Finder->addMatcher( 104b70e6e96SCarlos Galvez fieldDecl( 105b70e6e96SCarlos Galvez unless(isMemberOfLambda()), 106b70e6e96SCarlos Galvez anyOf( 107b70e6e96SCarlos Galvez fieldDecl(hasType(hasCanonicalType(referenceType()))).bind("ref"), 108b70e6e96SCarlos Galvez fieldDecl(hasType(qualType(isConstQualified()))).bind("const")), 109b70e6e96SCarlos Galvez hasDeclContext(cxxRecordDecl(isCopyableOrMovable()))), 1103fd42130SCarlos Galvez this); 1119ae5896dSCarlos Galvez } 1129ae5896dSCarlos Galvez 1139ae5896dSCarlos Galvez void AvoidConstOrRefDataMembersCheck::check( 1149ae5896dSCarlos Galvez const MatchFinder::MatchResult &Result) { 1159ae5896dSCarlos Galvez if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("ref")) 1169ae5896dSCarlos Galvez diag(MatchedDecl->getLocation(), "member %0 of type %1 is a reference") 1179ae5896dSCarlos Galvez << MatchedDecl << MatchedDecl->getType(); 1189ae5896dSCarlos Galvez if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>("const")) 1199ae5896dSCarlos Galvez diag(MatchedDecl->getLocation(), "member %0 of type %1 is const qualified") 1209ae5896dSCarlos Galvez << MatchedDecl << MatchedDecl->getType(); 1219ae5896dSCarlos Galvez } 1229ae5896dSCarlos Galvez 1237d2ea6c4SCarlos Galvez } // namespace clang::tidy::cppcoreguidelines 124