From: Adam Czachorowski Date: Wed, 18 Nov 2020 16:43:19 +0000 (+0100) Subject: [clangd] AddUsing: Used spelled text instead of type name. X-Git-Tag: llvmorg-13-init~5232 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f6e59294b63e1fd0b25720f24111cd17865004be;p=platform%2Fupstream%2Fllvm.git [clangd] AddUsing: Used spelled text instead of type name. This improves the behavior related to type aliases, as well as cases of typo correction. Differential Revision: https://reviews.llvm.org/D91966 --- diff --git a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp index cf8347f..b53df446 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp @@ -43,9 +43,10 @@ public: } private: - // The qualifier to remove. Set by prepare(). + // All of the following are set by prepare(). + // The qualifier to remove. NestedNameSpecifierLoc QualifierToRemove; - // The name following QualifierToRemove. Set by prepare(). + // The name following QualifierToRemove. llvm::StringRef Name; }; REGISTER_TWEAK(AddUsing) @@ -206,8 +207,17 @@ bool isNamespaceForbidden(const Tweak::Selection &Inputs, return false; } +std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL, + const PrintingPolicy &Policy) { + std::string Out; + llvm::raw_string_ostream OutStream(Out); + NNSL.getNestedNameSpecifier()->print(OutStream, Policy); + return OutStream.str(); +} + bool AddUsing::prepare(const Selection &Inputs) { auto &SM = Inputs.AST->getSourceManager(); + const auto &TB = Inputs.AST->getTokens(); // Do not suggest "using" in header files. That way madness lies. if (isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(), @@ -247,11 +257,20 @@ bool AddUsing::prepare(const Selection &Inputs) { } } else if (auto *T = Node->ASTNode.get()) { if (auto E = T->getAs()) { - if (auto *BaseTypeIdentifier = - E.getType().getUnqualifiedType().getBaseTypeIdentifier()) { - Name = BaseTypeIdentifier->getName(); - QualifierToRemove = E.getQualifierLoc(); - } + QualifierToRemove = E.getQualifierLoc(); + + auto SpelledTokens = + TB.spelledForExpanded(TB.expandedTokens(E.getSourceRange())); + if (!SpelledTokens) + return false; + auto SpelledRange = syntax::Token::range(SM, SpelledTokens->front(), + SpelledTokens->back()); + Name = SpelledRange.text(SM); + + std::string QualifierToRemoveStr = getNNSLAsString( + QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy()); + if (!Name.consume_front(QualifierToRemoveStr)) + return false; // What's spelled doesn't match the qualifier. } } @@ -283,20 +302,13 @@ bool AddUsing::prepare(const Selection &Inputs) { Expected AddUsing::apply(const Selection &Inputs) { auto &SM = Inputs.AST->getSourceManager(); - auto &TB = Inputs.AST->getTokens(); - // Determine the length of the qualifier under the cursor, then remove it. - auto SpelledTokens = TB.spelledForExpanded( - TB.expandedTokens(QualifierToRemove.getSourceRange())); - if (!SpelledTokens) { - return error("Could not determine length of the qualifier"); - } - unsigned Length = - syntax::Token::range(SM, SpelledTokens->front(), SpelledTokens->back()) - .length(); + std::string QualifierToRemoveStr = getNNSLAsString( + QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy()); tooling::Replacements R; if (auto Err = R.add(tooling::Replacement( - SM, SpelledTokens->front().location(), Length, ""))) { + SM, SM.getSpellingLoc(QualifierToRemove.getBeginLoc()), + QualifierToRemoveStr.length(), ""))) { return std::move(Err); } @@ -313,9 +325,8 @@ Expected AddUsing::apply(const Selection &Inputs) { if (InsertionPoint->AlwaysFullyQualify && !isFullyQualified(QualifierToRemove.getNestedNameSpecifier())) UsingTextStream << "::"; - QualifierToRemove.getNestedNameSpecifier()->print( - UsingTextStream, Inputs.AST->getASTContext().getPrintingPolicy()); - UsingTextStream << Name << ";" << InsertionPoint->Suffix; + UsingTextStream << QualifierToRemoveStr << Name << ";" + << InsertionPoint->Suffix; assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID()); if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0, diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index 98d797a..edfaee7 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -2520,6 +2520,9 @@ public: EXPECT_UNAVAILABLE(Header + "void fun() { ::ban::fo^o(); }"); EXPECT_AVAILABLE(Header + "void fun() { banana::fo^o(); }"); + // Do not offer code action on typo-corrections. + EXPECT_UNAVAILABLE(Header + "/*error-ok*/c^c C;"); + // Check that we do not trigger in header files. FileName = "test.h"; ExtraArgs.push_back("-xc++-header"); // .h file is treated a C by default. @@ -2793,6 +2796,35 @@ using one::two::ff;using one::two::ee; void fun() { ff(); +})cpp"}, + // using alias; insert using for the spelled name. + {R"cpp( +#include "test.hpp" + +void fun() { + one::u^u u; +})cpp", + R"cpp( +#include "test.hpp" + +using one::uu; + +void fun() { + uu u; +})cpp"}, + // using namespace. + {R"cpp( +#include "test.hpp" +using namespace one; +namespace { +two::c^c C; +})cpp", + R"cpp( +#include "test.hpp" +using namespace one; +namespace {using two::cc; + +cc C; })cpp"}}; llvm::StringMap EditedFiles; for (const auto &Case : Cases) { @@ -2809,6 +2841,7 @@ public: static void mm() {} }; } +using uu = two::cc; })cpp"; EXPECT_EQ(apply(SubCase, &EditedFiles), Case.ExpectedSource); }