This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Improve symbol qualification in DefineInline code action
ClosedPublic

Authored by kadircet on Oct 16 2019, 6:34 AM.

Details

Summary

Currently define inline action fully qualifies any names in the
function body, which is not optimal and definitely natural.

This patch tries to improve the situation by dropping any name
specifiers shared by function and names spelled in the body. For example
if you are moving definition of a function in namespace clang::clangd,
and body has any decl's coming from clang or clang::clangd namespace,
those qualifications won't be added since they are redundant.

It also drops any qualifiers that are visible in target context.

Diff Detail

Event Timeline

kadircet created this revision.Oct 16 2019, 6:34 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 16 2019, 6:34 AM

We seem to have trouble only in presence of using declarations and using namespace directives.
I wonder how complicated it would be to take them into account instead. That would clearly be easier to read, as we'll hit right into the center of the problem.

Could you describe why handling using declarations and using namespace directives looks too complicated?

clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
141

Could you add an example to the description?

144

Maybe accept a NamedDecl we need to qualify instead of SourceContext?
This would help with avoiding confusion with two parameters of the same type and I believe we'll need it if we ever want to support using declarations in the same function.

160

It might be possible to have non-namespace contexts here:

namespace ns {
struct X {
  void foo();

  static void target_struct();
};
void target_ns();
}


void ns::X::foo() {
  // we start with non-namespace context.
  target_struct();
  target_ns(); 
}

Not sure if we run in those cases, though

clang-tools-extra/clangd/unittests/TweakTests.cpp
1682

We don't support using namespace and yet we choose to use them in the tests here.
Is there a case where we need to qualify without using namespace directive and using declarations?

kuhnel added a subscriber: kuhnel.Oct 16 2019, 9:10 AM

Build failed during patching:

00:03:00.433 [Phabricator] $ arc patch --diff 225207 --nocommit --nobranch --conduit-uri=https://reviews.llvm.org ********
00:03:01.736  INFO  Base commit is not in local repository; trying to fetch.
00:03:02.694 
00:03:02.694 
00:03:02.695     This diff is against commit 48ec5c8b81c23061eac4fe6f3a14e2e876b8d265, but
00:03:02.695     the commit is nowhere in the working copy. Try to apply it against the
00:03:02.695     current working copy state? (fdccf28697e5debe861247d218cbbecf9fd4323e)
00:03:02.695     [Y/n]  Exception 
00:03:02.695 The program is attempting to read user input, but stdin is being piped from some other source (not a TTY).
00:03:02.695 (Run with `--trace` for a full exception trace.)
kadircet edited the summary of this revision. (Show Details)Oct 17 2019, 6:08 AM
kadircet marked 5 inline comments as done.Oct 17 2019, 6:10 AM

We seem to have trouble only in presence of using declarations and using namespace directives.
I wonder how complicated it would be to take them into account instead. That would clearly be easier to read, as we'll hit right into the center of the problem.

Could you describe why handling using declarations and using namespace directives looks too complicated?

As discussed offline, changed the patch to handle using directives. Using declarations are handled implicitly, as we bail out if they are not visible from target
location.

clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
160

the SourceContext is guaranteed to be a namespace context to be start with, since we only call this function in qualifyAllDecls after making sure current decl is a namespace decl.
So there is no way for any of its parents to be anything but a namespacedecl.

clang-tools-extra/clangd/unittests/TweakTests.cpp
1682

We don't support using namespace and yet we choose to use them in the tests here.

I believe you misunderstood the "doesn't take using directives into account" part. It is not that we don't support them, it is just the getQualification function generates suboptimal specifiers in the presence of using directives/declarations. For example:

namespace ns1{
 namespace ns2 { void foo(); }
 using namespace ns2;
 void bar();

 void bar() {
     foo();
 }
}

when we issue an inline on function bar the body will become ns2::foo instead of just foo because we didn't take using namespace ns2 into account.

Is there a case where we need to qualify without using namespace directive and using declarations?

if there are no using directive/declarations then the visible scope of declaration and definition should hopefully be the same and we wouldn't need to qualify anything.
But as I mentioned, it is not that we are not supporting those, we are just not producing

kadircet updated this revision to Diff 225416.Oct 17 2019, 6:10 AM
kadircet marked an inline comment as done.
  • Handle using directives
kadircet updated this revision to Diff 227040.Oct 30 2019, 1:13 AM
  • Rebase
  • Move qualification logic into AST.h so that it can be used by define outline, see D69033.
ilya-biryukov added inline comments.Nov 5 2019, 4:31 AM
clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
195

Why do we need to change this?
My understanding is that we want semantic decl context, not the lexical one here.

199

NIT: it looks like this FIXME belongs to the getQualification function itself.

clang-tools-extra/clangd/unittests/TweakTests.cpp
1674

NIT: could you indent functions inside the namespaces?

kadircet updated this revision to Diff 230213.Nov 20 2019, 1:52 AM
kadircet marked 4 inline comments as done.
  • Address comments
  • Rebase
  • Get rid of string literals inside macros
clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
195

oopsy, was experimenting.

199

I don't think so, as qualification function has no idea about the function body, right?

ilya-biryukov added inline comments.Nov 21 2019, 2:55 AM
clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
199

But it does handle using namespace directives and namespace aliases, so if we ever add this logic it better live in the same place, i.e. inside getQualification function or inside another overload handling that case.

ilya-biryukov added inline comments.Nov 21 2019, 3:02 AM
clang-tools-extra/clangd/unittests/TweakTests.cpp
1670

NIT: capitalize the last word: DropCommonNameSpecifiers

ilya-biryukov accepted this revision.Nov 21 2019, 3:04 AM
ilya-biryukov added inline comments.
clang-tools-extra/clangd/unittests/TweakTests.cpp
1805

NIT: move the FIXME to the test itself, it's a little hard to find

This revision is now accepted and ready to land.Nov 21 2019, 3:04 AM
kadircet updated this revision to Diff 230424.Nov 21 2019, 4:40 AM
kadircet marked 4 inline comments as done.
  • Address comments
kuhnel removed a subscriber: kuhnel.Nov 21 2019, 5:01 AM
This revision was automatically updated to reflect the committed changes.