10#include "clang/AST/ASTContext.h"
11#include "clang/AST/RecordLayout.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
36 const auto OfBaseClass = ofClass(cxxRecordDecl().bind(
"BaseDecl"));
37 const auto IsDerivedFromBaseDecl =
38 cxxRecordDecl(isDerivedFrom(equalsBoundNode(
"BaseDecl")))
40 const auto HasTypeDerivedFromBaseDecl =
41 anyOf(hasType(IsDerivedFromBaseDecl),
42 hasType(references(IsDerivedFromBaseDecl)));
43 const auto IsCallToBaseClass = hasParent(cxxConstructorDecl(
44 ofClass(isSameOrDerivedFrom(equalsBoundNode(
"DerivedDecl"))),
45 hasAnyConstructorInitializer(allOf(
46 isBaseInitializer(), withInitializer(equalsBoundNode(
"Call"))))));
49 const auto SlicesObjectInAssignment =
50 callExpr(expr().bind(
"Call"),
51 callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(),
52 isMoveAssignmentOperator()),
54 hasArgument(1, HasTypeDerivedFromBaseDecl));
58 const auto SlicesObjectInCtor = cxxConstructExpr(
60 hasDeclaration(cxxConstructorDecl(
61 anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)),
62 hasArgument(0, HasTypeDerivedFromBaseDecl),
65 unless(IsCallToBaseClass));
68 traverse(TK_AsIs, expr(SlicesObjectInAssignment).bind(
"Call")),
this);
69 Finder->addMatcher(traverse(TK_AsIs, SlicesObjectInCtor),
this);
75void 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);
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());
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.