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:
Erich Keane 2019-09-30 19:12:29 +00:00
parent 3405237f77
commit 5c2c60d2fc
6 changed files with 49 additions and 8 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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());

View File

@ -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();

View File

@ -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);
} }

View File

@ -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 $}}