[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.
This commit is contained in:
flovent 2025-06-25 22:15:01 +08:00 committed by GitHub
parent 022e1e99f3
commit b8bda9d446
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 35 additions and 0 deletions

View File

@ -554,6 +554,8 @@ public:
const FunctionDecl *getDecl() const override;
RuntimeDefinition getRuntimeDefinition() const override;
unsigned getNumArgs() const override { return getOriginExpr()->getNumArgs(); }
const Expr *getArgExpr(unsigned Index) const override {

View File

@ -688,6 +688,18 @@ const FunctionDecl *SimpleFunctionCall::getDecl() const {
return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
}
RuntimeDefinition SimpleFunctionCall::getRuntimeDefinition() const {
// Clang converts lambdas to function pointers using an implicit conversion
// operator, which returns the lambda's '__invoke' method. However, Sema
// leaves the body of '__invoke' empty (it is generated later in CodeGen), so
// we need to skip '__invoke' and access the lambda's operator() directly.
if (const auto *CMD = dyn_cast_if_present<CXXMethodDecl>(getDecl());
CMD && CMD->isLambdaStaticInvoker())
return RuntimeDefinition{CMD->getParent()->getLambdaCallOperator()};
return AnyFunctionCall::getRuntimeDefinition();
}
const FunctionDecl *CXXInstanceCall::getDecl() const {
const auto *CE = cast_or_null<CallExpr>(getOriginExpr());
if (!CE)

View File

@ -0,0 +1,21 @@
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-lambdas=true -verify %s
void clang_analyzer_eval(bool);
void basic() {
int (*ret_zero)() = []() { return 0; };
clang_analyzer_eval(ret_zero() == 0); // expected-warning{{TRUE}}
}
void withParam() {
int (*add_ten)(int) = [](int b) { return b + 10; };
clang_analyzer_eval(add_ten(1) == 11); // expected-warning{{TRUE}}
}
int callBack(int (*fp)(int), int x) {
return fp(x);
}
void passWithFunc() {
clang_analyzer_eval(callBack([](int x) { return x; }, 5) == 5); // expected-warning{{TRUE}}
}