This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Add RemoveUsingNamespace tweak.
ClosedPublic

Authored by usaxena95 on Oct 7 2019, 2:49 AM.

Details

Summary

Removes the 'using namespace' under the cursor and qualifies all accesses in the current file.
E.g.:

using namespace std;
vector<int> foo(std::map<int, int>);

Would become:

std::vector<int> foo(std::map<int, int>);

Diff Detail

Event Timeline

usaxena95 created this revision.Oct 7 2019, 2:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 7 2019, 2:49 AM

The main comment is about limiting this only to global namespace. PTAL.

clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
80

continue rest of the file in the anonymous namespace

111

I think I had this FIXME in some of the versions, but TBF I don't remember why I added it.
Maybe remove?

147

Qualifying identifiers and removing using namespaces might actually conflict.
Could you add a (commented-out) test with an example that fails and a FIXME?

namespace a::b { struct foo {}; }
using namespace a::b;
using namespace b; // we'll try to both qualify this 'b' and remove this line.
clang-tools-extra/clangd/unittests/TweakTests.cpp
783

Could we disallow the action outside global namespace for now to make sure it's always correct.

#include <vector>

using namespace std;

is a very common pattern anyway, supporting only it in the first version looks ok.

usaxena95 updated this revision to Diff 223592.Oct 7 2019, 7:17 AM
usaxena95 marked 3 inline comments as done.

Make the tweak trigger only for TopLevelDecl.

ilya-biryukov added inline comments.Oct 7 2019, 8:18 AM
clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
87

NIT: remove redundant {} around this return

100

I believe this check is redundant in presence of isTopLevelDecl()...
(It used to check the 'using namespace' is not inside a function body)

122

Could you take a look at handling these?

Also happy if we make it a separate change, but we shouldn't delay this.
Both are important cases.

The examples should roughly be:

namespace std {
inline namespace v1 {
  struct vector {};
}
}

using namespace std;
vector v;

and

namespace tokens {
enum Token {
  comma, identifier, numeric
};
}

using namespace tokens;
auto x = comma;
164

NIT: could you rephrase as re-qualify names instead
"add qualifiers" confuses some people, this can be read as "add a const qualifier"

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

Argh, this should definitely be fixed :-(
One simple way to handle this particular only qualify references, which come after the first using namespace we are removing.

There's a SourceManager::isBeforeInTranslationUnit function that can be used to find out whether something is written before or after a particular point.

This won't fix all of the cases, but fixing in the general case seems hard.

Bulid results are available at http://results.llvm-merge-guard.org/Phabricator-19

See http://jenkins.llvm-merge-guard.org/job/Phabricator/19/ for more details.

usaxena95 updated this revision to Diff 223898.Oct 8 2019, 10:28 AM
usaxena95 marked 8 inline comments as done.

Addressed comments.

usaxena95 added inline comments.Oct 8 2019, 10:28 AM
clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
100

Aah. Makes sense.

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

We also need to qualify the map in such cases.
I have made an attempt to fix this by traversing all the parents namespace decl and checking whether they are equal to the concerned namespace.
But this introduces other problems:

namespace aa { namespace bb { struct map {}; }}
using namespace aa::bb;
using namespace a^a;
int main() {
  map m;
}

get changed to

namespace aa { namespace bb { struct map {}; }}
using namespace aa::bb;

int main() {
  aa::map m;
}

Here aa::map is invalid.

ilya-biryukov added inline comments.Oct 9 2019, 3:11 AM
clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
87

Can we use ASTNode.get<TranslationUnitDecl>() to directly to check for this?

Not sure how DynTypedNode works, though, maybe that's impossible.

100

Hm, I'd expect us to simply go up until the first non-trasparent namespace and stop at it.
Consider the following example:

namespace a { namespace b {
  struct Foo {};
}}

using namespace a; // <-- remove this
using namespace b;

Foo x; // <-- Foo does not need to be qualified, as it's inside b.

OTOH, in case of inline namespace we'd choose to qualify:

namespace a { inline namespace b {
  struct Foo {};
}}

using namespace a; // <-- remove this

Foo x; // <-- need to change to a::Foo

Obviously, if we had using namespace a::b we'd not want to qualify, but that's complicated. And it's very rare to have using namespace for an inline namespace.

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

This is incorrect, right? We should not be qualifying here.
See the relevant comment on isInsideNamespace

usaxena95 updated this revision to Diff 224038.Oct 9 2019, 6:19 AM
usaxena95 marked 5 inline comments as done.

Make action unavailable if the namespace contains a using namespace decl.

clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
87

Works for the test.
The doc for get<T> says that it returns NULL if the stored node does not have a type that is convertible to T.

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

Works fine now.

Many thanks, this LG overall, just a few NITs and documentation requests.

clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
32

Could you mention current limitation in the comment?
Something like

Currently limited to using namespace directives inside global namespace to simplify implementation.
86

NIT: could be written as a single statement

return Node->Parent
    && Node->Parent->....

up to you, though, both versions look fine.

114

Could you provide the rationale why we do this too?
This information would be super-useful to folks fixing this later

151

Worth documenting why we do this here:

Avoid adding qualifiers before macro expansions, it's probably incorrect, e.g.
    #define FOO 1 + matrix()
    using namespace linalg; // provides matrix
    auto x = FOO; 
 
Should not be changed to:
    auto x = linalg::FOO;
158

NIT: could you add a comment on why we do this? Something like

Directive was not visible before this point.
usaxena95 updated this revision to Diff 224067.Oct 9 2019, 8:47 AM
usaxena95 marked 5 inline comments as done.

Added documentation.

ilya-biryukov accepted this revision.Oct 10 2019, 10:53 AM

LGTM, many thanks!

A few last NITs too

clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
32

NIT: use three slashes

33

NIT: s/a using directives/using directives

92

NIT: end the sentence with a period

92

I believe this function is more general, e.g. it returns the parent class for class members.
I suggest renaming to visibleContext and removing mentions of returning a namespace in the doc comment.

100

NIT: remove braces, LLVM code style suggests to avoid them in simple single-statement loops

117

NIT:s/namepsace/namespace

138

NIT: the comment looks redundant, the variable name is very descriptive. Maybe remove?

140

I'm not 100% certain this is considered to be the end location, as there macros, etc.
Could we instead start with an invalid source location

163

NIT: could you indent the example?
To make it clearly distinguishable from the rest of the comment

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

What happens if we remove this one?
Will it still qualify as a::b or as b::?

Could we add a test just to document the behavior?

This revision is now accepted and ready to land.Oct 10 2019, 10:53 AM
usaxena95 updated this revision to Diff 225183.Oct 16 2019, 2:43 AM
usaxena95 marked 11 inline comments as done.

Addressed comments.

usaxena95 added inline comments.Oct 16 2019, 2:47 AM
clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
140

Replaced it with uninitialized (which is invalid) SourceLocation. Makes it simpler.

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

It qualifies as b::. Added a test for it.

usaxena95 marked an inline comment as done.Oct 16 2019, 2:48 AM
This revision was automatically updated to reflect the committed changes.