upsj updated this revision to Diff 434257.
upsj added a comment.

This is now ready to review, only needed to fix a formatting issue


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D124690/new/

https://reviews.llvm.org/D124690

Files:
  clang-tools-extra/clangd/AST.cpp
  clang-tools-extra/clangd/AST.h
  clang-tools-extra/clangd/InlayHints.cpp
  clang-tools-extra/clangd/unittests/InlayHintTests.cpp

Index: clang-tools-extra/clangd/unittests/InlayHintTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/InlayHintTests.cpp
+++ clang-tools-extra/clangd/unittests/InlayHintTests.cpp
@@ -174,6 +174,44 @@
   )cpp");
 }
 
+TEST(ParameterHints, NoNameVariadicDeclaration) {
+  // No hint for anonymous variadic parameter
+  assertParameterHints(R"cpp(
+    template <typename... Args>
+    void foo(Args&& ...);
+    void bar() {
+      foo(42);
+    }
+  )cpp");
+}
+
+TEST(ParameterHints, NoNameVariadicForwarded) {
+  // No hint for anonymous variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    void foo(int);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(std::forward<Args>($fwd[[args]])...); }
+    void baz() {
+      bar(42);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd"});
+}
+
+TEST(ParameterHints, NoNameVariadicPlain) {
+  // No hint for anonymous variadic parameter
+  assertParameterHints(R"cpp(
+    void foo(int);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(args...); }
+    void baz() {
+      bar(42);
+    }
+  )cpp");
+}
+
 TEST(ParameterHints, NameInDefinition) {
   // Parameter name picked up from definition if necessary.
   assertParameterHints(R"cpp(
@@ -186,6 +224,19 @@
                        ExpectedHint{"param: ", "param"});
 }
 
+TEST(ParameterHints, NamePartiallyInDefinition) {
+  // Parameter name picked up from definition if necessary.
+  assertParameterHints(R"cpp(
+    void foo(int, int b);
+    void bar() {
+      foo($param1[[42]], $param2[[42]]);
+    }
+    void foo(int a, int) {};
+  )cpp",
+                       ExpectedHint{"a: ", "param1"},
+                       ExpectedHint{"b: ", "param2"});
+}
+
 TEST(ParameterHints, NameMismatch) {
   // Prefer name from declaration.
   assertParameterHints(R"cpp(
@@ -258,6 +309,276 @@
                        ExpectedHint{"param: ", "param"});
 }
 
+TEST(ParameterHints, VariadicForwardedConstructor) {
+  // Name hint for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    struct S { S(int a); };
+    template <typename T, typename... Args>
+    T bar(Args&&... args) { return T{std::forward<Args>($fwd[[args]])...}; }
+    void baz() {
+      int b;
+      bar<S>($param[[b]]);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd"},
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicPlainConstructor) {
+  // Name hint for variadic parameter
+  assertParameterHints(R"cpp(
+    struct S { S(int a); };
+    template <typename T, typename... Args>
+    T bar(Args&&... args) { return T{args...}; }
+    void baz() {
+      int b;
+      bar<S>($param[[b]]);
+    }
+  )cpp",
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicForwardedNewConstructor) {
+  // Name hint for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    struct S { S(int a); };
+    template <typename T, typename... Args>
+    T* bar(Args&&... args) { return new T{std::forward<Args>($fwd[[args]])...}; }
+    void baz() {
+      int b;
+      bar<S>($param[[b]]);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd"},
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicPlainNewConstructor) {
+  // Name hint for variadic parameter
+  assertParameterHints(R"cpp(
+    struct S { S(int a); };
+    template <typename T, typename... Args>
+    T* bar(Args&&... args) { return new T{args...}; }
+    void baz() {
+      int b;
+      bar<S>($param[[b]]);
+    }
+  )cpp",
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicForwarded) {
+  // Name for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    void foo(int a);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(std::forward<Args>($fwd[[args]])...); }
+    void baz() {
+      int b;
+      bar($param[[b]]);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd"},
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicPlain) {
+  // Name hint for variadic parameter
+  assertParameterHints(R"cpp(
+    void foo(int a);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(args...); }
+    void baz() {
+      bar($param[[42]]);
+    }
+  )cpp",
+                       ExpectedHint{"a: ", "param"});
+}
+
+TEST(ParameterHints, VariadicSplitTwolevel) {
+  // Name for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    void baz(int b);
+    template <typename... Args>
+    void foo(int a, Args&&... args) {
+      return baz(std::forward<Args>($fwd1[[args]])...);
+    }
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(std::forward<Args>($fwd2[[args]])...); }
+    void bazz() {
+      bar($param1[[32]], $param2[[42]]);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd1"}, ExpectedHint{"&: ", "fwd2"},
+                       ExpectedHint{"a: ", "param1"},
+                       ExpectedHint{"b: ", "param2"});
+}
+
+TEST(ParameterHints, VariadicOverloaded) {
+  // Name for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(
+      R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    void baz(int b, int c);
+    void baz(int bb, int cc, int dd);
+    template <typename... Args>
+    void foo(int a, Args&&... args) {
+      return baz(std::forward<Args>($fwd1[[args]])...);
+    }
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(std::forward<Args>($fwd2[[args]])...); }
+    void bazz() {
+      bar($param1[[32]], $param2[[42]], $param3[[52]]);
+      bar($param4[[1]], $param5[[2]], $param6[[3]], $param7[[4]]);
+    }
+  )cpp",
+      ExpectedHint{"&: ", "fwd1"}, ExpectedHint{"&: ", "fwd2"},
+      ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"},
+      ExpectedHint{"c: ", "param3"}, ExpectedHint{"a: ", "param4"},
+      ExpectedHint{"bb: ", "param5"}, ExpectedHint{"cc: ", "param6"},
+      ExpectedHint{"dd: ", "param7"});
+}
+
+TEST(ParameterHints, VariadicRecursive) {
+  assertParameterHints(
+      R"cpp(
+    void foo();
+
+    template <typename Head, typename... Tail>
+    void foo(Head head, Tail... tail) {
+      foo(tail...);
+    }
+
+    int main() {
+      foo(1, 2, 3);
+    }
+  )cpp");
+}
+
+TEST(ParameterHints, VariadicTwoCalls) {
+  assertParameterHints(
+      R"cpp(
+    void f1(int a, int b);
+    void f2(int c, int d);
+
+    bool cond;
+
+    template <typename... Args>
+    void foo(Args... args) {
+      if (cond) {
+        f1(args...);
+      } else {
+        f2(args...);
+      }
+    }
+
+    int main() {
+      foo($param1[[1]], $param2[[2]]);
+    }
+  )cpp",
+      ExpectedHint{"a: ", "param1"}, ExpectedHint{"b: ", "param2"});
+}
+
+TEST(ParameterHints, VariadicInfinite) {
+  assertParameterHints(
+      R"cpp(
+    template <typename... Args>
+    void foo(Args...);
+
+    template <typename... Args>
+    void bar(Args... args) {
+      foo(args...);
+    }
+
+    template <typename... Args>
+    void foo(Args... args) {
+      bar(args...);
+    }
+
+    int main() {
+      foo(1, 2);
+    }
+  )cpp");
+}
+
+TEST(ParameterHints, VariadicEmplace) {
+  // Name for variadic parameter
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(
+      R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    using size_t = decltype(sizeof(0));
+    void *operator new(size_t, void *);
+    struct S {
+      S(int A);
+      S(int B, int C);
+    };
+    struct alloc {
+      template <typename T>
+      T* allocate();
+      template <typename T, typename... Args>
+      void construct(T* ptr, Args&&... args) {
+        ::new ((void*)ptr) T{std::forward<Args>($fwd1[[args]])...};
+      }
+    };
+    template <typename T>
+    struct container {
+      template <typename... Args>
+      void emplace(Args&&... args) {
+        alloc a;
+        auto ptr = a.template allocate<T>();
+        a.construct(ptr, std::forward<Args>($fwd2[[args]])...);
+      }
+    };
+    void foo() {
+      container<S> c;
+      c.emplace($param1[[1]]);
+      c.emplace($param2[[2]], $param3[[3]]);
+    }
+  )cpp",
+      ExpectedHint{"&: ", "fwd1"}, ExpectedHint{"&: ", "fwd2"},
+      ExpectedHint{"A: ", "param1"}, ExpectedHint{"B: ", "param2"},
+      ExpectedHint{"C: ", "param3"});
+}
+
+TEST(ParameterHints, MatchingNameVariadicForwarded) {
+  // No name hint for variadic parameter with matching name
+  // This prototype of std::forward is sufficient for clang to recognize it
+  assertParameterHints(R"cpp(
+    namespace std { template <typename T> T&& forward(T&); }
+    void foo(int a);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(std::forward<Args>($fwd[[args]])...); }
+    void baz() {
+      int a;
+      bar(a);
+    }
+  )cpp",
+                       ExpectedHint{"&: ", "fwd"});
+}
+
+TEST(ParameterHints, MatchingNameVariadicPlain) {
+  // No name hint for variadic parameter with matching name
+  assertParameterHints(R"cpp(
+    void foo(int a);
+    template <typename... Args>
+    void bar(Args&&... args) { return foo(args...); }
+    void baz() {
+      int a;
+      bar(a);
+    }
+  )cpp");
+}
+
 TEST(ParameterHints, Operator) {
   // No hint for operator call with operator syntax.
   assertParameterHints(R"cpp(
Index: clang-tools-extra/clangd/InlayHints.cpp
===================================================================
--- clang-tools-extra/clangd/InlayHints.cpp
+++ clang-tools-extra/clangd/InlayHints.cpp
@@ -378,7 +378,7 @@
   // assume that if this location does not come from a macro definition, then
   // the entire argument list likely appears in the main file and can be hinted.
   void processCall(SourceLocation Anchor, const FunctionDecl *Callee,
-                   llvm::ArrayRef<const Expr *const> Args) {
+                   llvm::ArrayRef<const Expr *> Args) {
     if (!Cfg.InlayHints.Parameters || Args.size() == 0 || !Callee)
       return;
 
@@ -392,19 +392,20 @@
       if (Ctor->isCopyOrMoveConstructor())
         return;
 
-    // Don't show hints for variadic parameters.
-    size_t FixedParamCount = getFixedParamCount(Callee);
-    size_t ArgCount = std::min(FixedParamCount, Args.size());
-    auto Params = Callee->parameters();
+    // Resolve parameter packs to their forwarded parameter
+    auto Params = resolveForwardingParameters(Callee);
+    // We are only interested in expanded arguments with corresponding
+    // parameters.
+    size_t ParamHintCount = std::min(Params.size(), getExpandedArgCount(Args));
 
-    NameVec ParameterNames = chooseParameterNames(Callee, ArgCount);
+    NameVec ParameterNames = chooseParameterNames(Params);
 
     // Exclude setters (i.e. functions with one argument whose name begins with
     // "set"), as their parameter name is also not likely to be interesting.
     if (isSetter(Callee, ParameterNames))
       return;
 
-    for (size_t I = 0; I < ArgCount; ++I) {
+    for (size_t I = 0; I < ParamHintCount; ++I) {
       StringRef Name = ParameterNames[I];
       bool NameHint = shouldHintName(Args[I], Name);
       bool ReferenceHint = shouldHintReference(Params[I]);
@@ -507,29 +508,48 @@
     return {};
   }
 
-  NameVec chooseParameterNames(const FunctionDecl *Callee, size_t ArgCount) {
-    // The current strategy here is to use all the parameter names from the
-    // canonical declaration, unless they're all empty, in which case we
-    // use all the parameter names from the definition (in present in the
-    // translation unit).
-    // We could try a bit harder, e.g.:
-    //   - try all re-declarations, not just canonical + definition
-    //   - fall back arg-by-arg rather than wholesale
-
-    NameVec ParameterNames = getParameterNamesForDecl(Callee, ArgCount);
-
-    if (llvm::all_of(ParameterNames, std::mem_fn(&StringRef::empty))) {
-      if (const FunctionDecl *Def = Callee->getDefinition()) {
-        ParameterNames = getParameterNamesForDecl(Def, ArgCount);
+  NameVec chooseParameterNames(SmallVector<const ParmVarDecl *> Parameters) {
+    NameVec ParameterNames;
+    for (const auto *P : Parameters) {
+      if (isExpandedParameterPack(P)) {
+        ParameterNames.emplace_back();
+      } else {
+        auto SimpleName = getSimpleName(*P);
+        // If the parameter is unnamed in the declaration:
+        // attempt to get its name from the definition
+        if (SimpleName.empty()) {
+          if (auto *Callee = dyn_cast<FunctionDecl>(P->getDeclContext())) {
+            if (auto *Def = Callee->getDefinition()) {
+              auto I = std::distance(
+                  Callee->param_begin(),
+                  std::find(Callee->param_begin(), Callee->param_end(), P));
+              if (I < Callee->getNumParams()) {
+                SimpleName = getSimpleName(*Def->getParamDecl(I));
+              }
+            }
+          }
+        }
+        ParameterNames.emplace_back(SimpleName);
       }
     }
-    assert(ParameterNames.size() == ArgCount);
 
     // Standard library functions often have parameter names that start
     // with underscores, which makes the hints noisy, so strip them out.
     for (auto &Name : ParameterNames)
       stripLeadingUnderscores(Name);
 
+    // Remove parameter names that occur multiple times completely.
+    llvm::StringMap<size_t> NameLastSeen;
+    for (size_t I = 0; I < ParameterNames.size(); I++) {
+      auto Name = ParameterNames[I];
+      auto It = NameLastSeen.find(Name);
+      if (It != NameLastSeen.end()) {
+        ParameterNames[It->second] = {};
+        ParameterNames[I] = {};
+      }
+      NameLastSeen.insert_or_assign(Name, I);
+    }
+
     return ParameterNames;
   }
 
@@ -537,24 +557,13 @@
     Name = Name.ltrim('_');
   }
 
-  // Return the number of fixed parameters Function has, that is, not counting
-  // parameters that are variadic (instantiated from a parameter pack) or
-  // C-style varargs.
-  static size_t getFixedParamCount(const FunctionDecl *Function) {
-    if (FunctionTemplateDecl *Template = Function->getPrimaryTemplate()) {
-      FunctionDecl *F = Template->getTemplatedDecl();
-      size_t Result = 0;
-      for (ParmVarDecl *Parm : F->parameters()) {
-        if (Parm->isParameterPack()) {
-          break;
-        }
-        ++Result;
-      }
-      return Result;
-    }
-    // C-style varargs don't need special handling, they're already
-    // not included in getNumParams().
-    return Function->getNumParams();
+  // Return the length of the prefix of arguments that are not unexpanded pack
+  // expansion expressions.
+  static size_t getExpandedArgCount(llvm::ArrayRef<const Expr *> Args) {
+    return std::distance(
+        Args.begin(), std::find_if(Args.begin(), Args.end(), [](const Expr *E) {
+          return dyn_cast<PackExpansionExpr>(E) != nullptr;
+        }));
   }
 
   static StringRef getSimpleName(const NamedDecl &D) {
@@ -565,17 +574,6 @@
     return StringRef();
   }
 
-  NameVec getParameterNamesForDecl(const FunctionDecl *Function,
-                                   size_t ArgCount) {
-    NameVec Result;
-    for (size_t I = 0; I < ArgCount; ++I) {
-      const ParmVarDecl *Parm = Function->getParamDecl(I);
-      assert(Parm);
-      Result.emplace_back(getSimpleName(*Parm));
-    }
-    return Result;
-  }
-
   // We pass HintSide rather than SourceLocation because we want to ensure
   // it is in the same file as the common file range.
   void addInlayHint(SourceRange R, HintSide Side, InlayHintKind Kind,
Index: clang-tools-extra/clangd/AST.h
===================================================================
--- clang-tools-extra/clangd/AST.h
+++ clang-tools-extra/clangd/AST.h
@@ -16,6 +16,7 @@
 #include "index/SymbolID.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/Basic/SourceLocation.h"
@@ -199,6 +200,16 @@
 /// reasonable.
 bool isDeeplyNested(const Decl *D, unsigned MaxDepth = 10);
 
+/// Recursively resolves the parameters of a FunctionDecl that forwards its
+/// parameters to another function via variadic template parameters. This can
+/// for example be used to retrieve the constructor parameter ParmVarDecl for a
+/// make_unique or emplace_back call.
+llvm::SmallVector<const ParmVarDecl *>
+resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth = 10);
+
+/// Checks whether D is the expanded form of a parameter pack
+bool isExpandedParameterPack(const ParmVarDecl *D);
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/AST.cpp
===================================================================
--- clang-tools-extra/clangd/AST.cpp
+++ clang-tools-extra/clangd/AST.cpp
@@ -16,17 +16,20 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/TypeLoc.h"
+#include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Index/USRGeneration.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/None.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -665,5 +668,265 @@
   }
   return false;
 }
+
+namespace {
+
+// Returns the template parameter pack type from an instantiated function
+// template, if this is the case, nullptr otherwise.
+const TemplateTypeParmType *
+getPackTemplateParameter(const FunctionDecl *Callee) {
+  if (const auto *TemplateDecl = Callee->getPrimaryTemplate()) {
+    auto TemplateParams = TemplateDecl->getTemplateParameters()->asArray();
+    if (TemplateParams.size() > 0) {
+      if (const auto *TTPD =
+              dyn_cast<TemplateTypeParmDecl>(TemplateParams.back())) {
+        const auto *TTPT =
+            TTPD->getTypeForDecl()->castAs<TemplateTypeParmType>();
+        if (TTPT->isParameterPack()) {
+          return TTPT;
+        }
+      }
+    }
+  }
+  return nullptr;
+}
+
+// Returns the template parameter pack type that this parameter was expanded
+// from, if this is the case, nullptr otherwise.
+const TemplateTypeParmType *getPackTemplateParameter(const ParmVarDecl *Param) {
+  const auto *PlainType = Param->getType().getTypePtr();
+  if (auto *RT = dyn_cast<ReferenceType>(PlainType))
+    PlainType = RT->getPointeeTypeAsWritten().getTypePtr();
+  if (const auto *SubstType = dyn_cast<SubstTemplateTypeParmType>(PlainType)) {
+    const auto *ReplacedParameter = SubstType->getReplacedParameter();
+    if (ReplacedParameter->isParameterPack()) {
+      return dyn_cast<TemplateTypeParmType>(
+          ReplacedParameter->getCanonicalTypeUnqualified()->getTypePtr());
+    }
+  }
+  return nullptr;
+}
+
+class ForwardingCallVisitor
+    : public RecursiveASTVisitor<ForwardingCallVisitor> {
+public:
+  ForwardingCallVisitor(ArrayRef<const ParmVarDecl *> Parameters)
+      : Parameters{Parameters}, PackType{getPackTemplateParameter(
+                                    Parameters.front())} {}
+
+  bool VisitCallExpr(CallExpr *E) {
+    auto *Callee = getCalleeDeclOrUniqueOverload(E);
+    if (Callee) {
+      handleCall(Callee, E->arguments());
+    }
+    return !Info.hasValue();
+  }
+
+  bool VisitCXXConstructExpr(CXXConstructExpr *E) {
+    auto *Callee = E->getConstructor();
+    if (Callee) {
+      handleCall(Callee, E->arguments());
+    }
+    return !Info.hasValue();
+  }
+
+  // The expanded parameter pack to be resolved
+  ArrayRef<const ParmVarDecl *> Parameters;
+  // The type of the parameter pack
+  const TemplateTypeParmType *PackType;
+
+  struct ForwardingInfo {
+    // If the parameters were resolved to another FunctionDecl, these are its
+    // first non-variadic parameters (i.e. the first entries of the parameter
+    // pack that are passed as arguments bound to a non-pack parameter.)
+    ArrayRef<const ParmVarDecl *> Head;
+    // If the parameters were resolved to another FunctionDecl, these are its
+    // variadic parameters (i.e. the entries of the parameter pack that are
+    // passed as arguments bound to a pack parameter.)
+    ArrayRef<const ParmVarDecl *> Pack;
+    // If the parameters were resolved to another FunctionDecl, these are its
+    // last non-variadic parameters (i.e. the last entries of the parameter pack
+    // that are passed as arguments bound to a non-pack parameter.)
+    ArrayRef<const ParmVarDecl *> Tail;
+    // If the parameters were resolved to another forwarding FunctionDecl, this
+    // is it.
+    Optional<FunctionDecl *> PackTarget;
+  };
+
+  // The output of this visitor
+  Optional<ForwardingInfo> Info;
+
+private:
+  // inspects the given callee with the given args to check whether it
+  // contains Parameters, and sets FullyResolved, PartiallyResolved and
+  // NextTarget accordingly.
+  void handleCall(FunctionDecl *Callee, typename CallExpr::arg_range Args) {
+    if (std::any_of(Args.begin(), Args.end(), [](const Expr *E) {
+          return dyn_cast<PackExpansionExpr>(E) != nullptr;
+        })) {
+      return;
+    }
+    auto OptPackLocation = findPack(Args);
+    if (OptPackLocation) {
+      size_t PackLocation = OptPackLocation.getValue();
+      ArrayRef<ParmVarDecl *> MatchingParams =
+          Callee->parameters().slice(PackLocation, Parameters.size());
+      // Check whether the function has a parameter pack as the last template
+      // parameter
+      if (const auto *TTPT = getPackTemplateParameter(Callee)) {
+        // In this case: Separate the parameters into head, pack and tail
+        auto IsExpandedPack = [&](const ParmVarDecl *P) {
+          return getPackTemplateParameter(P) == TTPT;
+        };
+        ForwardingInfo FI;
+        FI.Head = MatchingParams.take_until(IsExpandedPack);
+        FI.Pack = MatchingParams.drop_front(FI.Head.size())
+                      .take_while(IsExpandedPack);
+        FI.Tail = MatchingParams.drop_front(FI.Head.size() + FI.Pack.size());
+        FI.PackTarget = Callee;
+        Info = FI;
+        return;
+      }
+      // Default case: assume all parameters were fully resolved
+      ForwardingInfo FI;
+      FI.Head = MatchingParams;
+      Info = FI;
+    }
+  }
+
+  // Returns the beginning of the expanded pack represented by Parameters
+  // in the given arguments, if it is there.
+  llvm::Optional<size_t> findPack(typename CallExpr::arg_range Args) {
+    // find the argument directly referring to the first parameter
+    auto FirstMatch = std::find_if(Args.begin(), Args.end(), [&](Expr *Arg) {
+      const auto *RefArg = unwrapArgument(Arg);
+      if (RefArg) {
+        if (Parameters.front() == dyn_cast<ParmVarDecl>(RefArg->getDecl())) {
+          return true;
+        }
+      }
+      return false;
+    });
+    if (FirstMatch == Args.end()) {
+      return llvm::None;
+    }
+    return std::distance(Args.begin(), FirstMatch);
+  }
+
+  static FunctionDecl *getCalleeDeclOrUniqueOverload(CallExpr *E) {
+    Decl *CalleeDecl = E->getCalleeDecl();
+    auto *Callee = dyn_cast_or_null<FunctionDecl>(CalleeDecl);
+    if (!Callee) {
+      if (auto *Lookup = dyn_cast<UnresolvedLookupExpr>(E->getCallee())) {
+        Callee = resolveOverload(Lookup, E);
+      }
+    }
+    // Ignore the callee if the number of arguments is wrong (deal with va_args)
+    if (Callee->getNumParams() == E->getNumArgs())
+      return Callee;
+    return nullptr;
+  }
+
+  static FunctionDecl *resolveOverload(UnresolvedLookupExpr *Lookup,
+                                       CallExpr *D) {
+    FunctionDecl *MatchingDecl = nullptr;
+    if (!Lookup->requiresADL()) {
+      // Check whether there is a single overload with this number of
+      // parameters
+      for (auto *Candidate : Lookup->decls()) {
+        if (auto *FuncCandidate = dyn_cast_or_null<FunctionDecl>(Candidate)) {
+          if (FuncCandidate->getNumParams() == D->getNumArgs()) {
+            if (MatchingDecl) {
+              // there are multiple candidates - abort
+              return nullptr;
+            }
+            MatchingDecl = FuncCandidate;
+          }
+        }
+      }
+    }
+    return MatchingDecl;
+  }
+
+  // Removes any implicit cast expressions around the given expression.
+  static const Expr *unwrapImplicitCast(const Expr *E) {
+    while (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
+      E = Cast->getSubExpr();
+    }
+    return E;
+  }
+
+  // Maps std::forward(E) to E, nullptr otherwise
+  static const Expr *unwrapForward(const Expr *E) {
+    if (const auto *Call = dyn_cast<CallExpr>(E)) {
+      const auto Callee = Call->getBuiltinCallee();
+      if (Callee == Builtin::BIforward) {
+        return Call->getArg(0);
+      }
+    }
+    return E;
+  }
+
+  // Maps std::forward(DeclRefExpr) to DeclRefExpr, removing any intermediate
+  // implicit casts, nullptr otherwise
+  static const DeclRefExpr *unwrapArgument(const Expr *E) {
+    E = unwrapImplicitCast(E);
+    E = unwrapForward(E);
+    E = unwrapImplicitCast(E);
+    return dyn_cast<DeclRefExpr>(E);
+  }
+};
+
+} // namespace
+
+SmallVector<const ParmVarDecl *>
+resolveForwardingParameters(const FunctionDecl *D, unsigned MaxDepth) {
+  auto Parameters = D->parameters();
+  // If the function has a template parameter pack
+  if (const auto *TTPT = getPackTemplateParameter(D)) {
+    // Split the parameters into head, pack and tail
+    auto IsExpandedPack = [&](const ParmVarDecl *P) {
+      return getPackTemplateParameter(P) == TTPT;
+    };
+    ArrayRef<const ParmVarDecl *> Head = Parameters.take_until(IsExpandedPack);
+    ArrayRef<const ParmVarDecl *> Pack =
+        Parameters.drop_front(Head.size()).take_while(IsExpandedPack);
+    ArrayRef<const ParmVarDecl *> Tail =
+        Parameters.drop_front(Head.size() + Pack.size());
+    SmallVector<const ParmVarDecl *> Result(Parameters.size());
+    // Fill in non-pack parameters
+    auto HeadIt = std::copy(Head.begin(), Head.end(), Result.begin());
+    auto TailIt = std::copy(Tail.rbegin(), Tail.rbegin(), Result.rbegin());
+    // Recurse on pack parameters
+    size_t Depth = 0;
+    const FunctionDecl *CurrentFunction = D;
+    while (!Pack.empty() && CurrentFunction && Depth < MaxDepth) {
+      // Find call expressions involving the pack
+      ForwardingCallVisitor V{Pack};
+      V.TraverseStmt(CurrentFunction->getBody());
+      if (!V.Info) {
+        break;
+      }
+      // If we found something: Fill in non-pack parameters
+      auto Info = V.Info.getValue();
+      HeadIt = std::copy(Info.Head.begin(), Info.Head.end(), HeadIt);
+      TailIt = std::copy(Info.Tail.rbegin(), Info.Tail.rend(), TailIt);
+      // Prepare next recursion level
+      Pack = Info.Pack;
+      CurrentFunction = Info.PackTarget.getValueOr(nullptr);
+      Depth++;
+    }
+    // Fill in the remaining unresolved pack parameters
+    HeadIt = std::copy(Pack.begin(), Pack.end(), HeadIt);
+    assert(TailIt.base() == HeadIt);
+    return Result;
+  }
+  return {Parameters.begin(), Parameters.end()};
+}
+
+bool isExpandedParameterPack(const ParmVarDecl *D) {
+  return getPackTemplateParameter(D) != nullptr;
+}
+
 } // namespace clangd
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to