11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/RecordLayout.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "clang/ASTMatchers/ASTMatchers.h" 20 namespace cppcoreguidelines {
22 void SlicingCheck::registerMatchers(MatchFinder *Finder) {
39 const auto OfBaseClass = ofClass(cxxRecordDecl().bind(
"BaseDecl"));
40 const auto IsDerivedFromBaseDecl =
41 cxxRecordDecl(isDerivedFrom(equalsBoundNode(
"BaseDecl")))
43 const auto HasTypeDerivedFromBaseDecl =
44 anyOf(hasType(IsDerivedFromBaseDecl),
45 hasType(references(IsDerivedFromBaseDecl)));
46 const auto IsWithinDerivedCtor =
47 hasParent(cxxConstructorDecl(ofClass(equalsBoundNode(
"DerivedDecl"))));
50 const auto SlicesObjectInAssignment =
51 callExpr(callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(),
52 isMoveAssignmentOperator()),
54 hasArgument(1, HasTypeDerivedFromBaseDecl));
58 const auto SlicesObjectInCtor = cxxConstructExpr(
59 hasDeclaration(cxxConstructorDecl(
60 anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)),
61 hasArgument(0, HasTypeDerivedFromBaseDecl),
64 unless(IsWithinDerivedCtor));
67 expr(anyOf(SlicesObjectInAssignment, SlicesObjectInCtor)).bind(
"Call"),
74 void SlicingCheck::DiagnoseSlicedOverriddenMethods(
75 const Expr &Call,
const CXXRecordDecl &DerivedDecl,
76 const CXXRecordDecl &BaseDecl) {
77 if (DerivedDecl.getCanonicalDecl() == BaseDecl.getCanonicalDecl())
79 for (
const auto &Method : DerivedDecl.methods()) {
82 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
84 if (Method->size_overridden_methods() > 0) {
85 diag(Call.getExprLoc(),
86 "slicing object from type %0 to %1 discards override %2")
87 << &DerivedDecl << &BaseDecl << Method;
91 for (
const auto &Base : DerivedDecl.bases()) {
92 if (
const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) {
93 if (
const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
94 BaseRecordType->getDecl()->getDefinition()))
95 DiagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
100 void SlicingCheck::check(
const MatchFinder::MatchResult &Result) {
101 const auto *BaseDecl = Result.Nodes.getNodeAs<CXXRecordDecl>(
"BaseDecl");
102 const auto *DerivedDecl =
103 Result.Nodes.getNodeAs<CXXRecordDecl>(
"DerivedDecl");
104 const auto *Call = Result.Nodes.getNodeAs<Expr>(
"Call");
105 assert(BaseDecl !=
nullptr);
106 assert(DerivedDecl !=
nullptr);
107 assert(Call !=
nullptr);
118 DiagnoseSlicedOverriddenMethods(*Call, *DerivedDecl, *BaseDecl);
121 const auto &BaseLayout =
122 BaseDecl->getASTContext().getASTRecordLayout(BaseDecl);
123 const auto &DerivedLayout =
124 DerivedDecl->getASTContext().getASTRecordLayout(DerivedDecl);
125 const CharUnits StateSize =
126 DerivedLayout.getDataSize() - BaseLayout.getDataSize();
127 if (StateSize.isPositive()) {
128 diag(Call->getExprLoc(),
"slicing object from type %0 to %1 discards " 130 << DerivedDecl << BaseDecl << static_cast<int>(StateSize.getQuantity());