diff options
Diffstat (limited to 'tools/libclang/CIndexCodeCompletion.cpp')
-rw-r--r-- | tools/libclang/CIndexCodeCompletion.cpp | 306 |
1 files changed, 290 insertions, 16 deletions
diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp index 277fadf..d591c5d 100644 --- a/tools/libclang/CIndexCodeCompletion.cpp +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -16,18 +16,23 @@ #include "CIndexDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" +#include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/System/Program.h" +#include <cstdlib> +#include <cstdio> + #ifdef UDP_CODE_COMPLETION_LOGGER #include "clang/Basic/Version.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Timer.h" -#include "llvm/Support/raw_ostream.h" #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> @@ -43,11 +48,15 @@ namespace { /// This is the representation behind a CXCompletionString. class CXStoredCodeCompletionString : public CodeCompletionString { unsigned Priority; + CXAvailabilityKind Availability; public: - CXStoredCodeCompletionString(unsigned Priority) : Priority(Priority) { } + CXStoredCodeCompletionString(unsigned Priority, + CXAvailabilityKind Availability) + : Priority(Priority), Availability(Availability) { } unsigned getPriority() const { return Priority; } + CXAvailabilityKind getAvailability() const { return Availability; } }; } @@ -205,6 +214,13 @@ unsigned clang_getCompletionPriority(CXCompletionString completion_string) { return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely); } +enum CXAvailabilityKind +clang_getCompletionAvailability(CXCompletionString completion_string) { + CXStoredCodeCompletionString *CCStr + = (CXStoredCodeCompletionString *)completion_string; + return CCStr? CCStr->getAvailability() : CXAvailability_Available; +} + static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd, unsigned &Value) { if (Memory + sizeof(unsigned) > MemoryEnd) @@ -221,15 +237,11 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { AllocatedCXCodeCompleteResults(); ~AllocatedCXCodeCompleteResults(); - /// \brief The memory buffer from which we parsed the results. We - /// retain this buffer because the completion strings point into it. - llvm::MemoryBuffer *Buffer; - /// \brief Diagnostics produced while performing code completion. llvm::SmallVector<StoredDiagnostic, 8> Diagnostics; /// \brief Diag object - Diagnostic Diag; + llvm::IntrusiveRefCntPtr<Diagnostic> Diag; /// \brief Language options used to adjust source locations. LangOptions LangOpts; @@ -243,25 +255,29 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { /// \brief Temporary files that should be removed once we have finished /// with the code-completion results. std::vector<llvm::sys::Path> TemporaryFiles; + + /// \brief Temporary buffers that will be deleted once we have finished with the code-completion results. + llvm::SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers; }; AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults() - : CXCodeCompleteResults(), Buffer(0), SourceMgr(Diag) { } + : CXCodeCompleteResults(), Diag(new Diagnostic), SourceMgr(*Diag) { } AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { for (unsigned I = 0, N = NumResults; I != N; ++I) delete (CXStoredCodeCompletionString *)Results[I].CompletionString; delete [] Results; - delete Buffer; for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) TemporaryFiles[I].eraseFromDisk(); + for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) + delete TemporaryBuffers[I]; } CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, const char *source_filename, int num_command_line_args, - const char **command_line_args, + const char * const *command_line_args, unsigned num_unsaved_files, struct CXUnsavedFile *unsaved_files, const char *complete_filename, @@ -273,6 +289,17 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, #endif #endif + bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0; + + llvm::OwningPtr<llvm::NamedRegionTimer> CCTimer; + if (getenv("LIBCLANG_TIMING")) { + llvm::SmallString<128> TimerName; + llvm::raw_svector_ostream TimerNameOut(TimerName); + TimerNameOut << "Code completion (out-of-process) @ " << complete_filename + << ":" << complete_line << ":" << complete_column; + CCTimer.reset(new llvm::NamedRegionTimer(TimerNameOut.str())); + } + // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); @@ -348,6 +375,15 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, argv.push_back(arg); } + if (EnableLogging) { + std::string Log = ClangPath.str(); + for (unsigned I = 0, N = argv.size(); I != N; ++I) { + Log += ' '; + Log += argv[I]; + } + fprintf(stderr, "libclang (Code Completion): %s\n", Log.c_str()); + } + // Add the null terminator. argv.push_back(NULL); @@ -363,6 +399,8 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, llvm::sys::Path DiagnosticsFile(tmpResultsFileName); TemporaryFiles.push_back(DiagnosticsFile); + + // Invoke 'clang'. llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null // on Unix or NUL (Windows). @@ -392,7 +430,6 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults; Results->Results = 0; Results->NumResults = 0; - Results->Buffer = 0; // FIXME: Set Results->LangOpts! if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) { llvm::SmallVector<CXCompletionResult, 4> CompletionResults; @@ -407,8 +444,13 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, if (ReadUnsigned(Str, StrEnd, Priority)) break; + unsigned Availability; + if (ReadUnsigned(Str, StrEnd, Availability)) + break; + CXStoredCodeCompletionString *CCStr - = new CXStoredCodeCompletionString(Priority); + = new CXStoredCodeCompletionString(Priority, + (CXAvailabilityKind)Availability); if (!CCStr->Deserialize(Str, StrEnd)) { delete CCStr; continue; @@ -428,7 +470,7 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, Results->NumResults = CompletionResults.size(); memcpy(Results->Results, CompletionResults.data(), CompletionResults.size() * sizeof(CXCompletionResult)); - Results->Buffer = F; + Results->TemporaryBuffers.push_back(F); } LoadSerializedDiagnostics(DiagnosticsFile, num_unsaved_files, unsaved_files, @@ -511,9 +553,208 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, } #endif #endif + clang_sortCodeCompletionResults(Results->Results, Results->NumResults); return Results; } +} // end extern "C" + +namespace { + class CaptureCompletionResults : public CodeCompleteConsumer { + AllocatedCXCodeCompleteResults &AllocatedResults; + + public: + explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results) + : CodeCompleteConsumer(true, false, true, false), + AllocatedResults(Results) { } + + virtual void ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) { + AllocatedResults.Results = new CXCompletionResult [NumResults]; + AllocatedResults.NumResults = NumResults; + for (unsigned I = 0; I != NumResults; ++I) { + CXStoredCodeCompletionString *StoredCompletion + = new CXStoredCodeCompletionString(Results[I].Priority, + Results[I].Availability); + (void)Results[I].CreateCodeCompletionString(S, StoredCompletion); + AllocatedResults.Results[I].CursorKind = Results[I].CursorKind; + AllocatedResults.Results[I].CompletionString = StoredCompletion; + } + } + + // FIXME: Add ProcessOverloadCandidates? + }; +} + +extern "C" { +struct CodeCompleteAtInfo { + CXTranslationUnit TU; + const char *complete_filename; + unsigned complete_line; + unsigned complete_column; + struct CXUnsavedFile *unsaved_files; + unsigned num_unsaved_files; + unsigned options; + CXCodeCompleteResults *result; +}; +void clang_codeCompleteAt_Impl(void *UserData) { + CodeCompleteAtInfo *CCAI = static_cast<CodeCompleteAtInfo*>(UserData); + CXTranslationUnit TU = CCAI->TU; + const char *complete_filename = CCAI->complete_filename; + unsigned complete_line = CCAI->complete_line; + unsigned complete_column = CCAI->complete_column; + struct CXUnsavedFile *unsaved_files = CCAI->unsaved_files; + unsigned num_unsaved_files = CCAI->num_unsaved_files; + unsigned options = CCAI->options; + CCAI->result = 0; + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); +#endif +#endif + + bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0; + + ASTUnit *AST = static_cast<ASTUnit *>(TU); + if (!AST) + return; + + // Perform the remapping of source files. + llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; + for (unsigned I = 0; I != num_unsaved_files; ++I) { + llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename, + Buffer)); + } + + if (EnableLogging) { + // FIXME: Add logging. + } + + // Parse the resulting source file to find code-completion results. + AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults; + Results->Results = 0; + Results->NumResults = 0; + + // Create a code-completion consumer to capture the results. + CaptureCompletionResults Capture(*Results); + + // Perform completion. + AST->CodeComplete(complete_filename, complete_line, complete_column, + RemappedFiles.data(), RemappedFiles.size(), + (options & CXCodeComplete_IncludeMacros), + (options & CXCodeComplete_IncludeCodePatterns), + Capture, + *Results->Diag, Results->LangOpts, Results->SourceMgr, + Results->FileMgr, Results->Diagnostics, + Results->TemporaryBuffers); + + + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); + llvm::SmallString<256> LogResult; + llvm::raw_svector_ostream os(LogResult); + + // Figure out the language and whether or not it uses PCH. + const char *lang = 0; + bool usesPCH = false; + + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + if (*I == 0) + continue; + if (strcmp(*I, "-x") == 0) { + if (I + 1 != E) { + lang = *(++I); + continue; + } + } + else if (strcmp(*I, "-include") == 0) { + if (I+1 != E) { + const char *arg = *(++I); + llvm::SmallString<512> pchName; + { + llvm::raw_svector_ostream os(pchName); + os << arg << ".pth"; + } + pchName.push_back('\0'); + struct stat stat_results; + if (stat(pchName.data(), &stat_results) == 0) + usesPCH = true; + continue; + } + } + } + + os << "{ "; + os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); + os << ", \"numRes\": " << Results->NumResults; + os << ", \"diags\": " << Results->Diagnostics.size(); + os << ", \"pch\": " << (usesPCH ? "true" : "false"); + os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; + const char *name = getlogin(); + os << ", \"user\": \"" << (name ? name : "unknown") << '"'; + os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; + os << " }"; + + llvm::StringRef res = os.str(); + if (res.size() > 0) { + do { + // Setup the UDP socket. + struct sockaddr_in servaddr; + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); + if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, + &servaddr.sin_addr) <= 0) + break; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + break; + + sendto(sockfd, res.data(), res.size(), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + close(sockfd); + } + while (false); + } +#endif +#endif + CCAI->result = Results; +} +CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + unsigned options) { + CodeCompleteAtInfo CCAI = { TU, complete_filename, complete_line, + complete_column, unsaved_files, num_unsaved_files, + options, 0 }; + llvm::CrashRecoveryContext CRC; + + if (!CRC.RunSafely(clang_codeCompleteAt_Impl, &CCAI)) { + fprintf(stderr, "libclang: crash detected in code completion\n"); + static_cast<ASTUnit *>(TU)->setUnsafeToFree(true); + return 0; + } + + return CCAI.result; +} + +unsigned clang_defaultCodeCompleteOptions(void) { + return CXCodeComplete_IncludeMacros; +} + void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { if (!ResultsIn) return; @@ -522,7 +763,7 @@ void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); delete Results; } - + unsigned clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { AllocatedCXCodeCompleteResults *Results @@ -546,3 +787,36 @@ clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, } // end extern "C" + +namespace { + struct OrderCompletionResults { + bool operator()(const CXCompletionResult &XR, + const CXCompletionResult &YR) const { + CXStoredCodeCompletionString *X + = (CXStoredCodeCompletionString *)XR.CompletionString; + CXStoredCodeCompletionString *Y + = (CXStoredCodeCompletionString *)YR.CompletionString; + + const char *XText = X->getTypedText(); + const char *YText = Y->getTypedText(); + if (!XText || !YText) + return XText != 0; + + int result = llvm::StringRef(XText).compare_lower(YText); + if (result < 0) + return true; + if (result > 0) + return false; + + result = llvm::StringRef(XText).compare(YText); + return result; + } + }; +} + +extern "C" { + void clang_sortCodeCompletionResults(CXCompletionResult *Results, + unsigned NumResults) { + std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); + } +} |