124 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "AvoidConstOrRefDataMembersCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::cppcoreguidelines {16 17static bool isCopyConstructible(const CXXRecordDecl &Node) {18 if (Node.needsOverloadResolutionForCopyConstructor() &&19 Node.needsImplicitCopyConstructor()) {20 // unresolved21 for (const CXXBaseSpecifier &BS : Node.bases()) {22 const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl();23 if (BRD != nullptr && !isCopyConstructible(*BRD))24 return false;25 }26 }27 if (Node.hasSimpleCopyConstructor())28 return true;29 for (const CXXConstructorDecl *Ctor : Node.ctors())30 if (Ctor->isCopyConstructor())31 return !Ctor->isDeleted();32 return false;33}34 35static bool isMoveConstructible(const CXXRecordDecl &Node) {36 if (Node.needsOverloadResolutionForMoveConstructor() &&37 Node.needsImplicitMoveConstructor()) {38 // unresolved39 for (const CXXBaseSpecifier &BS : Node.bases()) {40 const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl();41 if (BRD != nullptr && !isMoveConstructible(*BRD))42 return false;43 }44 }45 if (Node.hasSimpleMoveConstructor())46 return true;47 for (const CXXConstructorDecl *Ctor : Node.ctors())48 if (Ctor->isMoveConstructor())49 return !Ctor->isDeleted();50 return false;51}52 53static bool isCopyAssignable(const CXXRecordDecl &Node) {54 if (Node.needsOverloadResolutionForCopyAssignment() &&55 Node.needsImplicitCopyAssignment()) {56 // unresolved57 for (const CXXBaseSpecifier &BS : Node.bases()) {58 const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl();59 if (BRD != nullptr && !isCopyAssignable(*BRD))60 return false;61 }62 }63 if (Node.hasSimpleCopyAssignment())64 return true;65 for (const CXXMethodDecl *Method : Node.methods())66 if (Method->isCopyAssignmentOperator())67 return !Method->isDeleted();68 return false;69}70 71static bool isMoveAssignable(const CXXRecordDecl &Node) {72 if (Node.needsOverloadResolutionForMoveAssignment() &&73 Node.needsImplicitMoveAssignment()) {74 // unresolved75 for (const CXXBaseSpecifier &BS : Node.bases()) {76 const CXXRecordDecl *BRD = BS.getType()->getAsCXXRecordDecl();77 if (BRD != nullptr && !isMoveAssignable(*BRD))78 return false;79 }80 }81 if (Node.hasSimpleMoveAssignment())82 return true;83 for (const CXXMethodDecl *Method : Node.methods())84 if (Method->isMoveAssignmentOperator())85 return !Method->isDeleted();86 return false;87}88 89namespace {90 91AST_MATCHER(FieldDecl, isMemberOfLambda) {92 return Node.getParent()->isLambda();93}94 95AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) {96 return isCopyConstructible(Node) || isMoveConstructible(Node) ||97 isCopyAssignable(Node) || isMoveAssignable(Node);98}99 100} // namespace101 102void 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 113void 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::cppcoreguidelines124