Index: clangd/ASTManager.h =================================================================== --- clangd/ASTManager.h +++ clangd/ASTManager.h @@ -18,9 +18,9 @@ #include #include #include +#include namespace clang { -class ASTUnit; class DiagnosticsEngine; class PCHContainerOperations; namespace tooling { @@ -55,20 +55,21 @@ // asynchronously. bool RunSynchronously; + CXIndex Index; + /// Loads a compilation database for URI. May return nullptr if it fails. The /// database is cached for subsequent accesses. clang::tooling::CompilationDatabase * getOrCreateCompilationDatabaseForFile(StringRef Uri); - // Craetes a new ASTUnit for the document at Uri. - // FIXME: This calls chdir internally, which is thread unsafe. - std::unique_ptr - createASTUnitForFile(StringRef Uri, const DocumentStore &Docs); + // Creates a new CXTranslationUnit for the document at Uri. + CXTranslationUnit + createTranslationUnitForFile(StringRef Uri, std::vector &unsavedFiles); void runWorker(); void parseFileAndPublishDiagnostics(StringRef File); /// Clang objects. - llvm::StringMap> ASTs; + llvm::StringMap CTranslationUnits; llvm::StringMap> CompilationDatabases; std::shared_ptr PCHs; Index: clangd/ASTManager.cpp =================================================================== --- clangd/ASTManager.cpp +++ clangd/ASTManager.cpp @@ -21,34 +21,38 @@ using namespace clangd; /// Retrieve a copy of the contents of every file in the store, for feeding into -/// ASTUnit. -static std::vector -getRemappedFiles(const DocumentStore &Docs) { +/// CXTranslationUnit. +static std::vector +getUnsavedFiles(const DocumentStore &Docs) { + std::vector UnsavedFiles; // FIXME: Use VFS instead. This would allow us to get rid of the chdir below. - std::vector RemappedFiles; for (const auto &P : Docs.getAllDocuments()) { StringRef FileName = P.first; FileName.consume_front("file://"); - RemappedFiles.push_back(ASTUnit::RemappedFile( - FileName, - llvm::MemoryBuffer::getMemBufferCopy(P.second, FileName).release())); + FileName.consume_front("file:"); + char * Filename = (char *) malloc(FileName.size() + 1); + memcpy(Filename, FileName.data(), FileName.size()); + Filename[FileName.size()] = 0; + + char * Content = (char *) malloc(P.second.size()); + memcpy(Content, P.second.data(), P.second.size()); + UnsavedFiles.push_back( + CXUnsavedFile { Filename, Content, P.second.size() }); } - return RemappedFiles; + return UnsavedFiles; } /// Convert from clang diagnostic level to LSP severity. -static int getSeverity(DiagnosticsEngine::Level L) { - switch (L) { - case DiagnosticsEngine::Remark: - return 4; - case DiagnosticsEngine::Note: +static int getSeverity(CXDiagnosticSeverity S) { + switch (S) { + case CXDiagnostic_Note: return 3; - case DiagnosticsEngine::Warning: + case CXDiagnostic_Warning: return 2; - case DiagnosticsEngine::Fatal: - case DiagnosticsEngine::Error: + case CXDiagnostic_Fatal: + case CXDiagnostic_Error: return 1; - case DiagnosticsEngine::Ignored: + case CXDiagnostic_Ignored: return 0; } llvm_unreachable("Unknown diagnostic level!"); @@ -57,6 +61,7 @@ ASTManager::ASTManager(JSONOutput &Output, DocumentStore &Store, bool RunSynchronously) : Output(Output), Store(Store), RunSynchronously(RunSynchronously), + Index(clang_createIndex(1, 1)), PCHs(std::make_shared()), ClangWorker([this]() { runWorker(); }) {} @@ -83,18 +88,20 @@ } void ASTManager::parseFileAndPublishDiagnostics(StringRef File) { - auto &Unit = ASTs[File]; // Only one thread can access this at a time. + auto UnsavedFiles = getUnsavedFiles(Store); - if (!Unit) { - Unit = createASTUnitForFile(File, this->Store); + auto &TU = CTranslationUnits[File]; + if (!TU) { + TU = createTranslationUnitForFile(File, UnsavedFiles); } else { // Do a reparse if this wasn't the first parse. // FIXME: This might have the wrong working directory if it changed in the // meantime. - Unit->Reparse(PCHs, getRemappedFiles(this->Store)); + CXUnsavedFile *UnsavedFilesPtr = &*UnsavedFiles.begin(); + clang_reparseTranslationUnit(TU, UnsavedFiles.size(), UnsavedFilesPtr, CXReparse_None); } - if (!Unit) + if (!TU) return; // Send the diagnotics to the editor. @@ -103,28 +110,47 @@ // main file. std::string Diagnostics; DiagnosticToReplacementMap LocalFixIts; // Temporary storage - for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), - DEnd = Unit->stored_diag_end(); - D != DEnd; ++D) { - if (!D->getLocation().isValid() || - !D->getLocation().getManager().isInMainFile(D->getLocation())) + auto const &DiagnosticSet = clang_getDiagnosticSetFromTU(TU); + unsigned NumDiagnosticsInSet = clang_getNumDiagnosticsInSet(DiagnosticSet); + for (unsigned i = 0; i < NumDiagnosticsInSet; ++i) { + auto const &D = clang_getDiagnosticInSet(DiagnosticSet, i); + auto const &Location = clang_getDiagnosticLocation(D); + if (clang_equalLocations(Location, clang_getNullLocation()) || + !clang_Location_isFromMainFile(Location)) continue; + + unsigned Line, Column; + clang_getSpellingLocation(Location, NULL, &Line, &Column, NULL); + enum CXDiagnosticSeverity sev = clang_getDiagnosticSeverity(D); + auto const &Spelling = clang_getDiagnosticSpelling(D); + Position P; - P.line = D->getLocation().getSpellingLineNumber() - 1; - P.character = D->getLocation().getSpellingColumnNumber(); + P.line = Line - 1; + P.character = Column; Range R = {P, P}; Diagnostics += R"({"range":)" + Range::unparse(R) + - R"(,"severity":)" + std::to_string(getSeverity(D->getLevel())) + - R"(,"message":")" + llvm::yaml::escape(D->getMessage()) + + R"(,"severity":)" + std::to_string(getSeverity(sev)) + + R"(,"message":")" + llvm::yaml::escape(clang_getCString(Spelling)) + R"("},)"; - // We convert to Replacements to become independent of the SourceManager. - clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()}; + clangd::Diagnostic Diag = {R, getSeverity(sev), clang_getCString(Spelling)}; auto &FixItsForDiagnostic = LocalFixIts[Diag]; - for (const FixItHint &Fix : D->getFixIts()) { - FixItsForDiagnostic.push_back(clang::tooling::Replacement( - Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert)); + unsigned FixItNum = clang_getDiagnosticNumFixIts(D); + for (unsigned CurFixIt = 0; CurFixIt < FixItNum; ++CurFixIt) { + CXSourceRange ReplacementRange; + CXString Replacement = clang_getDiagnosticFixIt(D, CurFixIt, &ReplacementRange); + + auto Start = clang_getRangeStart(ReplacementRange); + auto End = clang_getRangeEnd(ReplacementRange); + unsigned Offset; + clang_getSpellingLocation(Start, NULL, NULL, NULL, &Offset); + + unsigned EndOffset; + clang_getSpellingLocation(End, NULL, NULL, NULL, &EndOffset); + unsigned Length = EndOffset - Offset; + FixItsForDiagnostic.push_back(clang::tooling::Replacement(File, + Offset, Length, clang_getCString(Replacement))); } } @@ -177,12 +203,13 @@ return I.get(); } -std::unique_ptr -ASTManager::createASTUnitForFile(StringRef Uri, const DocumentStore &Docs) { +CXTranslationUnit +ASTManager::createTranslationUnitForFile(StringRef File, std::vector &UnsavedFiles) { tooling::CompilationDatabase *CDB = - getOrCreateCompilationDatabaseForFile(Uri); - + getOrCreateCompilationDatabaseForFile(File); + StringRef Uri(File); Uri.consume_front("file://"); + Uri.consume_front("file:"); std::vector Commands; if (CDB) { @@ -213,15 +240,10 @@ ArgStrs.push_back(S.c_str()); auto ArgP = &*ArgStrs.begin(); - return std::unique_ptr(ASTUnit::LoadFromCommandLine( - ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir, - /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, - getRemappedFiles(Docs), - /*RemappedFilesKeepOriginalName=*/true, - /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Complete, - /*CacheCodeCompletionResults=*/true, - /*IncludeBriefCommentsInCodeCompletion=*/true, - /*AllowPCHWithCompilerErrors=*/true)); + auto unsaved_files_ptr = &*UnsavedFiles.begin(); + CXTranslationUnit CXUnit; + clang_parseTranslationUnit2FullArgv(Index, nullptr, ArgP, ArgStrs.size(), unsaved_files_ptr, UnsavedFiles.size(), CXTranslationUnit_DetailedPreprocessingRecord, &CXUnit); + return CXUnit; } std::vector Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -12,6 +12,7 @@ clangBasic clangFormat clangFrontend + libclang clangTooling clangToolingCore LLVMSupport