10#include "../utils/Matchers.h"
11#include "../utils/OptionsUtils.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
18using namespace clang::ast_matchers::internal;
23AST_MATCHER_P(LambdaExpr, hasCallOperator, Matcher<CXXMethodDecl>,
25 return InnerMatcher.matches(*
Node.getCallOperator(), Finder,
Builder);
28AST_MATCHER_P(LambdaExpr, hasLambdaBody, Matcher<Stmt>, InnerMatcher) {
29 return InnerMatcher.matches(*
Node.getBody(), Finder,
Builder);
34 Options.
store(Opts,
"LegacyResourceProducers", LegacyResourceProducers);
35 Options.
store(Opts,
"LegacyResourceConsumers", LegacyResourceConsumers);
41 const auto OwnerDecl = typeAliasTemplateDecl(hasName(
"::gsl::owner"));
42 const auto IsOwnerType = hasType(OwnerDecl);
44 const auto LegacyCreatorFunctions =
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);
69 const auto ScopeDeclaration = anyOf(translationUnitDecl(), namespaceDecl(),
70 recordDecl(), functionDecl());
75 cxxDeleteExpr(hasDescendant(declRefExpr(unless(ConsideredOwner))
76 .bind(
"deleted_variable")))
77 .bind(
"delete_expr")),
88 traverse(TK_AsIs, callExpr(callee(LegacyOwnerConsumers),
90 unless(ignoringImpCasts(ConsideredOwner)),
91 hasType(pointerType()))))
92 .bind(
"legacy_consumer")),
99 binaryOperator(isAssignmentOperator(), hasLHS(IsOwnerType),
100 hasRHS(unless(ConsideredOwner)))
101 .bind(
"owner_assignment")),
108 varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType)
109 .bind(
"owner_initialization"))),
112 const auto HasConstructorInitializerForOwner =
113 has(cxxConstructorDecl(forEachConstructorInitializer(
115 isMemberInitializer(), forField(IsOwnerType),
119 allOf(unless(ConsideredOwner), unless(parenListExpr()))))
120 .bind(
"owner_member_initializer"))));
125 traverse(TK_AsIs, cxxRecordDecl(HasConstructorInitializerForOwner)),
130 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
131 hasLHS(unless(IsOwnerType)),
132 hasRHS(CreatesOwner))
133 .bind(
"bad_owner_creation_assignment"),
139 traverse(TK_AsIs, namedDecl(varDecl(hasInitializer(CreatesOwner),
141 .bind(
"bad_owner_creation_variable"))),
147 callExpr(forEachArgumentWithParam(
148 expr(unless(ConsideredOwner)).bind(
"expected_owner_argument"),
149 parmVarDecl(IsOwnerType))),
154 Finder->addMatcher(callExpr(forEachArgumentWithParam(
155 expr(CreatesOwner).bind(
"bad_owner_creation_argument"),
156 parmVarDecl(unless(IsOwnerType))
157 .bind(
"bad_owner_creation_parameter"))),
160 auto IsNotInSubLambda = stmt(
162 stmt(anyOf(equalsBoundNode(
"body"), lambdaExpr())).bind(
"scope")),
163 hasAncestor(stmt(equalsBoundNode(
"scope"), equalsBoundNode(
"body"))));
169 decl().bind(
"function_decl"),
171 stmt(stmt().bind(
"body"),
173 returnStmt(hasReturnValue(ConsideredOwner),
177 hasAncestor(functionDecl().bind(
"context")),
178 hasAncestor(functionDecl(
179 equalsBoundNode(
"context"),
180 equalsBoundNode(
"function_decl"))))
181 .bind(
"bad_owner_return")))),
182 returns(qualType(unless(hasDeclaration(OwnerDecl))).bind(
"result"))),
189 hasAncestor(decl(ScopeDeclaration).bind(
"scope-decl")),
191 stmt(stmt().bind(
"body"),
194 hasReturnValue(ConsideredOwner),
198 hasAncestor(decl(ScopeDeclaration).bind(
"context")),
199 hasAncestor(decl(equalsBoundNode(
"context"),
200 equalsBoundNode(
"scope-decl"))))
201 .bind(
"bad_owner_return")))),
202 hasCallOperator(returns(
203 qualType(unless(hasDeclaration(OwnerDecl))).bind(
"result"))))
211 has(fieldDecl(IsOwnerType).bind(
"undestructed_owner_member")),
212 anyOf(unless(has(cxxDestructorDecl())),
213 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))
214 .bind(
"non_destructor_class"),
219 const auto &Nodes = Result.Nodes;
221 bool CheckExecuted =
false;
222 CheckExecuted |= handleDeletion(Nodes);
223 CheckExecuted |= handleLegacyConsumers(Nodes);
224 CheckExecuted |= handleExpectedOwner(Nodes);
225 CheckExecuted |= handleAssignmentAndInit(Nodes);
226 CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
227 CheckExecuted |= handleReturnValues(Nodes);
228 CheckExecuted |= handleOwnerMembers(Nodes);
231 assert(CheckExecuted &&
232 "None of the subroutines executed, logic error in matcher!");
235bool OwningMemoryCheck::handleDeletion(
const BoundNodes &Nodes) {
237 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>(
"delete_expr");
238 const auto *DeletedVariable =
239 Nodes.getNodeAs<DeclRefExpr>(
"deleted_variable");
243 diag(DeleteStmt->getBeginLoc(),
244 "deleting a pointer through a type that is "
245 "not marked 'gsl::owner<>'; consider using a "
246 "smart pointer instead")
247 << DeletedVariable->getSourceRange();
251 const ValueDecl *
Decl = DeletedVariable->getDecl();
252 diag(
Decl->getBeginLoc(),
"variable declared here", DiagnosticIDs::Note)
253 <<
Decl->getSourceRange();
260bool OwningMemoryCheck::handleLegacyConsumers(
const BoundNodes &Nodes) {
262 const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>(
"legacy_consumer");
267 if (LegacyConsumer) {
268 diag(LegacyConsumer->getBeginLoc(),
269 "calling legacy resource function without passing a 'gsl::owner<>'")
270 << LegacyConsumer->getSourceRange();
276bool OwningMemoryCheck::handleExpectedOwner(
const BoundNodes &Nodes) {
278 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>(
"expected_owner_argument");
282 diag(ExpectedOwner->getBeginLoc(),
283 "expected argument of type 'gsl::owner<>'; got %0")
284 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
291bool OwningMemoryCheck::handleAssignmentAndInit(
const BoundNodes &Nodes) {
292 const auto *OwnerAssignment =
293 Nodes.getNodeAs<BinaryOperator>(
"owner_assignment");
294 const auto *OwnerInitialization =
295 Nodes.getNodeAs<VarDecl>(
"owner_initialization");
296 const auto *OwnerInitializer =
297 Nodes.getNodeAs<CXXCtorInitializer>(
"owner_member_initializer");
300 if (OwnerAssignment) {
301 diag(OwnerAssignment->getBeginLoc(),
302 "expected assignment source to be of type 'gsl::owner<>'; got %0")
303 << OwnerAssignment->getRHS()->getType()
304 << OwnerAssignment->getSourceRange();
309 if (OwnerInitialization) {
310 diag(OwnerInitialization->getBeginLoc(),
311 "expected initialization with value of type 'gsl::owner<>'; got %0")
312 << OwnerInitialization->getAnyInitializer()->getType()
313 << OwnerInitialization->getSourceRange();
318 if (OwnerInitializer) {
319 diag(OwnerInitializer->getSourceLocation(),
320 "expected initialization of owner member variable with value of type "
321 "'gsl::owner<>'; got %0")
324 << OwnerInitializer->getInit()->getType()
325 << OwnerInitializer->getSourceRange();
333bool OwningMemoryCheck::handleAssignmentFromNewOwner(
const BoundNodes &Nodes) {
334 const auto *BadOwnerAssignment =
335 Nodes.getNodeAs<BinaryOperator>(
"bad_owner_creation_assignment");
336 const auto *BadOwnerInitialization =
337 Nodes.getNodeAs<VarDecl>(
"bad_owner_creation_variable");
339 const auto *BadOwnerArgument =
340 Nodes.getNodeAs<Expr>(
"bad_owner_creation_argument");
341 const auto *BadOwnerParameter =
342 Nodes.getNodeAs<ParmVarDecl>(
"bad_owner_creation_parameter");
345 if (BadOwnerAssignment) {
346 diag(BadOwnerAssignment->getBeginLoc(),
347 "assigning newly created 'gsl::owner<>' to non-owner %0")
348 << BadOwnerAssignment->getLHS()->getType()
349 << BadOwnerAssignment->getSourceRange();
354 if (BadOwnerInitialization) {
355 diag(BadOwnerInitialization->getBeginLoc(),
356 "initializing non-owner %0 with a newly created 'gsl::owner<>'")
357 << BadOwnerInitialization->getType()
358 << BadOwnerInitialization->getSourceRange();
367 if (BadOwnerArgument) {
368 assert(BadOwnerParameter &&
369 "parameter for the problematic argument not found");
370 diag(BadOwnerArgument->getBeginLoc(),
"initializing non-owner argument of "
371 "type %0 with a newly created "
373 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
379bool OwningMemoryCheck::handleReturnValues(
const BoundNodes &Nodes) {
382 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>(
"bad_owner_return");
383 const auto *ResultType = Nodes.getNodeAs<QualType>(
"result");
389 diag(BadReturnType->getBeginLoc(),
390 "returning a newly created resource of "
391 "type %0 or 'gsl::owner<>' from a "
392 "%select{function|lambda}1 whose return type is not 'gsl::owner<>'")
393 << *ResultType << (Nodes.getNodeAs<Expr>(
"lambda") !=
nullptr)
394 << BadReturnType->getSourceRange();
402bool OwningMemoryCheck::handleOwnerMembers(
const BoundNodes &Nodes) {
405 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>(
"non_destructor_class");
409 const auto *DeclaredOwnerMember =
410 Nodes.getNodeAs<FieldDecl>(
"undestructed_owner_member");
411 assert(DeclaredOwnerMember &&
412 "match on class with bad destructor but without a declared owner");
414 diag(DeclaredOwnerMember->getBeginLoc(),
415 "member variable of type 'gsl::owner<>' requires the class %0 to "
416 "implement a destructor to release the owned resource")
const FunctionDecl * Decl
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
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 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(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap