diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CIndex/CIndex.cpp | 315 | ||||
-rw-r--r-- | tools/CIndex/CIndex.exports | 4 | ||||
-rw-r--r-- | tools/c-index-test/c-index-test.c | 106 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 32 |
4 files changed, 456 insertions, 1 deletions
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 4798e28328..9389139116 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -16,12 +16,15 @@ #include "clang/Index/Indexer.h" #include "clang/Index/ASTLocation.h" #include "clang/Index/Utils.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Decl.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/ASTUnit.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Config/config.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -31,6 +34,7 @@ #include <cstdio> #include <vector> +#include <sstream> #ifdef LLVM_ON_WIN32 #define WIN32_LEAN_AND_MEAN @@ -993,5 +997,316 @@ void clang_getDefinitionSpellingAndExtent(CXCursor C, *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); } +enum CXCompletionChunkKind +clang_getCompletionChunkKind(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return CXCompletionChunk_Text; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + return CXCompletionChunk_TypedText; + case CodeCompletionString::CK_Text: + return CXCompletionChunk_Text; + case CodeCompletionString::CK_Optional: + return CXCompletionChunk_Optional; + case CodeCompletionString::CK_Placeholder: + return CXCompletionChunk_Placeholder; + case CodeCompletionString::CK_Informative: + return CXCompletionChunk_Informative; + case CodeCompletionString::CK_CurrentParameter: + return CXCompletionChunk_CurrentParameter; + case CodeCompletionString::CK_LeftParen: + return CXCompletionChunk_LeftParen; + case CodeCompletionString::CK_RightParen: + return CXCompletionChunk_RightParen; + case CodeCompletionString::CK_LeftBracket: + return CXCompletionChunk_LeftBracket; + case CodeCompletionString::CK_RightBracket: + return CXCompletionChunk_RightBracket; + case CodeCompletionString::CK_LeftBrace: + return CXCompletionChunk_LeftBrace; + case CodeCompletionString::CK_RightBrace: + return CXCompletionChunk_RightBrace; + case CodeCompletionString::CK_LeftAngle: + return CXCompletionChunk_LeftAngle; + case CodeCompletionString::CK_RightAngle: + return CXCompletionChunk_RightAngle; + case CodeCompletionString::CK_Comma: + return CXCompletionChunk_Comma; + } + + // Should be unreachable, but let's be careful. + return CXCompletionChunk_Text; +} + +const char *clang_getCompletionChunkText(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return 0; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + return (*CCStr)[chunk_number].Text; + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return 0; + } + + // Should be unreachable, but let's be careful. + return 0; +} + +CXCompletionString +clang_getCompletionChunkCompletionString(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return 0; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + return 0; + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return (*CCStr)[chunk_number].Optional; + } + + // Should be unreachable, but let's be careful. + return 0; +} + +unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->size() : 0; +} +static CXCursorKind parseResultKind(llvm::StringRef Str) { + return llvm::StringSwitch<CXCursorKind>(Str) + .Case("Typedef", CXCursor_TypedefDecl) + .Case("Struct", CXCursor_StructDecl) + .Case("Union", CXCursor_UnionDecl) + .Case("Class", CXCursor_ClassDecl) + .Case("Field", CXCursor_FieldDecl) + .Case("EnumConstant", CXCursor_EnumConstantDecl) + .Case("Function", CXCursor_FunctionDecl) + // FIXME: Hacks here to make C++ member functions look like C functions + .Case("CXXMethod", CXCursor_FunctionDecl) + .Case("CXXConstructor", CXCursor_FunctionDecl) + .Case("CXXDestructor", CXCursor_FunctionDecl) + .Case("CXXConversion", CXCursor_FunctionDecl) + .Case("Var", CXCursor_VarDecl) + .Case("ParmVar", CXCursor_ParmDecl) + .Case("ObjCInterface", CXCursor_ObjCInterfaceDecl) + .Case("ObjCCategory", CXCursor_ObjCCategoryDecl) + .Case("ObjCProtocol", CXCursor_ObjCProtocolDecl) + .Case("ObjCProperty", CXCursor_ObjCPropertyDecl) + .Case("ObjCIvar", CXCursor_ObjCIvarDecl) + .Case("ObjCInstanceMethod", CXCursor_ObjCInstanceMethodDecl) + .Case("ObjCClassMethod", CXCursor_ObjCClassMethodDecl) + .Default(CXCursor_NotImplemented); +} + +void clang_codeComplete(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column, + CXCompletionIterator completion_iterator, + CXClientData client_data) { + // The indexer, which is mainly used to determine where diagnostics go. + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + // Build up the arguments for invoking 'clang'. + std::vector<const char *> argv; + + // First add the complete path to the 'clang' executable. + llvm::sys::Path ClangPath = CXXIdx->getClangPath(); + argv.push_back(ClangPath.c_str()); + + // Add the '-fsyntax-only' argument so that we only perform a basic + // syntax check of the code. + argv.push_back("-fsyntax-only"); + + // Add the appropriate '-code-completion-at=file:line:column' argument + // to perform code completion, with an "-Xclang" preceding it. + std::string code_complete_at; + code_complete_at += "-code-completion-at="; + code_complete_at += complete_filename; + code_complete_at += ":"; + code_complete_at += llvm::utostr(complete_line); + code_complete_at += ":"; + code_complete_at += llvm::utostr(complete_column); + argv.push_back("-Xclang"); + argv.push_back(code_complete_at.c_str()); + argv.push_back("-Xclang"); + argv.push_back("-code-completion-printer=cindex"); + + // Add the source file name (FIXME: later, we'll want to build temporary + // file from the buffer, or just feed the source text via standard input). + argv.push_back(source_filename); + + // Process the compiler options, stripping off '-o', '-c', '-fsyntax-only'. + for (int i = 0; i < num_command_line_args; ++i) + if (const char *arg = command_line_args[i]) { + if (strcmp(arg, "-o") == 0) { + ++i; // Also skip the matching argument. + continue; + } + if (strcmp(arg, "-emit-ast") == 0 || + strcmp(arg, "-c") == 0 || + strcmp(arg, "-fsyntax-only") == 0) { + continue; + } + + // Keep the argument. + argv.push_back(arg); + } + + // Add the null terminator. + argv.push_back(NULL); + + // Generate a temporary name for the AST file. + char tmpFile[L_tmpnam]; + char *tmpFileName = tmpnam(tmpFile); + llvm::sys::Path ResultsFile(tmpFileName); + + // Invoke 'clang'. + llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null + // on Unix or NUL (Windows). + std::string ErrMsg; + const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, &DevNull, 0 }; + llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, + /* redirects */ &Redirects[0], + /* secondsToWait */ 0, + /* memoryLimits */ 0, &ErrMsg); + + if (!ErrMsg.empty()) { + llvm::errs() << "clang_codeComplete: " << ErrMsg + << '\n' << "Arguments: \n"; + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I!=E; ++I) { + if (*I) + llvm::errs() << ' ' << *I << '\n'; + } + llvm::errs() << '\n'; + } + + // Parse the resulting source file to find code-completion results. + using llvm::MemoryBuffer; + using llvm::StringRef; + if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) { + StringRef Buffer = F->getBuffer(); + do { + StringRef::size_type CompletionIdx = Buffer.find("COMPLETION:"); + StringRef::size_type OverloadIdx = Buffer.find("OVERLOAD:"); + if (CompletionIdx == StringRef::npos && OverloadIdx == StringRef::npos) + break; + + if (OverloadIdx < CompletionIdx) { + // Parse an overload result. + Buffer = Buffer.substr(OverloadIdx); + + // Skip past the OVERLOAD: + Buffer = Buffer.substr(Buffer.find(':') + 1); + + // Find the entire completion string. + StringRef::size_type EOL = Buffer.find_first_of("\n\r"); + if (EOL == StringRef::npos) + continue; + + StringRef Line = Buffer.substr(0, EOL); + Buffer = Buffer.substr(EOL + 1); + CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line); + if (!CCStr || CCStr->empty()) + continue; + + // Vend the code-completion result to the caller. + CXCompletionResult Result; + Result.CursorKind = CXCursor_NotImplemented; + Result.CompletionString = CCStr; + if (completion_iterator) + completion_iterator(&Result, client_data); + delete CCStr; + + continue; + } + + // Parse a completion result. + Buffer = Buffer.substr(CompletionIdx); + + // Skip past the COMPLETION: + Buffer = Buffer.substr(Buffer.find(':') + 1); + + // Get the rank + unsigned Rank = 0; + StringRef::size_type AfterRank = Buffer.find(':'); + Buffer.substr(0, AfterRank).getAsInteger(10, Rank); + Buffer = Buffer.substr(AfterRank + 1); + + // Get the kind of result. + StringRef::size_type AfterKind = Buffer.find(':'); + StringRef Kind = Buffer.substr(0, AfterKind); + Buffer = Buffer.substr(AfterKind + 1); + + // Skip over any whitespace. + Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); + + // Find the entire completion string. + StringRef::size_type EOL = Buffer.find_first_of("\n\r"); + if (EOL == StringRef::npos) + continue; + + StringRef Line = Buffer.substr(0, EOL); + Buffer = Buffer.substr(EOL + 1); + CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line); + if (!CCStr || CCStr->empty()) + continue; + + // Vend the code-completion result to the caller. + CXCompletionResult Result; + Result.CursorKind = parseResultKind(Kind); + Result.CompletionString = CCStr; + if (completion_iterator) + completion_iterator(&Result, client_data); + delete CCStr; + } while (true); + delete F; + } + + ResultsFile.eraseFromDisk(); +} + } // end extern "C" diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports index 5f461d8ef6..ba977c1c85 100644 --- a/tools/CIndex/CIndex.exports +++ b/tools/CIndex/CIndex.exports @@ -1,8 +1,11 @@ +_clang_codeComplete _clang_createIndex _clang_createTranslationUnit _clang_createTranslationUnitFromSourceFile _clang_disposeIndex _clang_disposeTranslationUnit +_clang_getCompletionChunkKind +_clang_getCompletionChunkText _clang_getCursor _clang_getCursorColumn _clang_getCursorDecl @@ -24,6 +27,7 @@ _clang_getEntity _clang_getEntityFromDecl _clang_getFileName _clang_getFileTime +_clang_getNumCompletionChunks _clang_getTranslationUnitSpelling _clang_getURI _clang_isDeclaration diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 8791ee20a5..429c5e52dc 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -1,6 +1,7 @@ /* c-index-test.c */ #include "clang-c/Index.h" +#include <stdlib.h> #include <stdio.h> #include <string.h> @@ -94,10 +95,115 @@ static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor, } } +/* Parse file:line:column from the input string. Returns 0 on success, non-zero + on failure. If successful, the pointer *filename will contain newly-allocated + memory (that will be owned by the caller) to store the file name. */ +int parse_file_line_column(const char *input, char **filename, unsigned *line, + unsigned *column) { + const char *colon = strchr(input, ':'); + char *endptr = 0; + if (!colon) { + fprintf(stderr, "could not parse filename:line:column in '%s'\n", input); + return 1; + } + + /* Copy the file name. */ + *filename = (char*)malloc(colon - input); + strncpy(*filename, input, colon - input); + (*filename)[colon - input] = 0; + input = colon + 1; + + /* Parse the line number. */ + *line = strtol(input, &endptr, 10); + if (*endptr != ':') { + fprintf(stderr, "could not parse line:column in '%s'\n", input); + free(filename); + *filename = 0; + return 1; + } + input = endptr + 1; + + /* Parse the column number. */ + *column = strtol(input, &endptr, 10); + if (*endptr != 0) { + fprintf(stderr, "could not parse column in '%s'\n", input); + free(filename); + *filename = 0; + return 1; + } + + return 0; +} + +const char * +clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) { + switch (Kind) { + case CXCompletionChunk_Optional: return "Optional"; + case CXCompletionChunk_TypedText: return "TypedText"; + case CXCompletionChunk_Text: return "Text"; + case CXCompletionChunk_Placeholder: return "Placeholder"; + case CXCompletionChunk_Informative: return "Informative"; + case CXCompletionChunk_CurrentParameter: return "CurrentParameter"; + case CXCompletionChunk_LeftParen: return "LeftParen"; + case CXCompletionChunk_RightParen: return "RightParen"; + case CXCompletionChunk_LeftBracket: return "LeftBracket"; + case CXCompletionChunk_RightBracket: return "RightBracket"; + case CXCompletionChunk_LeftBrace: return "LeftBrace"; + case CXCompletionChunk_RightBrace: return "RightBrace"; + case CXCompletionChunk_LeftAngle: return "LeftAngle"; + case CXCompletionChunk_RightAngle: return "RightAngle"; + case CXCompletionChunk_Comma: return "Comma"; + } + + return "Unknown"; +} + +void print_completion_result(CXCompletionResult *completion_result, + CXClientData client_data) { + FILE *file = (FILE *)client_data; + + fprintf(file, "%s:", + clang_getCursorKindSpelling(completion_result->CursorKind)); + int I, N = clang_getNumCompletionChunks(completion_result->CompletionString); + for (I = 0; I != N; ++I) { + const char *text + = clang_getCompletionChunkText(completion_result->CompletionString, I); + + enum CXCompletionChunkKind Kind + = clang_getCompletionChunkKind(completion_result->CompletionString, I); + fprintf(file, "{%s %s}", + clang_getCompletionChunkKindSpelling(Kind), + text? text : ""); + } + fprintf(file, "\n"); +} + +void perform_code_completion(int argc, const char **argv) { + const char *input = argv[1]; + char *filename = 0; + unsigned line; + unsigned column; + input += strlen("-code-completion-at="); + if (parse_file_line_column(input, &filename, &line, &column)) + return; + + CXIndex CIdx = clang_createIndex(0, 0); + clang_codeComplete(CIdx, argv[argc - 1], argc - 3, argv + 2, + filename, line, column, &print_completion_result, stdout); + clang_disposeIndex(CIdx); + free(filename); +} + /* * First sign of life:-) */ int main(int argc, char **argv) { + if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) { + perform_code_completion(argc, (const char **)argv); + return 0; + } + + if (argc != 3) { printf("Incorrect usage of c-index-test (requires 3 arguments)\n"); return 0; diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index 97dc3fd6ee..cfef4bf72c 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -215,15 +215,45 @@ OutputFile("o", llvm::cl::desc("Specify output file")); +enum CodeCompletionPrinter { + CCP_Debug, + CCP_CIndex +}; + static llvm::cl::opt<ParsedSourceLocation> CodeCompletionAt("code-completion-at", llvm::cl::value_desc("file:line:column"), llvm::cl::desc("Dump code-completion information at a location")); +static llvm::cl::opt<CodeCompletionPrinter> +CodeCompletionPrinter("code-completion-printer", + llvm::cl::desc("Choose output type:"), + llvm::cl::init(CCP_Debug), + llvm::cl::values( + clEnumValN(CCP_Debug, "debug", + "Debug code-completion results"), + clEnumValN(CCP_CIndex, "cindex", + "Code-completion results for the CIndex library"), + clEnumValEnd)); + +static llvm::cl::opt<bool> +CodeCompletionWantsMacros("code-completion-macros", + llvm::cl::desc("Include macros in code-completion results")); + /// \brief Buld a new code-completion consumer that prints the results of /// code completion to standard output. static CodeCompleteConsumer *BuildPrintingCodeCompleter(Sema &S, void *) { - return new PrintingCodeCompleteConsumer(S, llvm::outs()); + switch (CodeCompletionPrinter.getValue()) { + case CCP_Debug: + return new PrintingCodeCompleteConsumer(S, CodeCompletionWantsMacros, + llvm::outs()); + + case CCP_CIndex: + return new CIndexCodeCompleteConsumer(S, CodeCompletionWantsMacros, + llvm::outs()); + }; + + return 0; } //===----------------------------------------------------------------------===// |