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;
23 Options.
store(Opts,
"LegacyResourceProducers", LegacyResourceProducers);
24 Options.
store(Opts,
"LegacyResourceConsumers", LegacyResourceConsumers);
30 const auto OwnerDecl = typeAliasTemplateDecl(hasName(
"::gsl::owner"));
31 const auto IsOwnerType = hasType(OwnerDecl);
33 const auto LegacyCreatorFunctions =
35 const auto LegacyConsumerFunctions =
40 const auto CreatesLegacyOwner =
41 callExpr(callee(functionDecl(LegacyCreatorFunctions)));
45 const auto LegacyOwnerCast =
46 castExpr(hasSourceExpression(CreatesLegacyOwner));
49 const auto LegacyOwnerConsumers = functionDecl(LegacyConsumerFunctions);
51 const auto CreatesOwner =
54 functionDecl(returns(qualType(hasDeclaration(OwnerDecl)))))),
55 CreatesLegacyOwner, LegacyOwnerCast);
57 const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
62 cxxDeleteExpr(hasDescendant(declRefExpr(unless(ConsideredOwner))
63 .bind(
"deleted_variable")))
64 .bind(
"delete_expr")),
75 traverse(TK_AsIs, callExpr(callee(LegacyOwnerConsumers),
77 unless(ignoringImpCasts(ConsideredOwner)),
78 hasType(pointerType()))))
79 .bind(
"legacy_consumer")),
86 binaryOperator(isAssignmentOperator(), hasLHS(IsOwnerType),
87 hasRHS(unless(ConsideredOwner)))
88 .bind(
"owner_assignment")),
95 varDecl(hasInitializer(unless(ConsideredOwner)), IsOwnerType)
96 .bind(
"owner_initialization"))),
99 const auto HasConstructorInitializerForOwner =
100 has(cxxConstructorDecl(forEachConstructorInitializer(
102 isMemberInitializer(), forField(IsOwnerType),
106 allOf(unless(ConsideredOwner), unless(parenListExpr()))))
107 .bind(
"owner_member_initializer"))));
112 traverse(TK_AsIs, cxxRecordDecl(HasConstructorInitializerForOwner)),
117 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
118 hasLHS(unless(IsOwnerType)),
119 hasRHS(CreatesOwner))
120 .bind(
"bad_owner_creation_assignment"),
126 traverse(TK_AsIs, namedDecl(varDecl(hasInitializer(CreatesOwner),
128 .bind(
"bad_owner_creation_variable"))),
134 callExpr(forEachArgumentWithParam(
135 expr(unless(ConsideredOwner)).bind(
"expected_owner_argument"),
136 parmVarDecl(IsOwnerType))),
141 Finder->addMatcher(callExpr(forEachArgumentWithParam(
142 expr(CreatesOwner).bind(
"bad_owner_creation_argument"),
143 parmVarDecl(unless(IsOwnerType))
144 .bind(
"bad_owner_creation_parameter"))),
150 functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
151 .bind(
"bad_owner_return")),
152 unless(returns(qualType(hasDeclaration(OwnerDecl)))))
153 .bind(
"function_decl"),
160 has(fieldDecl(IsOwnerType).bind(
"undestructed_owner_member")),
161 anyOf(unless(has(cxxDestructorDecl())),
162 has(cxxDestructorDecl(anyOf(isDefaulted(), isDeleted())))))
163 .bind(
"non_destructor_class"),
168 const auto &Nodes = Result.Nodes;
170 bool CheckExecuted =
false;
171 CheckExecuted |= handleDeletion(Nodes);
172 CheckExecuted |= handleLegacyConsumers(Nodes);
173 CheckExecuted |= handleExpectedOwner(Nodes);
174 CheckExecuted |= handleAssignmentAndInit(Nodes);
175 CheckExecuted |= handleAssignmentFromNewOwner(Nodes);
176 CheckExecuted |= handleReturnValues(Nodes);
177 CheckExecuted |= handleOwnerMembers(Nodes);
180 assert(CheckExecuted &&
181 "None of the subroutines executed, logic error in matcher!");
184bool OwningMemoryCheck::handleDeletion(
const BoundNodes &Nodes) {
186 const auto *DeleteStmt = Nodes.getNodeAs<CXXDeleteExpr>(
"delete_expr");
187 const auto *DeletedVariable =
188 Nodes.getNodeAs<DeclRefExpr>(
"deleted_variable");
192 diag(DeleteStmt->getBeginLoc(),
193 "deleting a pointer through a type that is "
194 "not marked 'gsl::owner<>'; consider using a "
195 "smart pointer instead")
196 << DeletedVariable->getSourceRange();
200 const ValueDecl *
Decl = DeletedVariable->getDecl();
201 diag(
Decl->getBeginLoc(),
"variable declared here", DiagnosticIDs::Note)
202 <<
Decl->getSourceRange();
209bool OwningMemoryCheck::handleLegacyConsumers(
const BoundNodes &Nodes) {
211 const auto *LegacyConsumer = Nodes.getNodeAs<CallExpr>(
"legacy_consumer");
216 if (LegacyConsumer) {
217 diag(LegacyConsumer->getBeginLoc(),
218 "calling legacy resource function without passing a 'gsl::owner<>'")
219 << LegacyConsumer->getSourceRange();
225bool OwningMemoryCheck::handleExpectedOwner(
const BoundNodes &Nodes) {
227 const auto *ExpectedOwner = Nodes.getNodeAs<Expr>(
"expected_owner_argument");
231 diag(ExpectedOwner->getBeginLoc(),
232 "expected argument of type 'gsl::owner<>'; got %0")
233 << ExpectedOwner->getType() << ExpectedOwner->getSourceRange();
240bool OwningMemoryCheck::handleAssignmentAndInit(
const BoundNodes &Nodes) {
241 const auto *OwnerAssignment =
242 Nodes.getNodeAs<BinaryOperator>(
"owner_assignment");
243 const auto *OwnerInitialization =
244 Nodes.getNodeAs<VarDecl>(
"owner_initialization");
245 const auto *OwnerInitializer =
246 Nodes.getNodeAs<CXXCtorInitializer>(
"owner_member_initializer");
249 if (OwnerAssignment) {
250 diag(OwnerAssignment->getBeginLoc(),
251 "expected assignment source to be of type 'gsl::owner<>'; got %0")
252 << OwnerAssignment->getRHS()->getType()
253 << OwnerAssignment->getSourceRange();
258 if (OwnerInitialization) {
259 diag(OwnerInitialization->getBeginLoc(),
260 "expected initialization with value of type 'gsl::owner<>'; got %0")
261 << OwnerInitialization->getAnyInitializer()->getType()
262 << OwnerInitialization->getSourceRange();
267 if (OwnerInitializer) {
268 diag(OwnerInitializer->getSourceLocation(),
269 "expected initialization of owner member variable with value of type "
270 "'gsl::owner<>'; got %0")
273 << OwnerInitializer->getInit()->getType()
274 << OwnerInitializer->getSourceRange();
282bool OwningMemoryCheck::handleAssignmentFromNewOwner(
const BoundNodes &Nodes) {
283 const auto *BadOwnerAssignment =
284 Nodes.getNodeAs<BinaryOperator>(
"bad_owner_creation_assignment");
285 const auto *BadOwnerInitialization =
286 Nodes.getNodeAs<VarDecl>(
"bad_owner_creation_variable");
288 const auto *BadOwnerArgument =
289 Nodes.getNodeAs<Expr>(
"bad_owner_creation_argument");
290 const auto *BadOwnerParameter =
291 Nodes.getNodeAs<ParmVarDecl>(
"bad_owner_creation_parameter");
294 if (BadOwnerAssignment) {
295 diag(BadOwnerAssignment->getBeginLoc(),
296 "assigning newly created 'gsl::owner<>' to non-owner %0")
297 << BadOwnerAssignment->getLHS()->getType()
298 << BadOwnerAssignment->getSourceRange();
303 if (BadOwnerInitialization) {
304 diag(BadOwnerInitialization->getBeginLoc(),
305 "initializing non-owner %0 with a newly created 'gsl::owner<>'")
306 << BadOwnerInitialization->getType()
307 << BadOwnerInitialization->getSourceRange();
316 if (BadOwnerArgument) {
317 assert(BadOwnerParameter &&
318 "parameter for the problematic argument not found");
319 diag(BadOwnerArgument->getBeginLoc(),
"initializing non-owner argument of "
320 "type %0 with a newly created "
322 << BadOwnerParameter->getType() << BadOwnerArgument->getSourceRange();
328bool OwningMemoryCheck::handleReturnValues(
const BoundNodes &Nodes) {
331 const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>(
"bad_owner_return");
332 const auto *
Function = Nodes.getNodeAs<FunctionDecl>(
"function_decl");
338 diag(BadReturnType->getBeginLoc(),
339 "returning a newly created resource of "
340 "type %0 or 'gsl::owner<>' from a "
341 "function whose return type is not 'gsl::owner<>'")
342 <<
Function->getReturnType() << BadReturnType->getSourceRange();
350bool OwningMemoryCheck::handleOwnerMembers(
const BoundNodes &Nodes) {
353 const auto *BadClass = Nodes.getNodeAs<CXXRecordDecl>(
"non_destructor_class");
357 const auto *DeclaredOwnerMember =
358 Nodes.getNodeAs<FieldDecl>(
"undestructed_owner_member");
359 assert(DeclaredOwnerMember &&
360 "match on class with bad destructor but without a declared owner");
362 diag(DeclaredOwnerMember->getBeginLoc(),
363 "member variable of type 'gsl::owner<>' requires the class %0 to "
364 "implement a destructor to release the owned resource")
const FunctionDecl * Decl
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...
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap