11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
16using namespace clang::ast_matchers::internal;
21AST_MATCHER_P(LambdaExpr, hasCallOperator, Matcher<CXXMethodDecl>,
23 return InnerMatcher.matches(*Node.getCallOperator(), Finder, Builder);
26AST_MATCHER_P(LambdaExpr, hasLambdaBody, Matcher<Stmt>, InnerMatcher) {
27 return InnerMatcher.matches(*Node.getBody(), Finder, Builder);
32 Options.store(Opts,
"LegacyResourceProducers", LegacyResourceProducers);
33 Options.store(Opts,
"LegacyResourceConsumers", LegacyResourceConsumers);
39 const auto OwnerDecl = typeAliasTemplateDecl(hasName(
"::gsl::owner"));
40 const auto IsOwnerType = hasType(OwnerDecl);
42 const auto LegacyCreatorFunctions =
44 const auto LegacyConsumerFunctions =
49 const auto CreatesLegacyOwner =
50 callExpr(callee(functionDecl(LegacyCreatorFunctions)));
54 const auto LegacyOwnerCast =
55 castExpr(hasSourceExpression(CreatesLegacyOwner));
58 const auto LegacyOwnerConsumers = functionDecl(LegacyConsumerFunctions);
60 const auto CreatesOwner =
63 functionDecl(returns(qualType(hasDeclaration(OwnerDecl)))))),
64 CreatesLegacyOwner, LegacyOwnerCast);
66 const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
67 const auto ScopeDeclaration = anyOf(translationUnitDecl(), namespaceDecl(),
68 recordDecl(), functionDecl());
73 cxxDeleteExpr(hasDescendant(declRefExpr(unless(ConsideredOwner))
74 .bind(
"deleted_variable")))
75 .bind(
"delete_expr")),
86 traverse(TK_AsIs, callExpr(callee(LegacyOwnerConsumers),
88 unless(ignoringImpCasts(ConsideredOwner)),
89 hasType(pointerType()))))
90 .bind(
"legacy_consumer")),
97 binaryOperator(isAssignmentOperator(), hasLHS(IsOwnerType),
98 hasRHS(unless(ConsideredOwner)))
99 .bind(
"owner_assignment")),
106 varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType)
107 .bind(
"owner_initialization"))),
110 const auto HasConstructorInitializerForOwner =
111 has(cxxConstructorDecl(forEachConstructorInitializer(
113 isMemberInitializer(), forField(IsOwnerType),
117 allOf(unless(ConsideredOwner), unless(parenListExpr()))))
118 .bind(
"owner_member_initializer"))));
123 traverse(TK_AsIs, cxxRecordDecl(HasConstructorInitializerForOwner)),
128 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
129 hasLHS(unless(IsOwnerType)),
130 hasRHS(CreatesOwner))
131 .bind(
"bad_owner_creation_assignment"),
137 traverse(TK_AsIs, namedDecl(varDecl(hasInitializer(CreatesOwner),
139 .bind(
"bad_owner_creation_variable"))),
145 callExpr(forEachArgumentWithParam(
146 expr(unless(ConsideredOwner)).bind(
"expected_owner_argument"),
147 parmVarDecl(IsOwnerType))),
152 Finder->addMatcher(callExpr(forEachArgumentWithParam(
153 expr(CreatesOwner).bind(
"bad_owner_creation_argument"),
154 parmVarDecl(unless(IsOwnerType))
155 .bind(
"bad_owner_creation_parameter"))),
158 auto IsNotInSubLambda = stmt(
160 stmt(anyOf(equalsBoundNode(
"body"), lambdaExpr())).bind(
"scope")),
161 hasAncestor(stmt(equalsBoundNode(
"scope"), equalsBoundNode(
"body"))));
167 decl().bind(
"function_decl"),
169 stmt(stmt().bind(
"body"),
171 returnStmt(hasReturnValue(ConsideredOwner),
175 hasAncestor(functionDecl().bind(
"context")),
176 hasAncestor(functionDecl(
177 equalsBoundNode(
"context"),
178 equalsBoundNode(
"function_decl"))))
179 .bind(
"bad_owner_return")))),
180 returns(qualType(unless(hasDeclaration(OwnerDecl))).bind(
"result"))),
187 hasAncestor(decl(ScopeDeclaration).bind(
"scope-decl")),
189 stmt(stmt().bind(
"body"),
192 hasReturnValue(ConsideredOwner),
196 hasAncestor(decl(ScopeDeclaration).bind(
"context")),
197 hasAncestor(decl(equalsBoundNode(
"context"),
198 equalsBoundNode(
"scope-decl"))))
199 .bind(
"bad_owner_return")))),
200 hasCallOperator(returns(
201 qualType(unless(hasDeclaration(OwnerDecl))).bind(
"result"))))
209 has(fieldDecl(IsOwnerType).bind(
"undestructed_owner_member")),
210 anyOf(unless(has(cxxDestructorDecl())),
211 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))
212 .bind(
"non_destructor_class"),
217 const auto &Nodes = Result.Nodes;
219 bool CheckExecuted =
false;
220 CheckExecuted |= handleDeletion(Nodes);
221 CheckExecuted |= handleLegacyConsumers(Nodes);
222 CheckExecuted |= handleExpectedOwner(Nodes);
223 CheckExecuted |= handleAssignmentAndInit(Nodes);
224 CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
225 CheckExecuted |= handleReturnValues(Nodes);
226 CheckExecuted |= handleOwnerMembers(Nodes);
229 assert(CheckExecuted &&
230 "None of the subroutines executed, logic error in matcher!");
233bool OwningMemoryCheck::handleDeletion(
const BoundNodes &Nodes) {
235 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>(
"delete_expr");
236 const auto *DeletedVariable =
237 Nodes.getNodeAs<DeclRefExpr>(
"deleted_variable");
241 diag(DeleteStmt->getBeginLoc(),
242 "deleting a pointer through a type that is "
243 "not marked 'gsl::owner<>'; consider using a "
244 "smart pointer instead")
245 << DeletedVariable->getSourceRange();
249 const ValueDecl *Decl = DeletedVariable->getDecl();
250 diag(Decl->getBeginLoc(),
"variable declared here", DiagnosticIDs::Note)
251 << Decl->getSourceRange();
258bool OwningMemoryCheck::handleLegacyConsumers(
const BoundNodes &Nodes) {
260 const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>(
"legacy_consumer");
265 if (LegacyConsumer) {
266 diag(LegacyConsumer->getBeginLoc(),
267 "calling legacy resource function without passing a 'gsl::owner<>'")
268 << LegacyConsumer->getSourceRange();
274bool OwningMemoryCheck::handleExpectedOwner(
const BoundNodes &Nodes) {
276 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>(
"expected_owner_argument");
280 diag(ExpectedOwner->getBeginLoc(),
281 "expected argument of type 'gsl::owner<>'; got %0")
282 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
289bool OwningMemoryCheck::handleAssignmentAndInit(
const BoundNodes &Nodes) {
290 const auto *OwnerAssignment =
291 Nodes.getNodeAs<BinaryOperator>(
"owner_assignment");
292 const auto *OwnerInitialization =
293 Nodes.getNodeAs<VarDecl>(
"owner_initialization");
294 const auto *OwnerInitializer =
295 Nodes.getNodeAs<CXXCtorInitializer>(
"owner_member_initializer");
298 if (OwnerAssignment) {
299 diag(OwnerAssignment->getBeginLoc(),
300 "expected assignment source to be of type 'gsl::owner<>'; got %0")
301 << OwnerAssignment->getRHS()->getType()
302 << OwnerAssignment->getSourceRange();
307 if (OwnerInitialization) {
308 diag(OwnerInitialization->getBeginLoc(),
309 "expected initialization with value of type 'gsl::owner<>'; got %0")
310 << OwnerInitialization->getAnyInitializer()->getType()
311 << OwnerInitialization->getSourceRange();
316 if (OwnerInitializer) {
317 diag(OwnerInitializer->getSourceLocation(),
318 "expected initialization of owner member variable with value of type "
319 "'gsl::owner<>'; got %0")
322 << OwnerInitializer->getInit()->getType()
323 << OwnerInitializer->getSourceRange();
331bool OwningMemoryCheck::handleAssignmentFromNewOwner(
const BoundNodes &Nodes) {
332 const auto *BadOwnerAssignment =
333 Nodes.getNodeAs<BinaryOperator>(
"bad_owner_creation_assignment");
334 const auto *BadOwnerInitialization =
335 Nodes.getNodeAs<VarDecl>(
"bad_owner_creation_variable");
337 const auto *BadOwnerArgument =
338 Nodes.getNodeAs<Expr>(
"bad_owner_creation_argument");
339 const auto *BadOwnerParameter =
340 Nodes.getNodeAs<ParmVarDecl>(
"bad_owner_creation_parameter");
343 if (BadOwnerAssignment) {
344 diag(BadOwnerAssignment->getBeginLoc(),
345 "assigning newly created 'gsl::owner<>' to non-owner %0")
346 << BadOwnerAssignment->getLHS()->getType()
347 << BadOwnerAssignment->getSourceRange();
352 if (BadOwnerInitialization) {
353 diag(BadOwnerInitialization->getBeginLoc(),
354 "initializing non-owner %0 with a newly created 'gsl::owner<>'")
355 << BadOwnerInitialization->getType()
356 << BadOwnerInitialization->getSourceRange();
365 if (BadOwnerArgument) {
366 assert(BadOwnerParameter &&
367 "parameter for the problematic argument not found");
368 diag(BadOwnerArgument->getBeginLoc(),
"initializing non-owner argument of "
369 "type %0 with a newly created "
371 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
377bool OwningMemoryCheck::handleReturnValues(
const BoundNodes &Nodes) {
380 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>(
"bad_owner_return");
381 const auto *ResultType = Nodes.getNodeAs<QualType>(
"result");
387 diag(BadReturnType->getBeginLoc(),
388 "returning a newly created resource of "
389 "type %0 or 'gsl::owner<>' from a "
390 "%select{function|lambda}1 whose return type is not 'gsl::owner<>'")
391 << *ResultType << (Nodes.getNodeAs<Expr>(
"lambda") !=
nullptr)
392 << BadReturnType->getSourceRange();
400bool OwningMemoryCheck::handleOwnerMembers(
const BoundNodes &Nodes) {
403 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>(
"non_destructor_class");
407 const auto *DeclaredOwnerMember =
408 Nodes.getNodeAs<FieldDecl>(
"undestructed_owner_member");
409 assert(DeclaredOwnerMember &&
410 "match on class with bad destructor but without a declared owner");
412 diag(DeclaredOwnerMember->getBeginLoc(),
413 "member variable of type 'gsl::owner<>' requires the class %0 to "
414 "implement a destructor to release the owned resource")
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Make configuration of checker discoverable.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Match common cases, where the owner semantic is relevant, like function calls, delete expressions and...
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap