10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
18 using namespace clang::ast_matchers::internal;
22 namespace cppcoreguidelines {
28 const std::vector<std::string> NameList =
30 return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
35 Options.store(Opts,
"LegacyResourceProducers", LegacyResourceProducers);
36 Options.store(Opts,
"LegacyResourceConsumers", LegacyResourceConsumers);
41 void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
42 const auto OwnerDecl = typeAliasTemplateDecl(hasName(
"::gsl::owner"));
43 const auto IsOwnerType = hasType(OwnerDecl);
45 const auto LegacyCreatorFunctions =
hasAnyListedName(LegacyResourceProducers);
46 const auto LegacyConsumerFunctions =
51 const auto CreatesLegacyOwner =
52 callExpr(callee(functionDecl(LegacyCreatorFunctions)));
56 const auto LegacyOwnerCast =
57 castExpr(hasSourceExpression(CreatesLegacyOwner));
60 const auto LegacyOwnerConsumers = functionDecl(LegacyConsumerFunctions);
62 const auto CreatesOwner =
65 functionDecl(returns(qualType(hasDeclaration(OwnerDecl)))))),
66 CreatesLegacyOwner, LegacyOwnerCast);
68 const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
72 traverse(ast_type_traits::TK_AsIs,
73 cxxDeleteExpr(hasDescendant(declRefExpr(unless(ConsideredOwner))
74 .bind(
"deleted_variable")))
75 .bind(
"delete_expr")),
86 traverse(ast_type_traits::TK_AsIs,
87 callExpr(callee(LegacyOwnerConsumers),
89 expr(unless(ignoringImpCasts(ConsideredOwner)),
90 hasType(pointerType()))))
91 .bind(
"legacy_consumer")),
97 traverse(ast_type_traits::TK_AsIs,
98 binaryOperator(isAssignmentOperator(), hasLHS(IsOwnerType),
99 hasRHS(unless(ConsideredOwner)))
100 .bind(
"owner_assignment")),
105 traverse(ast_type_traits::TK_AsIs,
107 varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType)
108 .bind(
"owner_initialization"))),
111 const auto HasConstructorInitializerForOwner =
112 has(cxxConstructorDecl(forEachConstructorInitializer(
114 isMemberInitializer(), forField(IsOwnerType),
118 allOf(unless(ConsideredOwner), unless(parenListExpr()))))
119 .bind(
"owner_member_initializer"))));
123 Finder->addMatcher(traverse(ast_type_traits::TK_AsIs,
124 cxxRecordDecl(HasConstructorInitializerForOwner)),
129 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
130 hasLHS(unless(IsOwnerType)),
131 hasRHS(CreatesOwner))
132 .bind(
"bad_owner_creation_assignment"),
139 ast_type_traits::TK_AsIs,
141 varDecl(eachOf(allOf(hasInitializer(CreatesOwner),
142 unless(IsOwnerType)),
143 allOf(hasInitializer(ConsideredOwner),
144 hasType(autoType().bind(
"deduced_type")))))
145 .bind(
"bad_owner_creation_variable"))),
151 callExpr(forEachArgumentWithParam(
152 expr(unless(ConsideredOwner)).bind(
"expected_owner_argument"),
153 parmVarDecl(IsOwnerType))),
158 Finder->addMatcher(callExpr(forEachArgumentWithParam(
159 expr(CreatesOwner).bind(
"bad_owner_creation_argument"),
160 parmVarDecl(unless(IsOwnerType))
161 .bind(
"bad_owner_creation_parameter"))),
167 functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
168 .bind(
"bad_owner_return")),
169 unless(returns(qualType(hasDeclaration(OwnerDecl)))))
170 .bind(
"function_decl"),
177 has(fieldDecl(IsOwnerType).bind(
"undestructed_owner_member")),
178 anyOf(unless(has(cxxDestructorDecl())),
179 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))
180 .bind(
"non_destructor_class"),
184 void OwningMemoryCheck::check(
const MatchFinder::MatchResult &Result) {
185 const auto &Nodes = Result.Nodes;
187 bool CheckExecuted =
false;
188 CheckExecuted |= handleDeletion(Nodes);
189 CheckExecuted |= handleLegacyConsumers(Nodes);
190 CheckExecuted |= handleExpectedOwner(Nodes);
191 CheckExecuted |= handleAssignmentAndInit(Nodes);
192 CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
193 CheckExecuted |= handleReturnValues(Nodes);
194 CheckExecuted |= handleOwnerMembers(Nodes);
196 assert(CheckExecuted &&
197 "None of the subroutines executed, logic error in matcher!");
200 bool OwningMemoryCheck::handleDeletion(
const BoundNodes &Nodes) {
202 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>(
"delete_expr");
203 const auto *DeletedVariable =
204 Nodes.getNodeAs<DeclRefExpr>(
"deleted_variable");
208 diag(DeleteStmt->getBeginLoc(),
209 "deleting a pointer through a type that is "
210 "not marked 'gsl::owner<>'; consider using a "
211 "smart pointer instead")
212 << DeletedVariable->getSourceRange();
216 const ValueDecl *
Decl = DeletedVariable->getDecl();
217 diag(
Decl->getBeginLoc(),
"variable declared here", DiagnosticIDs::Note)
218 <<
Decl->getSourceRange();
225 bool OwningMemoryCheck::handleLegacyConsumers(
const BoundNodes &Nodes) {
227 const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>(
"legacy_consumer");
232 if (LegacyConsumer) {
233 diag(LegacyConsumer->getBeginLoc(),
234 "calling legacy resource function without passing a 'gsl::owner<>'")
235 << LegacyConsumer->getSourceRange();
241 bool OwningMemoryCheck::handleExpectedOwner(
const BoundNodes &Nodes) {
243 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>(
"expected_owner_argument");
247 diag(ExpectedOwner->getBeginLoc(),
248 "expected argument of type 'gsl::owner<>'; got %0")
249 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
256 bool OwningMemoryCheck::handleAssignmentAndInit(
const BoundNodes &Nodes) {
257 const auto *OwnerAssignment =
258 Nodes.getNodeAs<BinaryOperator>(
"owner_assignment");
259 const auto *OwnerInitialization =
260 Nodes.getNodeAs<VarDecl>(
"owner_initialization");
261 const auto *OwnerInitializer =
262 Nodes.getNodeAs<CXXCtorInitializer>(
"owner_member_initializer");
265 if (OwnerAssignment) {
266 diag(OwnerAssignment->getBeginLoc(),
267 "expected assignment source to be of type 'gsl::owner<>'; got %0")
268 << OwnerAssignment->getRHS()->getType()
269 << OwnerAssignment->getSourceRange();
274 if (OwnerInitialization) {
275 diag(OwnerInitialization->getBeginLoc(),
276 "expected initialization with value of type 'gsl::owner<>'; got %0")
277 << OwnerInitialization->getAnyInitializer()->getType()
278 << OwnerInitialization->getSourceRange();
283 if (OwnerInitializer) {
284 diag(OwnerInitializer->getSourceLocation(),
285 "expected initialization of owner member variable with value of type "
286 "'gsl::owner<>'; got %0")
289 << OwnerInitializer->getInit()->getType()
290 << OwnerInitializer->getSourceRange();
298 bool OwningMemoryCheck::handleAssignmentFromNewOwner(
const BoundNodes &Nodes) {
299 const auto *BadOwnerAssignment =
300 Nodes.getNodeAs<BinaryOperator>(
"bad_owner_creation_assignment");
301 const auto *BadOwnerInitialization =
302 Nodes.getNodeAs<VarDecl>(
"bad_owner_creation_variable");
304 const auto *BadOwnerArgument =
305 Nodes.getNodeAs<Expr>(
"bad_owner_creation_argument");
306 const auto *BadOwnerParameter =
307 Nodes.getNodeAs<ParmVarDecl>(
"bad_owner_creation_parameter");
310 if (BadOwnerAssignment) {
311 diag(BadOwnerAssignment->getBeginLoc(),
312 "assigning newly created 'gsl::owner<>' to non-owner %0")
313 << BadOwnerAssignment->getLHS()->getType()
314 << BadOwnerAssignment->getSourceRange();
319 if (BadOwnerInitialization) {
320 diag(BadOwnerInitialization->getBeginLoc(),
321 "initializing non-owner %0 with a newly created 'gsl::owner<>'")
322 << BadOwnerInitialization->getType()
323 << BadOwnerInitialization->getSourceRange();
330 if (Nodes.getNodeAs<AutoType>(
"deduced_type")) {
331 diag(BadOwnerInitialization->getBeginLoc(),
332 "type deduction did not result in an owner", DiagnosticIDs::Note);
339 if (BadOwnerArgument) {
340 assert(BadOwnerParameter &&
341 "parameter for the problematic argument not found");
342 diag(BadOwnerArgument->getBeginLoc(),
"initializing non-owner argument of "
343 "type %0 with a newly created "
345 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
351 bool OwningMemoryCheck::handleReturnValues(
const BoundNodes &Nodes) {
354 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>(
"bad_owner_return");
355 const auto *
Function = Nodes.getNodeAs<FunctionDecl>(
"function_decl");
361 diag(BadReturnType->getBeginLoc(),
362 "returning a newly created resource of "
363 "type %0 or 'gsl::owner<>' from a "
364 "function whose return type is not 'gsl::owner<>'")
365 <<
Function->getReturnType() << BadReturnType->getSourceRange();
373 bool OwningMemoryCheck::handleOwnerMembers(
const BoundNodes &Nodes) {
376 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>(
"non_destructor_class");
380 const auto *DeclaredOwnerMember =
381 Nodes.getNodeAs<FieldDecl>(
"undestructed_owner_member");
382 assert(DeclaredOwnerMember &&
383 "match on class with bad destructor but without a declared owner");
385 diag(DeclaredOwnerMember->getBeginLoc(),
386 "member variable of type 'gsl::owner<>' requires the class %0 to "
387 "implement a destructor to release the owned resource")