Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -12,6 +12,7 @@ #include "Diagnostics.h" #include "Function.h" +#include "Headers.h" #include "Path.h" #include "Protocol.h" #include "clang/Frontend/FrontendAction.h" @@ -40,18 +41,18 @@ namespace clangd { -using InclusionLocations = std::vector>; - // Stores Preamble and associated data. struct PreambleData { PreambleData(PrecompiledPreamble Preamble, std::vector TopLevelDeclIDs, - std::vector Diags, InclusionLocations IncLocations); + std::vector Diags, std::vector Inclusions); PrecompiledPreamble Preamble; std::vector TopLevelDeclIDs; std::vector Diags; - InclusionLocations IncLocations; + // Processes like code completions and go-to-definitions will need #include + // information, and their compile action skips preamble range. + std::vector Inclusions; }; /// Information required to run clang, e.g. to parse AST or do code completion. @@ -95,14 +96,14 @@ /// Returns the esitmated size of the AST and the accessory structures, in /// bytes. Does not include the size of the preamble. std::size_t getUsedBytes() const; - const InclusionLocations &getInclusionLocations() const; + const std::vector &getInclusions() const; private: ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, std::vector TopLevelDecls, std::vector Diags, - InclusionLocations IncLocations); + std::vector Inclusions); private: void ensurePreambleDeclsDeserialized(); @@ -122,7 +123,7 @@ std::vector Diags; std::vector TopLevelDecls; bool PreambleDeclsDeserialized; - InclusionLocations IncLocations; + std::vector Inclusions; }; using ASTParsedCallback = std::function; Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -83,44 +83,13 @@ std::vector TopLevelDecls; }; -class InclusionLocationsCollector : public PPCallbacks { -public: - InclusionLocationsCollector(SourceManager &SourceMgr, - InclusionLocations &IncLocations) - : SourceMgr(SourceMgr), IncLocations(IncLocations) {} - - void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, - StringRef FileName, bool IsAngled, - CharSourceRange FilenameRange, const FileEntry *File, - StringRef SearchPath, StringRef RelativePath, - const Module *Imported, - SrcMgr::CharacteristicKind FileType) override { - auto SR = FilenameRange.getAsRange(); - if (SR.isInvalid() || !File || File->tryGetRealPathName().empty()) - return; - - if (SourceMgr.isInMainFile(SR.getBegin())) { - // Only inclusion directives in the main file make sense. The user cannot - // select directives not in the main file. - IncLocations.emplace_back(halfOpenToRange(SourceMgr, FilenameRange), - File->tryGetRealPathName()); - } - } - -private: - SourceManager &SourceMgr; - InclusionLocations &IncLocations; -}; - class CppFilePreambleCallbacks : public PreambleCallbacks { public: std::vector takeTopLevelDeclIDs() { return std::move(TopLevelDeclIDs); } - InclusionLocations takeInclusionLocations() { - return std::move(IncLocations); - } + std::vector takeInclusions() { return std::move(Inclusions); } void AfterPCHEmitted(ASTWriter &Writer) override { TopLevelDeclIDs.reserve(TopLevelDecls.size()); @@ -146,14 +115,15 @@ std::unique_ptr createPPCallbacks() override { assert(SourceMgr && "SourceMgr must be set at this point"); - return llvm::make_unique(*SourceMgr, - IncLocations); + return collectInclusionsInMainFileCallback( + *SourceMgr, + [this](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); }); } private: std::vector TopLevelDecls; std::vector TopLevelDeclIDs; - InclusionLocations IncLocations; + std::vector Inclusions; SourceManager *SourceMgr = nullptr; }; @@ -191,15 +161,15 @@ return llvm::None; } - InclusionLocations IncLocations; + std::vector Inclusions; // Copy over the includes from the preamble, then combine with the // non-preamble includes below. if (Preamble) - IncLocations = Preamble->IncLocations; + Inclusions = Preamble->Inclusions; - Clang->getPreprocessor().addPPCallbacks( - llvm::make_unique(Clang->getSourceManager(), - IncLocations)); + Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback( + Clang->getSourceManager(), + [&Inclusions](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); })); if (!Action->Execute()) log("Execute() failed when building AST for " + MainInput.getFile()); @@ -213,7 +183,7 @@ std::vector ParsedDecls = Action->takeTopLevelDecls(); return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), std::move(ParsedDecls), ASTDiags.take(), - std::move(IncLocations)); + std::move(Inclusions)); } void ParsedAST::ensurePreambleDeclsDeserialized() { @@ -279,27 +249,27 @@ ::getUsedBytes(TopLevelDecls) + ::getUsedBytes(Diags); } -const InclusionLocations &ParsedAST::getInclusionLocations() const { - return IncLocations; +const std::vector &ParsedAST::getInclusions() const { + return Inclusions; } PreambleData::PreambleData(PrecompiledPreamble Preamble, std::vector TopLevelDeclIDs, std::vector Diags, - InclusionLocations IncLocations) + std::vector Inclusions) : Preamble(std::move(Preamble)), TopLevelDeclIDs(std::move(TopLevelDeclIDs)), Diags(std::move(Diags)), - IncLocations(std::move(IncLocations)) {} + Inclusions(std::move(Inclusions)) {} ParsedAST::ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, std::vector TopLevelDecls, - std::vector Diags, InclusionLocations IncLocations) + std::vector Diags, std::vector Inclusions) : Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Diags(std::move(Diags)), TopLevelDecls(std::move(TopLevelDecls)), PreambleDeclsDeserialized(false), - IncLocations(std::move(IncLocations)) { + Inclusions(std::move(Inclusions)) { assert(this->Clang); assert(this->Action); } @@ -448,8 +418,7 @@ return std::make_shared( std::move(*BuiltPreamble), SerializedDeclsCollector.takeTopLevelDeclIDs(), - PreambleDiagnostics.take(), - SerializedDeclsCollector.takeInclusionLocations()); + PreambleDiagnostics.take(), SerializedDeclsCollector.takeInclusions()); } else { log("Could not build a preamble for file " + Twine(FileName)); return nullptr; Index: clangd/Headers.h =================================================================== --- clangd/Headers.h +++ clangd/Headers.h @@ -11,7 +11,9 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEADERS_H #include "Path.h" +#include "Protocol.h" #include "clang/Basic/VirtualFileSystem.h" +#include "clang/Lex/PPCallbacks.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" @@ -32,6 +34,18 @@ bool valid() const; }; +// An #include directive that we found in the main file. +struct Inclusion { + Range R; // Inclusion range. + std::string Written; // Inclusion name as written e.g. . + Path Resolved; // Resolved path of included file. Empty if not resolved. +}; + +/// Returns a PPCallback that visits all inclusions in the main file. +std::unique_ptr +collectInclusionsInMainFileCallback(const SourceManager &SM, + std::function Callback); + /// Determines the preferred way to #include a file, taking into account the /// search path. Usually this will prefer a shorter representation like /// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'. Index: clangd/Headers.cpp =================================================================== --- clangd/Headers.cpp +++ clangd/Headers.cpp @@ -10,6 +10,7 @@ #include "Headers.h" #include "Compiler.h" #include "Logger.h" +#include "SourceCode.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -24,27 +25,34 @@ class RecordHeaders : public PPCallbacks { public: - RecordHeaders(llvm::StringSet<> &WrittenHeaders, - llvm::StringSet<> &ResolvedHeaders) - : WrittenHeaders(WrittenHeaders), ResolvedHeaders(ResolvedHeaders) {} + RecordHeaders(const SourceManager &SM, + std::function Callback) + : SM(SM), Callback(std::move(Callback)) {} - void InclusionDirective(SourceLocation /*HashLoc*/, - const Token & /*IncludeTok*/, + // Record existing #includes - both written and resolved paths. Only #includes + // in the main file are collected. + void InclusionDirective(SourceLocation HashLoc, const Token & /*IncludeTok*/, llvm::StringRef FileName, bool IsAngled, - CharSourceRange /*FilenameRange*/, - const FileEntry *File, llvm::StringRef /*SearchPath*/, + CharSourceRange FilenameRange, const FileEntry *File, + llvm::StringRef /*SearchPath*/, llvm::StringRef /*RelativePath*/, const Module * /*Imported*/, SrcMgr::CharacteristicKind /*FileType*/) override { - WrittenHeaders.insert( - (IsAngled ? "<" + FileName + ">" : "\"" + FileName + "\"").str()); - if (File != nullptr && !File->tryGetRealPathName().empty()) - ResolvedHeaders.insert(File->tryGetRealPathName()); + // Only inclusion directives in the main file make sense. The user cannot + // select directives not in the main file. + if (HashLoc.isInvalid() || !SM.isInMainFile(HashLoc)) + return; + std::string Written = + (IsAngled ? "<" + FileName + ">" : "\"" + FileName + "\"").str(); + std::string Resolved = (!File || File->tryGetRealPathName().empty()) + ? "" + : File->tryGetRealPathName(); + Callback({halfOpenToRange(SM, FilenameRange), Written, Resolved}); } private: - llvm::StringSet<> &WrittenHeaders; - llvm::StringSet<> &ResolvedHeaders; + const SourceManager &SM; + std::function Callback; }; } // namespace @@ -58,6 +66,12 @@ (!Verbatim && llvm::sys::path::is_absolute(File)); } +std::unique_ptr +collectInclusionsInMainFileCallback(const SourceManager &SM, + std::function Callback) { + return llvm::make_unique(SM, std::move(Callback)); +} + /// FIXME(ioeric): we might not want to insert an absolute include path if the /// path is not shortened. llvm::Expected @@ -110,17 +124,22 @@ return llvm::make_error( "Failed to begin preprocessor only action for file " + File, llvm::inconvertibleErrorCode()); - llvm::StringSet<> WrittenHeaders; - llvm::StringSet<> ResolvedHeaders; - Clang->getPreprocessor().addPPCallbacks( - llvm::make_unique(WrittenHeaders, ResolvedHeaders)); + std::vector Inclusions; + Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback( + Clang->getSourceManager(), + [&Inclusions](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); })); if (!Action.Execute()) return llvm::make_error( "Failed to execute preprocessor only action for file " + File, llvm::inconvertibleErrorCode()); + llvm::StringSet<> IncludedHeaders; + for (const auto &Inc : Inclusions) { + IncludedHeaders.insert(Inc.Written); + if (!Inc.Resolved.empty()) + IncludedHeaders.insert(Inc.Resolved); + } auto Included = [&](llvm::StringRef Header) { - return WrittenHeaders.find(Header) != WrittenHeaders.end() || - ResolvedHeaders.find(Header) != ResolvedHeaders.end(); + return IncludedHeaders.find(Header) != IncludedHeaders.end(); }; if (Included(DeclaringHeader.File) || Included(InsertedHeader.File)) return ""; Index: clangd/XRefs.cpp =================================================================== --- clangd/XRefs.cpp +++ clangd/XRefs.cpp @@ -234,12 +234,10 @@ std::vector Result; // Handle goto definition for #include. - for (auto &IncludeLoc : AST.getInclusionLocations()) { - Range R = IncludeLoc.first; + for (auto &Inc : AST.getInclusions()) { Position Pos = sourceLocToPosition(SourceMgr, SourceLocationBeg); - - if (R.contains(Pos)) - Result.push_back(Location{URIForFile{IncludeLoc.second}, {}}); + if (!Inc.Resolved.empty() && Inc.R.contains(Pos)) + Result.push_back(Location{URIForFile{Inc.Resolved}, {}}); } if (!Result.empty()) return Result;