[PATCH] D124690: [clangd] add inlay hints for std::forward-ed parameter packs

Sam McCall via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu May 5 04:00:43 PDT 2022


sammccall added inline comments.


================
Comment at: clang-tools-extra/clangd/unittests/InlayHintTests.cpp:255
+  )cpp",
+                       ExpectedHint{"a: ", "param"});
+}
----------------
upsj wrote:
> sammccall wrote:
> > upsj wrote:
> > > I haven't been able to figure out why, but for some reason the `CXXConstructExpr` and `CXXTemporaryObjectExpr` are not being picked up by the `RecursiveASTVisitor`, while in a similar situation below, the `CallExpr` gets picked up by `VisitCallExpr`.
> > The AST here of the instantiated bar looks like
> > ```
> > -FunctionDecl <line:5:1, line:7:1> line:5:4 used bar 'S *(int &&)'
> >   |-TemplateArgument type 'S'
> >   |-TemplateArgument pack
> >   | `-TemplateArgument type 'int'
> >   |-ParmVarDecl <col:8, col:18> col:18 used args 'int &&'
> >   `-CompoundStmt <col:24, line:7:1>
> >     `-ReturnStmt <line:6:3, col:23>
> >       `-CXXNewExpr <col:10, col:23> 'S *' Function 'operator new' 'void *(unsigned long)'
> >         `-CXXConstructExpr <col:14, col:23> 'S':'S' 'void (int)' list
> >           `-ImplicitCastExpr <col:16> 'int':'int' <LValueToRValue>
> >             `-DeclRefExpr <col:16> 'int':'int' lvalue ParmVar 'args' 'int &&'
> > ```
> > 
> > So there's no CXXTemporaryObjectExpr (the value here is a pointer), but should be a CXXConstructExpr. 
> > 
> > Are you sure you're traversing bar's instantiation/specializaion, as opposed to its templated/generic FunctionDecl? The AST of the templated FunctionDecl indeed has an InitListExpr in place of the CXXConstructExpr because it's not resolved yet.
> It's quite possible this is related to the visitor not/only inconsistently traversing template instantiations due to shouldVisitTemplateInstantiations returning false. But I need to implement the new approach first, anyways.
Ah, I think I see the confusion.

We don't want to gather all the information in a single traversal of the whole AST. Such a traversal would need to walk all instantiated templates including those from headers, which is far too much/too slow.

Instead, you want to analyze particular forwarding functions by walking through their bodies looking for the forwarding call. RecursiveASTVisitor is the tool for this job - you just call TraverseDecl(FunctionDecl) to limit the scope. The visitor should not visit template instantiations, but the traversal can still be rooted at an instantiation!

So something roughly like:

```
Optional<vector<ParmVarDecl*>> ultimateParams(FunctionDecl* Callee) {
  if (!Callee || !isa<FunctionProtoType>(Callee->getType()))
    return None;
  if (not instantiated from template || Pattern doesn't use a pack)
    return Callee->parameters();

  for (P : Pattern->parameters()) {
    if (!P->isPack())
      Result.push_back(Callee->parameters[Pos++]);
    else {
      if (PackExpansionSize = ...) {
        if (ForwardedParams = forwardedParams(Callee, P))
          Result.append(*ForwardedParams);
        else // failed, no param info for forwarded args
          Result.append(nullptr, PackExpansionSize);
        Pos += PackExpansionSize;
      }
    }
  }
}

Optional<vector<ParmVarDecl*>> forwardedParams(FunctionDecl *Callee, ParmVarDecl *Pack) {
  class ForwardFinder : RAV<ForwardFinder> {
    bool VisitCallExpr(CallExpr *E) {
      if (E->params() expands Pack) {
        if (CallParams = ultimateParams(E->getCallee()->getAsFunctionDecl()))
          Result = CallParams.slice(section corresponding to Pack);
      }
    }
  };
  ForwardFinder Finder;
  if (Callee->hasBody())
    return Finder.VisitCallExpr(Callee);  
}
```

you could also memoize with a map, but I suspect simple recursion is going to be good enough


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D124690



More information about the cfe-commits mailing list