10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecordLayout.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
19 namespace cppcoreguidelines {
21 void SlicingCheck::registerMatchers(MatchFinder *Finder) {
38 const auto OfBaseClass = ofClass(cxxRecordDecl().bind(
"BaseDecl"));
39 const auto IsDerivedFromBaseDecl =
40 cxxRecordDecl(isDerivedFrom(equalsBoundNode(
"BaseDecl")))
42 const auto HasTypeDerivedFromBaseDecl =
43 anyOf(hasType(IsDerivedFromBaseDecl),
44 hasType(references(IsDerivedFromBaseDecl)));
45 const auto IsWithinDerivedCtor =
46 hasParent(cxxConstructorDecl(ofClass(equalsBoundNode(
"DerivedDecl"))));
49 const auto SlicesObjectInAssignment =
50 callExpr(callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(),
51 isMoveAssignmentOperator()),
53 hasArgument(1, HasTypeDerivedFromBaseDecl));
57 const auto SlicesObjectInCtor = cxxConstructExpr(
58 hasDeclaration(cxxConstructorDecl(
59 anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)),
60 hasArgument(0, HasTypeDerivedFromBaseDecl),
63 unless(IsWithinDerivedCtor));
66 traverse(ast_type_traits::TK_AsIs,
67 expr(anyOf(SlicesObjectInAssignment, SlicesObjectInCtor))
75 void SlicingCheck::DiagnoseSlicedOverriddenMethods(
76 const Expr &Call,
const CXXRecordDecl &DerivedDecl,
77 const CXXRecordDecl &BaseDecl) {
78 if (DerivedDecl.getCanonicalDecl() == BaseDecl.getCanonicalDecl())
80 for (
const auto *Method : DerivedDecl.methods()) {
83 if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
85 if (Method->size_overridden_methods() > 0) {
86 diag(Call.getExprLoc(),
87 "slicing object from type %0 to %1 discards override %2")
88 << &DerivedDecl << &BaseDecl << Method;
92 for (
const auto &
Base : DerivedDecl.bases()) {
93 if (
const auto *BaseRecordType =
Base.getType()->getAs<RecordType>()) {
94 if (
const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
95 BaseRecordType->getDecl()->getDefinition()))
96 DiagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
101 void SlicingCheck::check(
const MatchFinder::MatchResult &Result) {
102 const auto *BaseDecl = Result.Nodes.getNodeAs<CXXRecordDecl>(
"BaseDecl");
103 const auto *DerivedDecl =
104 Result.Nodes.getNodeAs<CXXRecordDecl>(
"DerivedDecl");
105 const auto *Call = Result.Nodes.getNodeAs<Expr>(
"Call");
106 assert(BaseDecl !=
nullptr);
107 assert(DerivedDecl !=
nullptr);
108 assert(Call !=
nullptr);
119 DiagnoseSlicedOverriddenMethods(*Call, *DerivedDecl, *BaseDecl);
122 const auto &BaseLayout =
123 BaseDecl->getASTContext().getASTRecordLayout(BaseDecl);
124 const auto &DerivedLayout =
125 DerivedDecl->getASTContext().getASTRecordLayout(DerivedDecl);
126 const CharUnits StateSize =
127 DerivedLayout.getDataSize() - BaseLayout.getDataSize();
128 if (StateSize.isPositive()) {
129 diag(Call->getExprLoc(),
"slicing object from type %0 to %1 discards "
131 << DerivedDecl << BaseDecl << static_cast<int>(StateSize.getQuantity());