]> granicus.if.org Git - clang/commitdiff
Teach CallGraph to look into Generic Lambdas.
authorErich Keane <erich.keane@intel.com>
Mon, 30 Sep 2019 19:12:29 +0000 (19:12 +0000)
committerErich Keane <erich.keane@intel.com>
Mon, 30 Sep 2019 19:12:29 +0000 (19:12 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373247 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclCXX.h
include/clang/AST/ExprCXX.h
lib/AST/DeclCXX.cpp
lib/AST/ExprCXX.cpp
lib/Analysis/CallGraph.cpp
test/Analysis/debug-CallGraph.cpp

index 0b835ecd736809294ad49a3d9d871e848a1bdbb0..de0aa5f9b900f60a8a1512239674942e5f37e188 100644 (file)
@@ -1172,6 +1172,10 @@ public:
   /// if this is a closure type.
   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
   /// is returned by the conversion operator, and the body of which
   /// is forwarded to the lambda call operator.
index bc7d7c0a32015dde69259242483b472a2584570d..8e13eb5fdbaa1a5ad135cbdd454be0c391b67107 100644 (file)
@@ -1907,6 +1907,10 @@ public:
   /// lambda expression.
   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
   /// parameter list associated with it, or else return null.
   TemplateParameterList *getTemplateParameterList() const;
index 297a598911db5fbe9d6859150e96152753b2085a..942588f4ede7186998d8cf3ba0279e9ad47f8581 100644 (file)
@@ -1399,17 +1399,25 @@ static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
 }
 #endif
 
-CXXMethodDecl* CXXRecordDecl::getLambdaCallOperator() const {
-  if (!isLambda()) return nullptr;
+NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
+  if (!RD.isLambda()) return nullptr;
   DeclarationName Name =
-    getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
-  DeclContext::lookup_result Calls = lookup(Name);
+    RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
+  DeclContext::lookup_result Calls = RD.lookup(Name);
 
   assert(!Calls.empty() && "Missing lambda call operator!");
   assert(allLookupResultsAreTheSame(Calls) &&
          "More than one lambda call operator!");
+  return Calls.front();
+}
+
+FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {
+  NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
+  return  dyn_cast<FunctionTemplateDecl>(CallOp);
+}
 
-  NamedDecl *CallOp = Calls.front();
+CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
+  NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
   if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
     return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
 
index 30c28314d5b2f1f004b543e4c418febaaca737f3..1d5fd80d0d4700988e6081a8c94bb3cee4fd8817 100644 (file)
@@ -1218,6 +1218,11 @@ CXXMethodDecl *LambdaExpr::getCallOperator() const {
   return Record->getLambdaCallOperator();
 }
 
+FunctionTemplateDecl *LambdaExpr::getDependentCallOperator() const {
+  CXXRecordDecl *Record = getLambdaClass();
+  return Record->getDependentLambdaCallOperator();
+}
+
 TemplateParameterList *LambdaExpr::getTemplateParameterList() const {
   CXXRecordDecl *Record = getLambdaClass();
   return Record->getGenericLambdaTemplateParameterList();
index 865840eb341a23f1be9f5ae3a519c4b23f3024c5..76be292dad8d2820ceaa5de8bc021059f7265fc7 100644 (file)
@@ -80,7 +80,10 @@ public:
   }
 
   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);
   }
 
index 1d6844fad94fc069b249637e5e36816de4b3f713..0f5a83b268a018f0c51f9bc74bdb8c3ed429b381 100644 (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() {
   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-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::templ calls: eee $}}
 // CHECK-NEXT: {{Function: SomeNS::templ calls: ccc $}}