This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Add parameter renaming to define-inline code action
ClosedPublic

Authored by kadircet on Oct 14 2019, 3:03 AM.

Details

Summary

When moving a function definition to declaration location we also need
to handle renaming of the both function and template parameters.

This patch achives that by making sure every parameter name and dependent type
in destination is renamed to their respective name in the source.

Diff Detail

Event Timeline

kadircet created this revision.Oct 14 2019, 3:03 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 14 2019, 3:03 AM

An alternative approach I'm thinking of:
After D68977 lands, we could try using findExplicitReferences to produce all of these edits:

  1. we collect locations of all references and declaration of relevant parameters and template parameters.
  2. find the ones where the name differs and produce the changes.

That would handle not only references in the default argument, but also all references in the body and should be less code overall.
IMO it's worth exploring this approach right away, e.g. you could layer your patch on top of the current version of D68977

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

We only need name from the Source. Why not accept it as a StringRef Name directly?
Would allow to call this function even if Source is not a NamedDecl

kadircet updated this revision to Diff 225209.Oct 16 2019, 6:42 AM
kadircet marked an inline comment as done.
  • Accept StringRef for Source in rewriteParameterName instead of a NamedDecl

An alternative approach I'm thinking of:
After D68977 lands, we could try using findExplicitReferences to produce all of these edits:

  1. we collect locations of all references and declaration of relevant parameters and template parameters.
  2. find the ones where the name differs and produce the changes.

That would handle not only references in the default argument, but also all references in the body and should be less code overall.
IMO it's worth exploring this approach right away, e.g. you could layer your patch on top of the current version of D68977

I totally agree that the solution you proposed would also work, but don't think it would be any less code. Since one needs to correlate
parameters between two different declarations, and findExplicitReferences doesn't really provide a nice way to achieve that. One
would have to rely on SourceLocation ordering or in the order callback was issued(which implicitly relies on AST traversal order).

It would be nice to unify the rewrite parameter name/type/defaultarg logics, but not sure if it is worth.

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

Build failed as patch failed to apply:

00:00:06.098 Checking patch clang-tools-extra/trunk/clangd/FindTarget.h...
00:00:06.100 error: clang-tools-extra/trunk/clangd/FindTarget.h: does not exist in index
00:00:06.100 Checking patch clang-tools-extra/trunk/clangd/FindTarget.cpp...
00:00:06.100 error: clang-tools-extra/trunk/clangd/FindTarget.cpp: does not exist in index
00:00:06.101 
00:00:06.101  Patch Failed! 
00:00:06.101 Usage Exception: Unable to apply patch!

I totally agree that the solution you proposed would also work, but don't think it would be any less code. Since one needs to correlate
parameters between two different declarations, and findExplicitReferences doesn't really provide a nice way to achieve that. One
would have to rely on SourceLocation ordering or in the order callback was issued(which implicitly relies on AST traversal order).

It would be nice to unify the rewrite parameter name/type/defaultarg logics, but not sure if it is worth.

I believe it should be less and much simpler code. In particular, I propose something like this:

DenseSet<const NameDecl*> ParametersToRename;
// Fill ParametersToRename with template and function parameters.

DenseMap<const NamedDecl*, std::vector<SourceLocation>> References;
findExplicitReferences(Func, [&](ReferenceLoc R) {
  if (!ParametersToRename.count(R.Target))
    return;
  References[R.Target].push_back(R.NameLoc);
});

for (auto [Param, Locations] : References) {
  auto NewName = NewNameForParam(Param);
  for (auto L : Locations)
    Replacements.add(replaceToken(L, NewName));
}

That should also handle various interesting cases like try-catch blocks and constructor initializer lists.

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

NIT: that renames \p Dest to \p SourceName

232

NIT: call it NewName?
Source and Target are very define-inline-specific. This function actually does a more general thing and using the define-inline terminology hurt the redability a little.

239

Why do we need to insert a space here? could you add an example?
I guess it's

void foo(int^) // <-- avoid gluing 'int' and the new name here
241

NIT: could you move this expression and a comment to a separate variable?
should simplify the if condition and improve readability

Ah, we're actually trying to rename parameters in the declaration to match the ones in the definition... So the try-catch blocks are not a problem, actually

kadircet updated this revision to Diff 226517.Oct 25 2019, 4:00 PM
kadircet marked 4 inline comments as done.
  • Use findExplicitReferences for decl traversals.

Build result: fail - 59631 tests passed, 1 failed and 805 were skipped.

failed: LLVM.tools/llvm-ar/mri-utf8.test

Log files: cmake-log.txt, ninja_check_all-log.txt, CMakeCache.txt

kadircet updated this revision to Diff 226619.Oct 28 2019, 1:12 AM
  • Don't rename if parameters have the same name

Build result: pass - 59695 tests passed, 0 failed and 763 were skipped.
Log files: cmake-log.txt, ninja_check_all-log.txt, CMakeCache.txt

ilya-biryukov marked an inline comment as done.Oct 28 2019, 6:05 AM
ilya-biryukov added inline comments.
clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
278

why not llvm::cast? We'd rather fail early (during the cast) than postpone it until we dereference Target a few lines further

285

Could we measure the token length instead?
There are various bizzare cases when the source text is different and we definitely don't want to accidentally crash on those, e.g.

int a = N\
A\
ME;
295

Wow, TIL I learned something new. Thanks.

kadircet updated this revision to Diff 226655.Oct 28 2019, 6:37 AM
kadircet marked 2 inline comments as done.
  • Address comments

D69511 broke the anonymous parameters. Sorry about that, I hope that's for the best in the long run :-)
We'll need some code to update this patch. Other than that, I think this patch is good to go!

ilya-biryukov added inline comments.Oct 28 2019, 6:55 AM
clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
233

NIT: instead of returning whether Dest is a template, we could instead:

  1. accept a TemplateDecl* Dest and TemplateDecl *Source,
  2. check whether functions are template at the call site and only call fillTemplateParameterMapping when they're template.

I believe that would make both functions simpler and more straight-forward. But up to you.

Build result: fail - 59657 tests passed, 2 failed and 805 were skipped.

failed: libc++.libcxx/thread/thread_threads/thread_thread_this/sleep_for.pass.cpp
failed: LLVM.tools/llvm-ar/mri-utf8.test

Log files: cmake-log.txt, ninja_check_all-log.txt, CMakeCache.txt

kadircet updated this revision to Diff 226849.Oct 29 2019, 1:06 AM
  • Handle unnamed parameters explicitly
kadircet marked an inline comment as done.Oct 29 2019, 1:07 AM

Build result: pass - 59704 tests passed, 0 failed and 762 were skipped.
Log files: console-log.txt, CMakeCache.txt

Just a few final NITs from my side.
And would also be nice to get tests with macros.

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

NIT: maybe use assignment syntax?
NewName += SourceParam->getName()

270

NIT: also add assert(Source->param_size() == Dest->param_size())?

275

s/signautre/signature

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

Could you add tests with some parameter references inside macro expansions?
To make sure we don't crash and don't produce invalid edits? Failing is obviously ok

kadircet updated this revision to Diff 226868.Oct 29 2019, 3:44 AM
kadircet marked 3 inline comments as done.
  • Handle macros in parameter names.
ilya-biryukov added inline comments.Oct 29 2019, 3:52 AM
clang-tools-extra/clangd/unittests/TweakTests.cpp
1526

We fail to rename the parameter in that case, right?
Should the action not be available or maybe show an error message?

Also ok with keeping the current behavior if we add a FIXME mentioning it would be nice to fix this.

Build result: pass - 59704 tests passed, 0 failed and 762 were skipped.
Log files: console-log.txt, CMakeCache.txt

kadircet updated this revision to Diff 226909.Oct 29 2019, 8:29 AM
kadircet marked 2 inline comments as done.
  • Use Lexer::makeFileCharRange
kadircet added inline comments.Oct 29 2019, 8:30 AM
clang-tools-extra/clangd/unittests/TweakTests.cpp
1526

somehow forgot my stashed changes ...

yes this was supposed to bail out on failure, but leave the macro occurrence if parameter names are the same.

Build result: pass - 59704 tests passed, 0 failed and 762 were skipped.
Log files: console-log.txt, CMakeCache.txt

kadircet updated this revision to Diff 227035.Oct 30 2019, 12:10 AM
  • Add comments
  • Early return when generating edits fails

Build result: pass - 59704 tests passed, 0 failed and 762 were skipped.
Log files: console-log.txt, CMakeCache.txt

ilya-biryukov accepted this revision.Oct 30 2019, 11:17 AM

LGTM, many thanks!

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

NIT: Maybe move declaration of Replacements here? To make it closer to the use-site.

This revision is now accepted and ready to land.Oct 30 2019, 11:17 AM
kadircet updated this revision to Diff 227233.Oct 31 2019, 1:21 AM
kadircet marked an inline comment as done.
  • Address comments
  • Get rid of raw string literals inside macro calls, to not break stage1 compilers.
This revision was automatically updated to reflect the committed changes.

Build result: pass - 59704 tests passed, 0 failed and 762 were skipped.
Log files: console-log.txt, CMakeCache.txt

thakis added inline comments.Oct 31 2019, 4:51 AM
clang-tools-extra/clangd/unittests/TweakTests.cpp
1388

You might need something like this in some of the new tests.

Tests are happy again after 1c66d09 , thanks!