Skip to content

Commit 2fc31e9

Browse files
PiotrZSLchencha3
authored andcommitted
[clang-tidy] Add support for lambdas in cppcoreguidelines-owning-memory (llvm#77246)
Implement proper support for lambdas and sub-functions/classes. Moved from https://reviews.llvm.org/D157285 Fixes: llvm#59389
1 parent 6cabe7a commit 2fc31e9

File tree

3 files changed

+169
-7
lines changed

3 files changed

+169
-7
lines changed

clang-tools-extra/clang-tidy/cppcoreguidelines/OwningMemoryCheck.cpp

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ using namespace clang::ast_matchers::internal;
1919

2020
namespace clang::tidy::cppcoreguidelines {
2121

22+
namespace {
23+
AST_MATCHER_P(LambdaExpr, hasCallOperator, Matcher<CXXMethodDecl>,
24+
InnerMatcher) {
25+
return InnerMatcher.matches(*Node.getCallOperator(), Finder, Builder);
26+
}
27+
28+
AST_MATCHER_P(LambdaExpr, hasLambdaBody, Matcher<Stmt>, InnerMatcher) {
29+
return InnerMatcher.matches(*Node.getBody(), Finder, Builder);
30+
}
31+
} // namespace
32+
2233
void OwningMemoryCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
2334
Options.store(Opts, "LegacyResourceProducers", LegacyResourceProducers);
2435
Options.store(Opts, "LegacyResourceConsumers", LegacyResourceConsumers);
@@ -55,6 +66,8 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
5566
CreatesLegacyOwner, LegacyOwnerCast);
5667

5768
const auto ConsideredOwner = eachOf(IsOwnerType, CreatesOwner);
69+
const auto ScopeDeclaration = anyOf(translationUnitDecl(), namespaceDecl(),
70+
recordDecl(), functionDecl());
5871

5972
// Find delete expressions that delete non-owners.
6073
Finder->addMatcher(
@@ -144,13 +157,51 @@ void OwningMemoryCheck::registerMatchers(MatchFinder *Finder) {
144157
.bind("bad_owner_creation_parameter"))),
145158
this);
146159

160+
auto IsNotInSubLambda = stmt(
161+
hasAncestor(
162+
stmt(anyOf(equalsBoundNode("body"), lambdaExpr())).bind("scope")),
163+
hasAncestor(stmt(equalsBoundNode("scope"), equalsBoundNode("body"))));
164+
147165
// Matching on functions, that return an owner/resource, but don't declare
148166
// their return type as owner.
149167
Finder->addMatcher(
150-
functionDecl(hasDescendant(returnStmt(hasReturnValue(ConsideredOwner))
151-
.bind("bad_owner_return")),
152-
unless(returns(qualType(hasDeclaration(OwnerDecl)))))
153-
.bind("function_decl"),
168+
functionDecl(
169+
decl().bind("function_decl"),
170+
hasBody(
171+
stmt(stmt().bind("body"),
172+
hasDescendant(
173+
returnStmt(hasReturnValue(ConsideredOwner),
174+
// Ignore sub-lambda expressions
175+
IsNotInSubLambda,
176+
// Ignore sub-functions
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"))),
183+
this);
184+
185+
// Matching on lambdas, that return an owner/resource, but don't declare
186+
// their return type as owner.
187+
Finder->addMatcher(
188+
lambdaExpr(
189+
hasAncestor(decl(ScopeDeclaration).bind("scope-decl")),
190+
hasLambdaBody(
191+
stmt(stmt().bind("body"),
192+
hasDescendant(
193+
returnStmt(
194+
hasReturnValue(ConsideredOwner),
195+
// Ignore sub-lambdas
196+
IsNotInSubLambda,
197+
// Ignore sub-functions
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"))))
204+
.bind("lambda"),
154205
this);
155206

156207
// Match on classes that have an owner as member, but don't declare a
@@ -329,7 +380,7 @@ bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
329380
// Function return statements, that are owners/resources, but the function
330381
// declaration does not declare its return value as owner.
331382
const auto *BadReturnType = Nodes.getNodeAs<ReturnStmt>("bad_owner_return");
332-
const auto *Function = Nodes.getNodeAs<FunctionDecl>("function_decl");
383+
const auto *ResultType = Nodes.getNodeAs<QualType>("result");
333384

334385
// Function return values, that should be owners but aren't.
335386
if (BadReturnType) {
@@ -338,8 +389,9 @@ bool OwningMemoryCheck::handleReturnValues(const BoundNodes &Nodes) {
338389
diag(BadReturnType->getBeginLoc(),
339390
"returning a newly created resource of "
340391
"type %0 or 'gsl::owner<>' from a "
341-
"function whose return type is not 'gsl::owner<>'")
342-
<< Function->getReturnType() << BadReturnType->getSourceRange();
392+
"%select{function|lambda}1 whose return type is not 'gsl::owner<>'")
393+
<< *ResultType << (Nodes.getNodeAs<Expr>("lambda") != nullptr)
394+
<< BadReturnType->getSourceRange();
343395

344396
// FIXME: Rewrite the return type as 'gsl::owner<OriginalType>'
345397
return true;

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ Changes in existing checks
165165
giving false positives for deleted functions and fix false negative when some
166166
parameters are forwarded, but other aren't.
167167

168+
- Improved :doc:`cppcoreguidelines-owning-memory
169+
<clang-tidy/checks/cppcoreguidelines/owning-memory>` check to properly handle
170+
return type in lambdas and in nested functions.
171+
168172
- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer
169173
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>`
170174
by removing enforcement of rule `C.48

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,109 @@ namespace PR63994 {
395395
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'A *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
396396
}
397397
}
398+
399+
namespace PR59389 {
400+
struct S {
401+
S();
402+
S(int);
403+
404+
int value = 1;
405+
};
406+
407+
void testLambdaInFunctionNegative() {
408+
const auto MakeS = []() -> ::gsl::owner<S*> {
409+
return ::gsl::owner<S*>{new S{}};
410+
};
411+
}
412+
413+
void testLambdaInFunctionPositive() {
414+
const auto MakeS = []() -> S* {
415+
return ::gsl::owner<S*>{new S{}};
416+
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
417+
};
418+
}
419+
420+
void testFunctionInFunctionNegative() {
421+
struct C {
422+
::gsl::owner<S*> test() {
423+
return ::gsl::owner<S*>{new S{}};
424+
}
425+
};
426+
}
427+
428+
void testFunctionInFunctionPositive() {
429+
struct C {
430+
S* test() {
431+
return ::gsl::owner<S*>{new S{}};
432+
// CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
433+
}
434+
};
435+
}
436+
437+
::gsl::owner<S*> testReverseLambdaNegative() {
438+
const auto MakeI = [] -> int { return 5; };
439+
return ::gsl::owner<S*>{new S(MakeI())};
440+
}
441+
442+
S* testReverseLambdaPositive() {
443+
const auto MakeI = [] -> int { return 5; };
444+
return ::gsl::owner<S*>{new S(MakeI())};
445+
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
446+
}
447+
448+
::gsl::owner<S*> testReverseFunctionNegative() {
449+
struct C {
450+
int test() { return 5; }
451+
};
452+
return ::gsl::owner<S*>{new S(C().test())};
453+
}
454+
455+
S* testReverseFunctionPositive() {
456+
struct C {
457+
int test() { return 5; }
458+
};
459+
return ::gsl::owner<S*>{new S(C().test())};
460+
// CHECK-NOTES: [[@LINE-1]]:5: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a function whose return type is not 'gsl::owner<>'
461+
}
462+
463+
void testLambdaInLambdaNegative() {
464+
const auto MakeS = []() -> ::gsl::owner<S*> {
465+
const auto MakeI = []() -> int { return 5; };
466+
return ::gsl::owner<S*>{new S(MakeI())};
467+
};
468+
}
469+
470+
void testLambdaInLambdaPositive() {
471+
const auto MakeS = []() -> S* {
472+
const auto MakeI = []() -> int { return 5; };
473+
return ::gsl::owner<S*>{new S(MakeI())};
474+
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
475+
};
476+
}
477+
478+
void testLambdaInLambdaWithDoubleReturns() {
479+
const auto MakeS = []() -> S* {
480+
const auto MakeS2 = []() -> S* {
481+
return ::gsl::owner<S*>{new S(1)};
482+
// CHECK-NOTES: [[@LINE-1]]:9: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>' [cppcoreguidelines-owning-memory]
483+
};
484+
return ::gsl::owner<S*>{new S(2)};
485+
// CHECK-NOTES: [[@LINE-1]]:7: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
486+
};
487+
}
488+
489+
void testReverseLambdaInLambdaNegative() {
490+
const auto MakeI = []() -> int {
491+
const auto MakeS = []() -> ::gsl::owner<S*> { return new S(); };
492+
return 5;
493+
};
494+
}
495+
496+
void testReverseLambdaInLambdaPositive() {
497+
const auto MakeI = []() -> int {
498+
const auto MakeS = []() -> S* { return new S(); };
499+
// CHECK-NOTES: [[@LINE-1]]:39: warning: returning a newly created resource of type 'S *' or 'gsl::owner<>' from a lambda whose return type is not 'gsl::owner<>'
500+
return 5;
501+
};
502+
}
503+
}

0 commit comments

Comments
 (0)