v1nh1shungry created this revision.
v1nh1shungry added reviewers: tom-anders, kadircet.
Herald added subscribers: arphaman, mgrang.
Herald added a project: All.
v1nh1shungry requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.
If we are removing the namespace `ns1` and there are user-defined literals
declared in an inline namespace `ns2` declared in `ns1`, we can add a
`using namespace ns1::ns2;` to keep the program correct.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D137817
Files:
clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
Index: clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
+++ clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
@@ -263,6 +263,26 @@
long double operator "" _w(long double);
}
+ int main() { 1.5_w; }
+ )cpp"},
+ {// Add using-directives for user-defined literals
+ // declared in an inline namespace
+ R"cpp(
+ namespace ns1 {
+ inline namespace ns2 {
+ long double operator"" _w(long double);
+ }
+ }
+ using namespace n^s1;
+ int main() { 1.5_w; }
+ )cpp",
+ R"cpp(
+ namespace ns1 {
+ inline namespace ns2 {
+ long double operator"" _w(long double);
+ }
+ }
+ using namespace ns1::ns2;
int main() { 1.5_w; }
)cpp"}};
for (auto C : Cases)
Index: clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
@@ -54,6 +54,10 @@
: TargetNS(Target.getNominatedNamespace()),
TargetCtx(Target.getDeclContext()), Results(Results) {}
+ FindSameUsings(const NamespaceDecl *TargetNS, const DeclContext *TargetCtx,
+ std::vector<const UsingDirectiveDecl *> &Results)
+ : TargetNS(TargetNS), TargetCtx(TargetCtx), Results(Results) {}
+
bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
if (D->getNominatedNamespace() != TargetNS ||
D->getDeclContext() != TargetCtx)
@@ -101,6 +105,40 @@
return D;
}
+// If the user-defined literal is declared in an inline namespace,
+// we add a using-directive for it
+void handleUserDefinedLiteral(const DeclContext *D, const DeclContext *Ctx,
+ ASTContext &AST,
+ std::vector<std::string> &UsingToAdd) {
+ if (D->isInlineNamespace()) {
+ const auto *ND = cast<NamespaceDecl>(D);
+ // Find if there is already a wanted using-directive,
+ // if there is, we are done. Otherwise we collect it
+ std::vector<const UsingDirectiveDecl *> AllDirectives;
+ FindSameUsings(ND, Ctx, AllDirectives).TraverseAST(AST);
+ if (AllDirectives.empty()) {
+ UsingToAdd.push_back(ND->getQualifiedNameAsString());
+ }
+ }
+}
+
+// Produce edit adding 'using namespace xxx::yyy;'
+// for user-defined literals declared in an inline namespace
+llvm::Optional<tooling::Replacement>
+addUsingDirectives(const std::vector<std::string> &UsingToAdd,
+ SourceManager &SM, SourceLocation Loc) {
+ if (UsingToAdd.empty())
+ return llvm::None;
+ std::string Directives;
+ for (auto &Using : UsingToAdd)
+ Directives += llvm::formatv("using namespace {0};\n", Using);
+ // Remove the trailing newline
+ Directives.pop_back();
+ // For now we insert the using-directives to where the first using-directive
+ // to remove stands
+ return tooling::Replacement(SM, Loc, 0, Directives);
+}
+
bool RemoveUsingNamespace::prepare(const Selection &Inputs) {
// Find the 'using namespace' directive under the cursor.
auto *CA = Inputs.ASTSelection.commonAncestor();
@@ -144,6 +182,9 @@
// Collect all references to symbols from the namespace for which we're
// removing the directive.
std::vector<SourceLocation> IdentsToQualify;
+ // Collect all namespaces' name to add for user-defined literals declared
+ // in an inline namespace
+ std::vector<std::string> UsingToAdd;
for (auto &D : Inputs.AST->getLocalTopLevelDecls()) {
findExplicitReferences(
D,
@@ -164,15 +205,19 @@
// Avoid adding qualifiers before user-defined literals, e.g.
// using namespace std;
// auto s = "foo"s; // Must not changed to auto s = "foo" std::s;
- // FIXME: Add a using-directive for user-defined literals
+ // And add a using-directive for user-defined literals
// declared in an inline namespace, e.g.
// using namespace s^td;
// int main() { cout << "foo"s; }
// change to
- // using namespace std::literals;
+ // using namespace std::string_literals;
// int main() { std::cout << "foo"s; }
- if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName)
+ if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName) {
+ handleUserDefinedLiteral(T->getDeclContext(),
+ TargetDirective->getDeclContext(), Ctx,
+ UsingToAdd);
return;
+ }
}
SourceLocation Loc = Ref.NameLoc;
if (Loc.isMacroID()) {
@@ -201,6 +246,10 @@
std::unique(IdentsToQualify.begin(), IdentsToQualify.end()),
IdentsToQualify.end());
+ llvm::sort(UsingToAdd);
+ UsingToAdd.erase(std::unique(UsingToAdd.begin(), UsingToAdd.end()),
+ UsingToAdd.end());
+
// Produce replacements to remove the using directives.
tooling::Replacements R;
for (auto *D : AllDirectives) {
@@ -217,6 +266,11 @@
/*Length=*/0, Qualifier)))
return std::move(Err);
}
+ // Produce replacements to add the using directives for user-defined literals
+ if (auto AddUsing =
+ addUsingDirectives(UsingToAdd, SM, FirstUsingDirectiveLoc))
+ if (auto Err = R.add(AddUsing.value()))
+ return std::move(Err);
return Effect::mainFileEdit(SM, std::move(R));
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits