diff --git a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp @@ -86,6 +86,13 @@ const SourceManager &SM; }; +bool isFullyQualified(const NestedNameSpecifier *NNS) { + if (!NNS) + return false; + return NNS->getKind() == NestedNameSpecifier::Global || + isFullyQualified(NNS->getPrefix()); +} + struct InsertionPointData { // Location to insert the "using" statement. If invalid then the statement // should not be inserted at all (it already exists). @@ -94,6 +101,9 @@ // insertion point is anchored to, we may need one or more \n to ensure // proper formatting. std::string Suffix; + // Whether using should be fully qualified, even if what the user typed was + // not. This is based on our detection of the local style. + bool AlwaysFullyQualify = false; }; // Finds the best place to insert the "using" statement. Returns invalid @@ -118,7 +128,13 @@ SM) .TraverseAST(Inputs.AST->getASTContext()); + bool AlwaysFullyQualify = true; for (auto &U : Usings) { + // Only "upgrade" to fully qualified is all relevant using decls are fully + // qualified. Otherwise trust what the user typed. + if (!isFullyQualified(U->getQualifier())) + AlwaysFullyQualify = false; + if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc())) // "Usings" is sorted, so we're done. break; @@ -137,6 +153,7 @@ if (LastUsingLoc.isValid()) { InsertionPointData Out; Out.Loc = LastUsingLoc; + Out.AlwaysFullyQualify = AlwaysFullyQualify; return Out; } @@ -278,6 +295,9 @@ std::string UsingText; llvm::raw_string_ostream UsingTextStream(UsingText); UsingTextStream << "using "; + if (InsertionPoint->AlwaysFullyQualify && + !isFullyQualified(QualifierToRemove.getNestedNameSpecifier())) + UsingTextStream << "::"; QualifierToRemove.getNestedNameSpecifier()->print( UsingTextStream, Inputs.AST->getASTContext().getPrintingPolicy()); UsingTextStream << Name << ";" << InsertionPoint->Suffix; diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -2723,6 +2723,63 @@ void foo::fun() { ff(); +})cpp"}, + // If all other using are fully qualified, add :: + {R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using ::one::two::ee; + +void fun() { + one::two::f^f(); +})cpp", + R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using ::one::two::ff;using ::one::two::ee; + +void fun() { + ff(); +})cpp"}, + // Make sure we don't add :: if it's already there + {R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using ::one::two::ee; + +void fun() { + ::one::two::f^f(); +})cpp", + R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using ::one::two::ff;using ::one::two::ee; + +void fun() { + ff(); +})cpp"}, + // If even one using doesn't start with ::, do not add it + {R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using one::two::ee; + +void fun() { + one::two::f^f(); +})cpp", + R"cpp( +#include "test.hpp" + +using ::one::two::cc; +using one::two::ff;using one::two::ee; + +void fun() { + ff(); })cpp"}}; llvm::StringMap EditedFiles; for (const auto &Case : Cases) {