This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Heuristically resolve dependent method calls
ClosedPublic

Authored by nridge on Dec 9 2019, 8:53 PM.

Details

Summary

The heuristic is to look in the definition of the primary template,
which is what you want in the vast majority of cases.

Fixes https://github.com/clangd/clangd/issues/141

Event Timeline

nridge created this revision.Dec 9 2019, 8:53 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 9 2019, 8:53 PM
nridge updated this revision to Diff 232996.Dec 9 2019, 8:56 PM

Add github issue number

nridge edited the summary of this revision. (Show Details)Dec 9 2019, 8:56 PM
Harbormaster completed remote builds in B42177: Diff 232996.
nridge updated this revision to Diff 232998.Dec 9 2019, 8:59 PM

Fix typo

sammccall accepted this revision.Dec 10 2019, 3:11 AM

Awesome, thanks, I'd been hoping to get around to this! LG with some naming/structure nits.
This doesn't handle references into smart pointers (details below), but feel free to do that in this patch, separately, or not at all.

clang-tools-extra/clangd/FindTarget.cpp
115–116

while here, I think UnresolvedUsingValueDecl and UnresolvedUsingTypenameDecl can be added here :-)

253

If I'm understanding the AST correctly, this drops the operator-> case, where the base is a smart-pointer.

e.g. void run(unique_ptr<Action<T>> X) { X->go(); }

There are potentially two reasons then that x->go is a *dependent* memberexpr:

  • operator-> probably returns T*, but unique_ptr might be specialized (you don't handle this)
  • we probably want the primary Action<T>::go, but Action might be specialized (you do handle this)

So it might be worth trying to get the pointee type (if the base type is a TemplateSpecializationType, look up operator-> in the primary template, and replace the base with its return type).
If you don't want to do this yet, we should leave a comment (like FIXME: handle unique_ptr<T> by looking up operator-> in the primary template)

298

In the long term, I'm not sure this comment will be as useful as one saying something like:
"Dependent name references like vector<T>::size() can't be definitively resolved, but the using the definition from the primary template is probably better than giving up"

321

can we make this function return the decls, and make the callers call add() in a loop?

That way this function could be reused for the operator-> lookup, and could be a standalone helper function outside this class. (Maybe eventually exposed in AST.h, as I can imagine it being useful in other places, but static in this file seems fine)

321

with multiple lookup results we could add all, discard all, or pick one arbitrarily.

I'm not sure what the best policy is, but it's worth pointing out in a comment what the cases are and what the choice is.

I think this should only affects overloads, where returning all seems reasonable (resolution would be way too much work).

Can you add a test?

clang-tools-extra/clangd/unittests/XRefsTests.cpp
470

nit: dependent method (just so it's easier to search for)

513

could you add a test for the operator-> case too, even if we don't handle it yet? (in which case with a FIXME)

Something like:

template <typename T> struct unique_ptr<T> { T* operator->(); };
template <typename T> struct S { void [[foo]](); }
template <typename T> void test(unique_ptr<S<T>>& V) { V->f^oo(); }
This revision is now accepted and ready to land.Dec 10 2019, 3:11 AM
nridge added a comment.EditedDec 12 2019, 1:45 PM

unique_ptr is harder to get to work than it might appear at first blush :)

Looking at the gcc 6 libstdc++ implementation as an example: the declared return type of operator-> is not T*, but unique_ptr<T>::pointer. The declaration of that is in turn:

typedef typename _Pointer::type pointer;

The definition of _Pointer is in turn:

// use SFINAE to determine whether _Del::pointer exists
class _Pointer
{
  template<typename _Up>
  static typename _Up::pointer __test(typename _Up::pointer*);

  template<typename _Up>
  static _Tp* __test(...);

  typedef typename remove_reference<_Dp>::type _Del;

public:
  typedef decltype(__test<_Del>(0)) type;
};

Writing heuristics that see through all that might be possible, and I'm happy to explore it, but definitely in a follow-up.

nridge updated this revision to Diff 233680.Dec 12 2019, 2:13 PM
nridge marked 7 inline comments as done.

Address review comments

nridge updated this revision to Diff 233681.Dec 12 2019, 2:15 PM
nridge marked an inline comment as done.

Fix a typo and address one remaining comment

nridge added inline comments.Dec 12 2019, 2:15 PM
clang-tools-extra/clangd/FindTarget.cpp
321

Note, the additions to LocateSymbol.Ambiguous provide test coverage for this.

This revision was automatically updated to reflect the committed changes.
thakis added a subscriber: thakis.Dec 12 2019, 4:29 PM

Tests fail on Windows: http://45.33.8.238/win/3976/step_9.txt

Probably needs the usual -fno-delayed-template-parsing workaround, see other clangd tests.

Probably needs the usual -fno-delayed-template-parsing workaround, see other clangd tests.

Thanks for the suggestion! Fix at https://reviews.llvm.org/D71444

Probably needs the usual -fno-delayed-template-parsing workaround, see other clangd tests.

Thanks for the suggestion! Fix at https://reviews.llvm.org/D71444

Committed in https://reviews.llvm.org/rG4f732a3d49ac.