Skip to content

Commit b8bda9d

Browse files
authored
[clang][analyzer] Correctly handle lambda-converted function pointers (#144906)
For lambdas that are converted to C function pointers, ``` int (*ret_zero)() = []() { return 0; }; ``` clang will generate conversion method like: ``` CXXConversionDecl implicit used constexpr operator int (*)() 'int (*() const noexcept)()' inline -CompoundStmt -ReturnStmt -ImplicitCastExpr 'int (*)()' <FunctionToPointerDecay> -DeclRefExpr 'int ()' lvalue CXXMethod 0x5ddb6fe35b18 '__invoke' 'int ()' -CXXMethodDecl implicit used __invoke 'int ()' static inline -CompoundStmt (empty) ``` Based on comment in Sema, `__invoke`'s function body is left empty because it's will be filled in CodeGen, so in AST analysis phase we should get lambda's `operator()` directly instead of calling `__invoke` itself.
1 parent 022e1e9 commit b8bda9d

File tree

3 files changed

+35
-0
lines changed

3 files changed

+35
-0
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,8 @@ class SimpleFunctionCall : public AnyFunctionCall {
554554

555555
const FunctionDecl *getDecl() const override;
556556

557+
RuntimeDefinition getRuntimeDefinition() const override;
558+
557559
unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); }
558560

559561
const Expr *getArgExpr(unsigned Index) const override {

clang/lib/StaticAnalyzer/Core/CallEvent.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,18 @@ const FunctionDecl *SimpleFunctionCall::getDecl() const {
688688
return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
689689
}
690690

691+
RuntimeDefinition SimpleFunctionCall::getRuntimeDefinition() const {
692+
// Clang converts lambdas to function pointers using an implicit conversion
693+
// operator, which returns the lambda's '__invoke' method. However, Sema
694+
// leaves the body of '__invoke' empty (it is generated later in CodeGen), so
695+
// we need to skip '__invoke' and access the lambda's operator() directly.
696+
if (const auto *CMD = dyn_cast_if_present<CXXMethodDecl>(getDecl());
697+
CMD && CMD->isLambdaStaticInvoker())
698+
return RuntimeDefinition{CMD->getParent()->getLambdaCallOperator()};
699+
700+
return AnyFunctionCall::getRuntimeDefinition();
701+
}
702+
691703
const FunctionDecl *CXXInstanceCall::getDecl() const {
692704
const auto *CE = cast_or_null<CallExpr>(getOriginExpr());
693705
if (!CE)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
2+
3+
void clang_analyzer_eval(bool);
4+
5+
void basic() {
6+
int (*ret_zero)() = []() { return 0; };
7+
clang_analyzer_eval(ret_zero() == 0); // expected-warning{{TRUE}}
8+
}
9+
10+
void withParam() {
11+
int (*add_ten)(int) = [](int b) { return b + 10; };
12+
clang_analyzer_eval(add_ten(1) == 11); // expected-warning{{TRUE}}
13+
}
14+
15+
int callBack(int (*fp)(int), int x) {
16+
return fp(x);
17+
}
18+
19+
void passWithFunc() {
20+
clang_analyzer_eval(callBack([](int x) { return x; }, 5) == 5); // expected-warning{{TRUE}}
21+
}

0 commit comments

Comments
 (0)