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