forked from OSchip/llvm-project
Teach CallGraph to look into Generic Lambdas.
CallGraph visited LambdaExpr by getting the Call Operator from CXXRecordDecl (LambdaExpr::getCallOperator calls CXXRecordDecl::getLambdaCallOperator), which replaced generic lambda call operators with the non-instantiated FunctionDecl. The result was that the CallGraph would only pick up non-dependent calls. This patch does a few things: 1- Extend CXXRecordDecl to have a getDependentLambdaCallOperator, which will get the FunctionTemplateDecl, rather than immediately getting the TemplateDecl. 2- Define getLambdaCallOperator and getDependentLambdaCallOperator in terms of a common function. 3- Extend LambdaExpr with a getDependentCallOperator, which just calls the above function. 4- Changes CallGraph to handle Generic LambdaExprs. llvm-svn: 373247
This commit is contained in:
parent
3405237f77
commit
5c2c60d2fc
|
@ -1172,6 +1172,10 @@ public:
|
||||||
/// if this is a closure type.
|
/// if this is a closure type.
|
||||||
CXXMethodDecl *getLambdaCallOperator() const;
|
CXXMethodDecl *getLambdaCallOperator() const;
|
||||||
|
|
||||||
|
/// Retrieve the dependent lambda call operator of the closure type
|
||||||
|
/// if this is a templated closure type.
|
||||||
|
FunctionTemplateDecl *getDependentLambdaCallOperator() const;
|
||||||
|
|
||||||
/// Retrieve the lambda static invoker, the address of which
|
/// Retrieve the lambda static invoker, the address of which
|
||||||
/// is returned by the conversion operator, and the body of which
|
/// is returned by the conversion operator, and the body of which
|
||||||
/// is forwarded to the lambda call operator.
|
/// is forwarded to the lambda call operator.
|
||||||
|
|
|
@ -1907,6 +1907,10 @@ public:
|
||||||
/// lambda expression.
|
/// lambda expression.
|
||||||
CXXMethodDecl *getCallOperator() const;
|
CXXMethodDecl *getCallOperator() const;
|
||||||
|
|
||||||
|
/// Retrieve the function template call operator associated with this
|
||||||
|
/// lambda expression.
|
||||||
|
FunctionTemplateDecl *getDependentCallOperator() const;
|
||||||
|
|
||||||
/// If this is a generic lambda expression, retrieve the template
|
/// If this is a generic lambda expression, retrieve the template
|
||||||
/// parameter list associated with it, or else return null.
|
/// parameter list associated with it, or else return null.
|
||||||
TemplateParameterList *getTemplateParameterList() const;
|
TemplateParameterList *getTemplateParameterList() const;
|
||||||
|
|
|
@ -1399,17 +1399,25 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CXXMethodDecl* CXXRecordDecl::getLambdaCallOperator() const {
|
NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
|
||||||
if (!isLambda()) return nullptr;
|
if (!RD.isLambda()) return nullptr;
|
||||||
DeclarationName Name =
|
DeclarationName Name =
|
||||||
getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
|
RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
|
||||||
DeclContext::lookup_result Calls = lookup(Name);
|
DeclContext::lookup_result Calls = RD.lookup(Name);
|
||||||
|
|
||||||
assert(!Calls.empty() && "Missing lambda call operator!");
|
assert(!Calls.empty() && "Missing lambda call operator!");
|
||||||
assert(allLookupResultsAreTheSame(Calls) &&
|
assert(allLookupResultsAreTheSame(Calls) &&
|
||||||
"More than one lambda call operator!");
|
"More than one lambda call operator!");
|
||||||
|
return Calls.front();
|
||||||
|
}
|
||||||
|
|
||||||
NamedDecl *CallOp = Calls.front();
|
FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {
|
||||||
|
NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
|
||||||
|
return dyn_cast<FunctionTemplateDecl>(CallOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
|
||||||
|
NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
|
||||||
if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
|
if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
|
||||||
return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
|
return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
|
||||||
|
|
||||||
|
|
|
@ -1218,6 +1218,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const {
|
||||||
return Record->getLambdaCallOperator();
|
return Record->getLambdaCallOperator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionTemplateDecl *LambdaExpr::getDependentCallOperator() const {
|
||||||
|
CXXRecordDecl *Record = getLambdaClass();
|
||||||
|
return Record->getDependentLambdaCallOperator();
|
||||||
|
}
|
||||||
|
|
||||||
TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
|
TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
|
||||||
CXXRecordDecl *Record = getLambdaClass();
|
CXXRecordDecl *Record = getLambdaClass();
|
||||||
return Record->getGenericLambdaTemplateParameterList();
|
return Record->getGenericLambdaTemplateParameterList();
|
||||||
|
|
|
@ -80,7 +80,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisitLambdaExpr(LambdaExpr *LE) {
|
void VisitLambdaExpr(LambdaExpr *LE) {
|
||||||
if (CXXMethodDecl *MD = LE->getCallOperator())
|
if (FunctionTemplateDecl *FTD = LE->getDependentCallOperator())
|
||||||
|
for (FunctionDecl *FD : FTD->specializations())
|
||||||
|
G->VisitFunctionDecl(FD);
|
||||||
|
else if (CXXMethodDecl *MD = LE->getCallOperator())
|
||||||
G->VisitFunctionDecl(MD);
|
G->VisitFunctionDecl(MD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCallGraph %s -fblocks 2>&1 | FileCheck %s
|
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCallGraph %s -fblocks -std=c++14 2>&1 | FileCheck %s
|
||||||
|
|
||||||
int get5() {
|
int get5() {
|
||||||
return 5;
|
return 5;
|
||||||
|
@ -68,8 +68,25 @@ void templUser() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Lambdas {
|
||||||
|
void Callee(){}
|
||||||
|
|
||||||
|
void f1() {
|
||||||
|
[](int i) {
|
||||||
|
Callee();
|
||||||
|
}(1);
|
||||||
|
[](auto i) {
|
||||||
|
Callee();
|
||||||
|
}(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CHECK:--- Call graph Dump ---
|
// CHECK:--- Call graph Dump ---
|
||||||
// CHECK-NEXT: {{Function: < root > calls: get5 add test_add mmm foo aaa < > bbb ddd ccc eee fff do_nothing test_single_call SomeNS::templ SomeNS::templ SomeNS::templUser $}}
|
// CHECK-NEXT: {{Function: < root > calls: get5 add test_add mmm foo aaa < > bbb ddd ccc eee fff do_nothing test_single_call SomeNS::templ SomeNS::templ SomeNS::templUser Lambdas::Callee Lambdas::f1 Lambdas::f1\(\)::\(anonymous class\)::operator\(\) Lambdas::f1\(\)::\(anonymous class\)::operator\(\) $}}
|
||||||
|
// CHECK-NEXT: {{Function: Lambdas::f1 calls: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) Lambdas::f1\(\)::\(anonymous class\)::operator\(\) $}}
|
||||||
|
// CHECK-NEXT: {{Function: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) calls: Lambdas::Callee $}}
|
||||||
|
// CHECK-NEXT: {{Function: Lambdas::f1\(\)::\(anonymous class\)::operator\(\) calls: Lambdas::Callee $}}
|
||||||
|
// CHECK-NEXT: {{Function: Lambdas::Callee calls: $}}
|
||||||
// CHECK-NEXT: {{Function: SomeNS::templUser calls: SomeNS::templ SomeNS::templ $}}
|
// CHECK-NEXT: {{Function: SomeNS::templUser calls: SomeNS::templ SomeNS::templ $}}
|
||||||
// CHECK-NEXT: {{Function: SomeNS::templ calls: eee $}}
|
// CHECK-NEXT: {{Function: SomeNS::templ calls: eee $}}
|
||||||
// CHECK-NEXT: {{Function: SomeNS::templ calls: ccc $}}
|
// CHECK-NEXT: {{Function: SomeNS::templ calls: ccc $}}
|
||||||
|
|
Loading…
Reference in New Issue