diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -395,7 +395,8 @@ CodeCompletion::IncludeCandidate Include; Include.Header = ToInclude->first; if (ToInclude->second && ShouldInsert) - Include.Insertion = Includes.insert(ToInclude->first); + Include.Insertion = Includes.insert( + ToInclude->first, Inc.Directive == Symbol::Import); Completion.Includes.push_back(std::move(Include)); } else log("Failed to generate include insertion edits for adding header " diff --git a/clang-tools-extra/clangd/Headers.h b/clang-tools-extra/clangd/Headers.h --- a/clang-tools-extra/clangd/Headers.h +++ b/clang-tools-extra/clangd/Headers.h @@ -246,7 +246,8 @@ /// Calculates an edit that inserts \p VerbatimHeader into code. If the header /// is already included, this returns None. - llvm::Optional insert(llvm::StringRef VerbatimHeader) const; + llvm::Optional insert(llvm::StringRef VerbatimHeader, + bool ViaImport) const; private: StringRef FileName; diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp --- a/clang-tools-extra/clangd/Headers.cpp +++ b/clang-tools-extra/clangd/Headers.cpp @@ -345,10 +345,11 @@ } llvm::Optional -IncludeInserter::insert(llvm::StringRef VerbatimHeader) const { +IncludeInserter::insert(llvm::StringRef VerbatimHeader, bool ViaImport) const { llvm::Optional Edit; if (auto Insertion = Inserter.insert(VerbatimHeader.trim("\"<>"), - VerbatimHeader.startswith("<"))) + VerbatimHeader.startswith("<"), + ViaImport)) Edit = replacementToEdit(Code, *Insertion); return Edit; } diff --git a/clang-tools-extra/clangd/IncludeFixer.h b/clang-tools-extra/clangd/IncludeFixer.h --- a/clang-tools-extra/clangd/IncludeFixer.h +++ b/clang-tools-extra/clangd/IncludeFixer.h @@ -55,7 +55,8 @@ std::vector fixesForSymbols(const SymbolSlab &Syms) const; llvm::Optional insertHeader(llvm::StringRef Name, - llvm::StringRef Symbol = "") const; + llvm::StringRef Symbol = "", + bool ViaImport = false) const; struct UnresolvedName { std::string Name; // E.g. "X" in foo::X. diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -249,18 +249,21 @@ } llvm::Optional IncludeFixer::insertHeader(llvm::StringRef Spelled, - llvm::StringRef Symbol) const { + llvm::StringRef Symbol, + bool ViaImport) const { Fix F; - if (auto Edit = Inserter->insert(Spelled)) + if (auto Edit = Inserter->insert(Spelled, ViaImport)) F.Edits.push_back(std::move(*Edit)); else return llvm::None; if (Symbol.empty()) - F.Message = llvm::formatv("Include {0}", Spelled); + F.Message = llvm::formatv("{0} {1}", ViaImport ? "Import" : "Include", + Spelled); else - F.Message = llvm::formatv("Include {0} for symbol {1}", Spelled, Symbol); + F.Message = llvm::formatv("{0} {1} for symbol {2}", + ViaImport ? "Import" : "Include", Spelled, Symbol); return F; } @@ -325,7 +328,8 @@ if (!InsertedHeaders.try_emplace(ToInclude->first).second) continue; if (auto Fix = - insertHeader(ToInclude->first, (Sym.Scope + Sym.Name).str())) + insertHeader(ToInclude->first, (Sym.Scope + Sym.Name).str(), + Inc.Directive == Symbol::Import)) Fixes.push_back(std::move(*Fix)); } } else { diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp --- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -117,7 +117,8 @@ return Path.value_or(""); } - llvm::Optional insert(llvm::StringRef VerbatimHeader) { + llvm::Optional insert(llvm::StringRef VerbatimHeader, + bool ViaImport) { Clang = setupClang(); PreprocessOnlyAction Action; EXPECT_TRUE( @@ -126,7 +127,7 @@ IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(), CDB.getCompileCommand(MainFile)->Directory, &Clang->getPreprocessor().getHeaderSearchInfo()); - auto Edit = Inserter.insert(VerbatimHeader); + auto Edit = Inserter.insert(VerbatimHeader, ViaImport); Action.EndSourceFile(); return Edit; } @@ -330,9 +331,13 @@ } TEST_F(HeadersTest, PreferInserted) { - auto Edit = insert(""); + auto Edit = insert("", /*ViaImport=*/false); EXPECT_TRUE(Edit); - EXPECT_TRUE(StringRef(Edit->newText).contains("")); + EXPECT_EQ(Edit->newText, "#include \n"); + + Edit = insert("\"header.h\"", /*ViaImport=*/true); + EXPECT_TRUE(Edit); + EXPECT_EQ(Edit->newText, "#import \"header.h\"\n"); } TEST(Headers, NoHeaderSearchInfo) { diff --git a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h --- a/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/clang/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -51,9 +51,9 @@ HeaderIncludes(llvm::StringRef FileName, llvm::StringRef Code, const IncludeStyle &Style); - /// Inserts an #include directive of \p Header into the code. If \p IsAngled - /// is true, \p Header will be quoted with <> in the directive; otherwise, it - /// will be quoted with "". + /// Inserts an #include or #import directive of \p Header into the code. + /// If \p IsAngled is true, \p Header will be quoted with <> in the directive; + /// otherwise, it will be quoted with "". /// /// When searching for points to insert new header, this ignores #include's /// after the #include block(s) in the beginning of a file to avoid inserting @@ -70,13 +70,13 @@ /// same category in the code that should be sorted after \p IncludeName. If /// \p IncludeName already exists (with exactly the same spelling), this /// returns None. - llvm::Optional insert(llvm::StringRef Header, - bool IsAngled) const; + llvm::Optional + insert(llvm::StringRef Header, bool IsAngled, bool IsImport = false) const; /// Removes all existing #includes of \p Header quoted with <> if \p IsAngled /// is true or "" if \p IsAngled is false. - /// This doesn't resolve the header file path; it only deletes #includes with - /// exactly the same spelling. + /// This doesn't resolve the header file path; it only deletes #includes and + /// #imports with exactly the same spelling. tooling::Replacements remove(llvm::StringRef Header, bool IsAngled) const; // Matches a whole #include directive. diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp --- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp +++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp @@ -342,7 +342,8 @@ } llvm::Optional -HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const { +HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled, + bool IsImport) const { assert(IncludeName == trimInclude(IncludeName)); // If a
("header") already exists in code, "header" (
) with // different quotation will still be inserted. @@ -372,7 +373,8 @@ } assert(InsertOffset <= Code.size()); std::string NewInclude = - std::string(llvm::formatv("#include {0}\n", QuotedName)); + IsImport ? std::string(llvm::formatv("#import {0}\n", QuotedName)) + : std::string(llvm::formatv("#include {0}\n", QuotedName)); // When inserting headers at end of the code, also append '\n' to the code // if it does not end with '\n'. // FIXME: when inserting multiple #includes at the end of code, only one