From db6026b48960726a72c85459088054a527e5f9ce Mon Sep 17 00:00:00 2001 From: Kirill Ignatiev Date: Mon, 19 Jan 2015 21:43:14 -0500 Subject: [PATCH] Add support for returning expression types to lisp The bulk of this commit is slightly dull code for printing information about libclang cursors in sexp form. --- irony-exprtype.el | 58 +++++ server/src/CMakeLists.txt | 1 + server/src/Command.cpp | 9 +- server/src/Commands.def | 3 + server/src/Irony.cpp | 288 +++++++++++++++++++++++++ server/src/Irony.h | 15 ++ server/src/main.cpp | 4 + server/src/support/Sexp.cpp | 188 ++++++++++++++++ server/src/support/Sexp.h | 134 ++++++++++++ server/src/support/Sexp_CursorKind.cpp | 232 ++++++++++++++++++++ server/src/support/Sexp_TypeKind.cpp | 73 +++++++ 11 files changed, 1004 insertions(+), 1 deletion(-) create mode 100644 irony-exprtype.el create mode 100644 server/src/support/Sexp.cpp create mode 100644 server/src/support/Sexp.h create mode 100644 server/src/support/Sexp_CursorKind.cpp create mode 100644 server/src/support/Sexp_TypeKind.cpp diff --git a/irony-exprtype.el b/irony-exprtype.el new file mode 100644 index 00000000..f6be2e2d --- /dev/null +++ b/irony-exprtype.el @@ -0,0 +1,58 @@ +;;; irony-exprtype.el --- Type information at point. -*- lexical-binding: t -*- +;;; Commentary: +;; +;; See Irony::exprtype in Irony.cpp. +;; +;;; Code: + +(require 'irony) +(require 'pcase) + +(defun irony-exprtype (&optional callback) + "Return type information string at point. + +If region is active, will use the region instead." + (interactive) + (if (use-region-p) + (irony-exprtype-region (region-beginning) (region-end) callback) + (irony-exprtype-region (point) (point) callback))) + +(defun irony-exprtype-region (start end callback) + (irony--send-file-request + "exprtype" + ;; FIXME Why "list" of a callback? + (list (apply-partially #'irony-exprtype--handler callback)) + (number-to-string (1- (position-bytes start))) + (number-to-string (1- (position-bytes end))))) + +(defun irony-exprtype--fix-offsets (thing) + ;; Adjust all values of the form (bound byte-offset byte-offset) + ;; to use char offsets instead. + (when (consp thing) + (pcase (car thing) + (`(bounds . (,start-bytes . (,end-bytes . nil))) + (let ((start (byte-to-position (1+ start-bytes))) + (end (byte-to-position (1+ end-bytes)))) + (setcar thing (list 'bounds start end)))) + (_ (irony-exprtype--fix-offsets (car thing)))) + (irony-exprtype--fix-offsets (cdr thing)))) + +(defun irony-exprtype--handler (callback exprtype) + (irony-exprtype--fix-offsets exprtype) + (if callback (funcall callback exprtype) (message "%S" exprtype))) + +;; ;; FIXME Remove when no longer helpful +;; (trace-function #'irony-exprtype) +;; (trace-function #'irony-exprtype-region) +;; (trace-function #'irony-exprtype--handler) +;; (trace-function #'process-send-string) +;; (trace-function #'start-process) +;; (trace-function #'irony--send-file-request) +;; (trace-function #'irony--server-process-filter) +;; (trace-function #'irony--start-server-process) + +(provide 'irony-exprtype) +;; Local Variables: +;; byte-compile-warnings: (not cl-functions) +;; End: +;;; irony-exprtype.el ends here diff --git a/server/src/CMakeLists.txt b/server/src/CMakeLists.txt index 90963c67..eff8f4c7 100644 --- a/server/src/CMakeLists.txt +++ b/server/src/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable(irony-server support/NonCopyable.h support/TemporaryFile.cpp support/TemporaryFile.h + support/Sexp.cpp Command.cpp Commands.def diff --git a/server/src/Command.cpp b/server/src/Command.cpp index e6b34efa..86a932f6 100644 --- a/server/src/Command.cpp +++ b/server/src/Command.cpp @@ -1,7 +1,7 @@ /** * \file * \author Guillaume Papin - * + * * \brief Command parser definitions. * * This file is distributed under the GNU General Public License. See @@ -158,6 +158,13 @@ Command *CommandParser::parse(const std::vector &argv) { handleUnsaved = true; break; + case Command::ExprType: + positionalArgs.push_back(StringConverter(&command_.file)); + positionalArgs.push_back(UnsignedIntConverter(&command_.line)); + positionalArgs.push_back(UnsignedIntConverter(&command_.column)); + handleUnsaved = true; + break; + case Command::Help: case Command::Exit: break; diff --git a/server/src/Commands.def b/server/src/Commands.def index 4e407f84..67f2f8ee 100644 --- a/server/src/Commands.def +++ b/server/src/Commands.def @@ -16,6 +16,9 @@ X(Diagnostics, "diagnostics", "FILE - look for diagnostics in file") X(Complete, "complete", "FILE LINE COL - perform code completion at a given location") +X(ExprType, + "exprtype", + "FILE START END - get type of expression between byte offsets START and END") X(Help, "help", "show this message") X(Exit, "exit", "exit interactive mode, print nothing") X(SetDebug, "set-debug", "[on|off] - enable or disable verbose logging") diff --git a/server/src/Irony.cpp b/server/src/Irony.cpp index 50658e89..b470eaa5 100644 --- a/server/src/Irony.cpp +++ b/server/src/Irony.cpp @@ -13,6 +13,8 @@ #include "Irony.h" #include "support/iomanip_quoted.h" +#include "support/Sexp.h" +using sexp::repr; #include #include @@ -342,3 +344,289 @@ void Irony::complete(const std::string &file, std::cout << ")\n"; } } + +unsigned getOffset(CXSourceLocation loc) { + unsigned offset, line, col; + clang_getSpellingLocation(loc, nullptr, &line, &col, &offset); + return offset; +} + +unsigned getOffset(CXCursor cursor) { + CXSourceLocation sloc = clang_getCursorLocation(cursor); + return getOffset(sloc); +} + +CXCursor cursorParent(CXCursor cursor) { + CXCursor parent = clang_getCursorLexicalParent(cursor); + if (clang_isInvalid(clang_getCursorKind(parent))) { + parent = clang_getCursorSemanticParent(cursor); + } + return parent; +} + +/// Whether cursor is suitable for having its information being shown +/// to the user. +bool isCursorPrintable(CXCursor cursor) { + CXType type = clang_getCursorType(cursor); + bool valid = !clang_isInvalid(cursor.kind) && + !clang_isUnexposed(cursor.kind) && + !clang_isTranslationUnit(cursor.kind); + // Unexposed types are allowed (common in C++) + bool validType = type.kind != CXType_Invalid; + bool isLiteral = cursor.kind == CXCursor_IntegerLiteral || + cursor.kind == CXCursor_FloatingLiteral || + cursor.kind == CXCursor_ImaginaryLiteral || + cursor.kind == CXCursor_StringLiteral || + cursor.kind == CXCursor_CharacterLiteral; + return valid && validType && !isLiteral; +} + +CXCursor getToplevelCursor(CXTranslationUnit tu, CXSourceLocation sloc) { + CXCursor cursor = clang_getCursor(tu, sloc); + CXCursorKind kind = clang_getCursorKind(cursor); + while (!clang_isTranslationUnit(kind) && !clang_isInvalid(kind)) { + CXCursor parent = clang_getCursorLexicalParent(cursor); + if (clang_isInvalid(clang_getCursorKind(parent))) { + parent = clang_getCursorSemanticParent(cursor); + } + if (clang_isTranslationUnit(clang_getCursorKind(parent))) { + break; + } + cursor = parent; + kind = clang_getCursorKind(cursor); + } + return cursor; +} + +CXCursor getCursorFirstChild(CXCursor cursor) { + CXCursor first_child = clang_getNullCursor(); + auto visitor = [](CXCursor current, CXCursor, CXClientData client_data) + -> CXChildVisitResult { + *static_cast(client_data) = current; + return CXChildVisit_Break; + }; + clang_visitChildren(cursor, visitor, &first_child); + return first_child; +} + +/// Wrapper around clang_getCursorExtent +struct Range { + Range(CXCursor cursor) + : range(clang_getCursorExtent(cursor)) + , start(clang_getRangeStart(range)) + , end(clang_getRangeEnd(range)) + , start_offset(getOffset(start)) + , end_offset(getOffset(end)) { + } + bool contains(unsigned offset) { + return start_offset <= offset && offset < end_offset; + } + bool contains(unsigned a, unsigned b) { + return start_offset <= std::min(a, b) && std::max(a, b) <= end_offset; + } + CXSourceRange range; + CXSourceLocation start, end; + unsigned start_offset, end_offset; +}; + +struct exprtypeAlist { + exprtypeAlist(const CXCursor &cursor) : cursor(cursor) { + } + const CXCursor &cursor; +}; + +struct typeAlist { + typeAlist(const CXType &type) : type(type) { + } + const CXType &type; +}; + +std::ostream &operator<<(std::ostream &out, const typeAlist &proxy) { + CXType type = proxy.type; + if (type.kind == CXType_Invalid) + return out << "nil"; + out << "("; + if (type.kind != CXType_Unexposed) + out << sexp::alistEntry("kind", repr(type.kind)); + out << sexp::alistEntry("spelling", repr(clang_getTypeSpelling(type))); + + int numargs = clang_getNumArgTypes(type); + if (numargs >= 0) { + out << " (args"; + for (int i = 0; i < numargs; ++i) { + CXType arg = clang_getArgType(type, i); + out << sexp::alistEntry("type", typeAlist(arg)); + } + out << ")"; + } + + if (clang_isFunctionTypeVariadic(type)) { + out << " (variadic . t)"; + } + + return out << ")"; +} + +std::ostream& operator<<(std::ostream& out, const exprtypeAlist &proxy) { + CXCursor cursor = proxy.cursor; + + /* Examples of what might be printed (from test.cc) + +FIXME More robust examples and testing + +(exprtype (bounds 832 836) (kind . CallExpr) (type (kind . Dependent) (spelling . "")) (call (bounds 832 833) (kind . OverloadedDeclRef) (spelling . "h") (overloaded (completion (comment . "docstring for h(X).") (priority . 50) (chunks (ResultType . "X") "h(" (Placeholder . "const X &x")")")) (completion (comment . "docstring for h") (priority . 50) (chunks (ResultType . "T") "h(" (Placeholder . "const T &x")")")))) (args (834 . 835))) + +(exprtype (bounds 834 835) (kind . DeclRefExpr) (spelling . "t") (type (spelling . "T")) (completion (priority . 50) (chunks (ResultType . "T") "t"))) + +(exprtype (bounds 892 902) (kind . ParmDecl) (spelling . "y") (type (kind . LValueReference) (spelling . "const Y &")) (completion (priority . 50) (chunks (ResultType . "const Y &") "y"))) + +(exprtype (bounds 1287 1291) (kind . CallExpr) (spelling . "f") (type (kind . Void) (spelling . "void")) (completion (comment . "docstring for f") (priority . 50) (chunks (ResultType . "void") "f(" (Placeholder . "string x") ")")) (call (bounds 1287 1288) (kind . DeclRefExpr) (spelling . "f") (type (kind . FunctionProto) (spelling . "void (string)") (args (type (kind . Typedef) (spelling . "string")))) (completion (comment . "docstring for f") (priority . 50) (chunks (ResultType . "void") "f(" (Placeholder . "string x") ")")) (comment . "docstring for f")) (args (1289 . 1290)) (comment . "docstring for f")) + +(exprtype (bounds 1289 1290) (kind . DeclRefExpr) (spelling . "x") (type (kind . Typedef) (spelling . "string")) (completion (priority . 50) (chunks (ResultType . "string") "x"))) + +NOTE: no arguments here despite this being a CallExpr +(exprtype (bounds 1205 1227) (kind . CallExpr) (spelling . "vector") (type (spelling . "vector")) (completion (parent . "std::__1::vector") (priority . 50) (chunks "vector(" (Placeholder . "size_type __n") "," (Placeholder . "const_reference __x") ")")) (call (bounds 1205 1211) (kind . TemplateRef) (spelling . "vector") (completion (parent . "std::__1") (priority . 50) (chunks "vector<" (Placeholder . "class _Tp") (Optional . (completion (priority . 0) (chunks "," (Placeholder . "class _Allocator")))) ">")))) + + */ + + Range range{cursor}; + CXType type = clang_getCursorType(cursor); + + // This is a long alist of essentially arbitrary elements. + if (!clang_isInvalid(cursor.kind)) + out << " (bounds " << range.start_offset << " " << range.end_offset << ")"; + out << sexp::alistEntry("kind", repr(cursor.kind)) + << sexp::alistEntry("spelling", repr(clang_getCursorSpelling(cursor))) + << sexp::alistEntry("type", typeAlist(type)); + + // Find a suitable completion string + { + CXCompletionString completion = + clang_getCursorCompletionString(clang_getCursorDefinition(cursor)); + if (!completion) + completion = + clang_getCursorCompletionString(clang_getCursorReferenced(cursor)); + if (!completion) + completion = clang_getCursorCompletionString(cursor); + if (completion) + out << " " << sexp::completion(completion); + } + + // If cursor is a call to a function, we show the type of the + // function as well. Then the type is the return type. + if (cursor.kind == CXCursor_CallExpr) { + // Find the first child, which should be the function + // FIXME Is this true for obj-c? I'm not familiar with it. + CXCursor funref = cursor; + for (CXCursor cursor_child = funref; !clang_isInvalid(cursor_child.kind); + funref = cursor_child, cursor_child = getCursorFirstChild(funref)) + ; + + out << " (call" << exprtypeAlist(funref) << ")"; + } + + // FIXME Constructor arguments don't seem to appear here. + // Are they of kind CallExpr? + int numargs = clang_Cursor_getNumArguments(cursor); + if (numargs >= 0) { + out << " (args"; + for (int i = 0; i < numargs; ++i) { + CXCursor arg = clang_Cursor_getArgument(cursor, i); + Range range{arg}; + out << " (" << range.start_offset << " . " << range.end_offset << ")"; + } + out << ")"; + } + + out << sexp::alistEntry("comment", sexp::comment(cursor)); + + // Overloaded decl ref + { + CXCursor ref = cursor; + int num_overloaded = clang_getNumOverloadedDecls(ref); + if (!num_overloaded) { + ref = clang_getCursorReferenced(cursor); + num_overloaded = clang_getNumOverloadedDecls(ref); + } + if (num_overloaded > 0) { + out << " (overloaded"; + for (int i = 0; i < num_overloaded; ++i) { + CXCursor decl = clang_getOverloadedDecl(ref, i); + out << " " << sexp::completion(decl); + } + out << ")"; + } + } + + return out; +} + +void Irony::exprtype(const std::string &file, + unsigned start_offset, + unsigned end_offset, + const std::vector &flags, + const std::vector &unsavedFiles) { + // NOTE Duplicate from complete(..) above + TUManager::Settings settings; + settings.parseTUOptions |= CXTranslationUnit_CacheCompletionResults; +#if HAS_BRIEF_COMMENTS_IN_COMPLETION + settings.parseTUOptions |= + CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; +#endif + (void)tuManager_.registerSettings(settings); + CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, unsavedFiles); + + if (!tu) { + std::cout << "nil\n"; + return; + } + + // if (true || debug_) { + // std::cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; + // dumpDiagnostics(tu); + // std::cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; + // } + + if (end_offset < start_offset) + end_offset = start_offset; + + CXFile cxfile = clang_getFile(tu, file.c_str()); + CXSourceLocation + start_loc = clang_getLocationForOffset(tu, cxfile, start_offset); + + CXCursor cursor = clang_getCursor(tu, start_loc); + + /* The lexical parent of a cursor doesn't seem to be its parent in + the AST, which is what we want. So go to the toplevel cursor, just + below the translation unit, and walk down looking for printable + cursors covering the region. */ + if (!isCursorPrintable(cursor) || + !Range(cursor).contains(start_offset, end_offset)) { + cursor = getToplevelCursor(tu, start_loc); + struct data_t { + const unsigned start_offset, end_offset; + CXCursor cursor; + } data{start_offset, end_offset, clang_getNullCursor()}; + auto visitor = [](CXCursor current, CXCursor, CXClientData client_data) + -> CXChildVisitResult { + Range range{current}; + data_t &data = *static_cast(client_data); + if (range.contains(data.start_offset, data.end_offset)) { + if (isCursorPrintable(current)) + data.cursor = current; + return CXChildVisit_Recurse; + } + return range.start_offset >= data.end_offset ? CXChildVisit_Break + : CXChildVisit_Continue; + }; + clang_visitChildren(cursor, visitor, &data); + if (!clang_Cursor_isNull(data.cursor)) + cursor = data.cursor; + } + + std::cout << "(exprtype"; + unsigned numdiag = clang_getNumDiagnostics(tu); + if (numdiag) + std::cout << sexp::alistEntry("diagnostics", numdiag); + std::cout << exprtypeAlist(cursor) << ")" << std::endl; +} diff --git a/server/src/Irony.h b/server/src/Irony.h index 88a593c5..babcdf28 100644 --- a/server/src/Irony.h +++ b/server/src/Irony.h @@ -60,6 +60,21 @@ class Irony { unsigned col, const std::vector &flags, const std::vector &unsavedFiles); + + /// \brief Get type of expression in a given region. + /// + /// FIXME Somewhere in Command.h, the offsets are called line & col. + /// + /// \param start_offset Byte offsets of start of region. + /// + /// \param end_offset Byte offset of end of region. If <= + /// start_offset, then just use the expression at start_offset. + void exprtype(const std::string &file, + unsigned start_offset, + unsigned end_offset, + const std::vector &flags, + const std::vector &unsavedFiles); + /// @} private: diff --git a/server/src/main.cpp b/server/src/main.cpp index f05fa01f..18fb74c5 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp @@ -178,6 +178,10 @@ int main(int ac, const char *av[]) { irony.complete(c->file, c->line, c->column, c->flags, c->cxUnsavedFiles); break; + case Command::ExprType: + irony.exprtype(c->file, c->line, c->column, c->flags, c->cxUnsavedFiles); + break; + case Command::Exit: return 0; diff --git a/server/src/support/Sexp.cpp b/server/src/support/Sexp.cpp new file mode 100644 index 00000000..5b502faf --- /dev/null +++ b/server/src/support/Sexp.cpp @@ -0,0 +1,188 @@ +/** + * \file + * \author Kirill Ignatiev + * \brief Printing libclang AST objects in sexp form. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#include "Sexp.h" +#include "iomanip_quoted.h" + +#include "Sexp_CursorKind.cpp" +#include "Sexp_TypeKind.cpp" + +#include +#include +#include +#include + +namespace sexp { + +/** String representation of a cursor's brief comment. */ +detail::comment_proxy comment(CXCursor cursor) { + return {cursor}; +} + +detail::completion_proxy completion(CXCursor cursor) { + return {clang_getCursorCompletionString(cursor)}; +} + +detail::completion_proxy completion(CXCompletionString completion) { + return {completion}; +} + +namespace detail { + +/** Libclang seems to return empty strings when there is nothing + meaningful to return, so empty strings are printed as nil. */ +std::ostream &operator<<(std::ostream &out, + const sexp_proxy &string) { + const char *data = clang_getCString(string.value); + if (!data) + return out << "nil"; + std::string s = std::string(data); + if (s == "") + return out << "nil"; + return out << support::quoted(s); +} + +std::ostream &operator<<(std::ostream &out, const sexp_proxy &integer) { + if (integer.value == -1) + return out << "nil"; + return out << integer.value; +} + +std::ostream &operator<<(std::ostream &out, const sexp_proxy &boolean) { + return out << (boolean.value ? "t" : "nil"); +} + +std::ostream &operator<<(std::ostream &out, const comment_proxy &proxy) { + CXString comment = clang_Cursor_getBriefCommentText(proxy.cursor); + if (!clang_getCString(comment)) { + CXCursor cursor = clang_getCursorDefinition(proxy.cursor); + comment = clang_Cursor_getBriefCommentText(cursor); + } + if (!clang_getCString(comment)) { + CXCursor cursor = clang_getCursorReferenced(proxy.cursor); + comment = clang_Cursor_getBriefCommentText(cursor); + } + if (clang_getCString(comment)) + out << repr(comment); + else + out << "nil"; + return out; +} + +std::string chunkKind(CXCompletionChunkKind kind) { + static const std::array names{ + {"Optional", + "TypedText", + "Text", + "Placeholder", + "Informative", + "CurrentParameter", + "LeftParen", + "RightParen", + "LeftBracket", + "RightBracket", + "LeftBrace", + "RightBrace", + "LeftAngle", + "RightAngle", + "Comma", + "ResultType", + "Colon", + "SemiColon", + "Equal", + "HorizontalSpace", + "VerticalSpace"}}; + assert(0 <= kind && kind < names.size()); + return names[kind]; +} + +std::ostream &operator<<(std::ostream &out, const completion_proxy &proxy) { + CXCompletionString completion = proxy.completion; + if (!completion) + return out << "nil"; + out << "(completion" + << alistEntry("parent", + repr(clang_getCompletionParent(completion, nullptr))) + << alistEntry("comment", + repr(clang_getCompletionBriefComment(completion))) + << alistEntry("priority", clang_getCompletionPriority(completion)); + + unsigned annot = clang_getCompletionNumAnnotations(completion); + if (annot) { + out << " (annotations"; + for (unsigned i = 0; i < annot; ++i) { + out << " " << repr(clang_getCompletionAnnotation(completion, i)); + } + out << ")"; + } + + static const std::array + chunk_chars = {{0, + 0, + 0, + 0, + 0, + 0, + '(', + ')', + '[', + ']', + '{', + '}', + '<', + '>', + ',', + 0, + ':', + ';', + '=', + ' ', + '\n'}}; + + // We will make the completion chunks presentable later, in lisp + unsigned numchunks = clang_getNumCompletionChunks(completion); + out << " (chunks"; + std::ostringstream s; + for (unsigned i = 0; i < numchunks; ++i) { + auto kind = clang_getCompletionChunkKind(completion, i); + + if (kind < chunk_chars.size() && chunk_chars[kind]) { + s << chunk_chars[kind]; + continue; + } + if (kind == CXCompletionChunk_TypedText) { + CXString text = clang_getCompletionChunkText(completion, i); + s << clang_getCString(text); + clang_disposeString(text); + continue; + } + if (s.tellp()) { + out << " " << support::quoted(s.str()); + s = std::ostringstream(); + } + + out << " (" << chunkKind(kind) << " . "; + if (kind == CXCompletionChunk_Optional) { + out << sexp::completion( + clang_getCompletionChunkCompletionString(completion, i)); + } else { + out << repr(clang_getCompletionChunkText(completion, i)); + } + out << ")"; + } + if (s.tellp()) + out << " " << support::quoted(s.str()); + out << ")"; // chunks + + return out << ")"; +} + +} // namespace detail +} // namespace sexp diff --git a/server/src/support/Sexp.h b/server/src/support/Sexp.h new file mode 100644 index 00000000..f61af203 --- /dev/null +++ b/server/src/support/Sexp.h @@ -0,0 +1,134 @@ +/** -*- mode: C++ -*- + * + * \file + * \author Kirill Ignatiev + * \brief Printing libclang AST objects in sexp form. + * + * This file is distributed under the GNU General Public License. See + * COPYING for details. + */ + +#ifndef IRONYMODE_SERVER_SRC_SUPPORT_SEXP_H_ +#define IRONYMODE_SERVER_SRC_SUPPORT_SEXP_H_ + +#include +#include +#include +#include + +namespace sexp { + +namespace detail { + +template +struct sexp_proxy; +struct comment_proxy; +template +struct alistEntry_proxy; +struct completion_proxy; +} + +/** Return printable representation of value as an emacs-lisp object. + + There is a special implementation for type int that prints -1 as nil. + + When printing a representation of a CXString, the string will be + destroyed after printing (repr takes ownership). +*/ +template +detail::sexp_proxy repr(const T &value) { + return {value}; +} + +/** String representation of a cursor's brief comment. */ +detail::comment_proxy comment(CXCursor); + +/** Completion strings. */ +detail::completion_proxy completion(CXCursor); +detail::completion_proxy completion(CXCompletionString); + +/** Print an alist entry of the form " (name . value)", omitting it if + value is nil, and printing parens nicely if value is a list. + Value is expected to be a single sexp and not a cons pair. +*/ +template +detail::alistEntry_proxy alistEntry(const std::string &name, + const T &value) { + return {name, value}; +} + +// Implementation + +namespace detail { + +template +struct sexp_proxy { + sexp_proxy(T value) : value(value) { + } + T value; +}; + +template <> +struct sexp_proxy { + sexp_proxy(CXString value) : value(value) { + } + ~sexp_proxy() { + clang_disposeString(value); + } + CXString value; +}; + +std::ostream &operator<<(std::ostream &, const sexp_proxy &); +std::ostream &operator<<(std::ostream &, const sexp_proxy &); +std::ostream &operator<<(std::ostream &, const sexp_proxy &); +std::ostream &operator<<(std::ostream &, const sexp_proxy &); +std::ostream &operator<<(std::ostream &, const sexp_proxy &); + +/// Comment information for a given cursor. +struct comment_proxy { + comment_proxy(const CXCursor &cursor) : cursor(cursor) { + } + const CXCursor &cursor; +}; +std::ostream &operator<<(std::ostream &, const comment_proxy &); + +/// See alistEntry. +template +struct alistEntry_proxy { + alistEntry_proxy(std::string name, const T &value) + : name_(name), value_(value) { + } + std::string name_; + const T &value_; +}; + +template +std::ostream &operator<<(std::ostream &out, const alistEntry_proxy &proxy) { + std::ostringstream sstream; + sstream << proxy.value_; + std::string s = sstream.str(); + if (s == "nil") + return out; + if (s[0] == '(' && s[s.size() - 1] == ')') { + int first_nonspace = 0; + while (s[++first_nonspace] == ' ') + ; + out << " (" << proxy.name_ << " " << s.substr(first_nonspace); + } else { + out << " (" << proxy.name_ << " . " << s << ")"; + } + return out; +} + +/// Completion string +struct completion_proxy { + completion_proxy(CXCompletionString completion) : completion(completion) { + } + CXCompletionString completion; +}; +std::ostream &operator<<(std::ostream &, const completion_proxy &); + +} // namespace detail +} // namespace sexp + +#endif // IRONYMODE_SERVER_SRC_SUPPORT_SEXP_H_ diff --git a/server/src/support/Sexp_CursorKind.cpp b/server/src/support/Sexp_CursorKind.cpp new file mode 100644 index 00000000..caece164 --- /dev/null +++ b/server/src/support/Sexp_CursorKind.cpp @@ -0,0 +1,232 @@ +#include "Sexp.h" + +namespace sexp { +namespace detail { + +std::string cursorKindName(CXCursorKind); + +std::ostream& operator<<(std::ostream& out, const sexp_proxy& cursor_kind) { + return out << cursorKindName(cursor_kind.value); +} + +/** + + This is a rather large, automatically generated dump of + CXCursorKind definition, from the source in clang-c/Index.h. + + It is a separate file so that it wouldn't pollute other files. + + NOTE: There are CXCursor_(First|Last)* values; these are not real + and are commented out. + + FIXME Some of these are undefined on my OSX, I don't know why. + + FIXME Others are equal to each other, despite not being obviously + the same. +*/ + +std::string cursorKindName(CXCursorKind cursor_kind) { + switch (cursor_kind) { + case CXCursor_UnexposedDecl: return "UnexposedDecl"; + case CXCursor_StructDecl: return "StructDecl"; + case CXCursor_UnionDecl: return "UnionDecl"; + case CXCursor_ClassDecl: return "ClassDecl"; + case CXCursor_EnumDecl: return "EnumDecl"; + case CXCursor_FieldDecl: return "FieldDecl"; + case CXCursor_EnumConstantDecl: return "EnumConstantDecl"; + case CXCursor_FunctionDecl: return "FunctionDecl"; + case CXCursor_VarDecl: return "VarDecl"; + case CXCursor_ParmDecl: return "ParmDecl"; + case CXCursor_ObjCInterfaceDecl: return "ObjCInterfaceDecl"; + case CXCursor_ObjCCategoryDecl: return "ObjCCategoryDecl"; + case CXCursor_ObjCProtocolDecl: return "ObjCProtocolDecl"; + case CXCursor_ObjCPropertyDecl: return "ObjCPropertyDecl"; + case CXCursor_ObjCIvarDecl: return "ObjCIvarDecl"; + case CXCursor_ObjCInstanceMethodDecl: return "ObjCInstanceMethodDecl"; + case CXCursor_ObjCClassMethodDecl: return "ObjCClassMethodDecl"; + case CXCursor_ObjCImplementationDecl: return "ObjCImplementationDecl"; + case CXCursor_ObjCCategoryImplDecl: return "ObjCCategoryImplDecl"; + case CXCursor_TypedefDecl: return "TypedefDecl"; + case CXCursor_CXXMethod: return "CXXMethod"; + case CXCursor_Namespace: return "Namespace"; + case CXCursor_LinkageSpec: return "LinkageSpec"; + case CXCursor_Constructor: return "Constructor"; + case CXCursor_Destructor: return "Destructor"; + case CXCursor_ConversionFunction: return "ConversionFunction"; + case CXCursor_TemplateTypeParameter: return "TemplateTypeParameter"; + case CXCursor_NonTypeTemplateParameter: return "NonTypeTemplateParameter"; + case CXCursor_TemplateTemplateParameter: return "TemplateTemplateParameter"; + case CXCursor_FunctionTemplate: return "FunctionTemplate"; + case CXCursor_ClassTemplate: return "ClassTemplate"; + case CXCursor_ClassTemplatePartialSpecialization: return "ClassTemplatePartialSpecialization"; + case CXCursor_NamespaceAlias: return "NamespaceAlias"; + case CXCursor_UsingDirective: return "UsingDirective"; + case CXCursor_UsingDeclaration: return "UsingDeclaration"; + case CXCursor_TypeAliasDecl: return "TypeAliasDecl"; + case CXCursor_ObjCSynthesizeDecl: return "ObjCSynthesizeDecl"; + case CXCursor_ObjCDynamicDecl: return "ObjCDynamicDecl"; + case CXCursor_CXXAccessSpecifier: return "CXXAccessSpecifier"; + // case CXCursor_FirstDecl: return "FirstDecl"; + // case CXCursor_LastDecl: return "LastDecl"; + // case CXCursor_FirstRef: return "FirstRef"; + case CXCursor_ObjCSuperClassRef: return "ObjCSuperClassRef"; + case CXCursor_ObjCProtocolRef: return "ObjCProtocolRef"; + case CXCursor_ObjCClassRef: return "ObjCClassRef"; + case CXCursor_TypeRef: return "TypeRef"; + case CXCursor_CXXBaseSpecifier: return "CXXBaseSpecifier"; + case CXCursor_TemplateRef: return "TemplateRef"; + case CXCursor_NamespaceRef: return "NamespaceRef"; + case CXCursor_MemberRef: return "MemberRef"; + case CXCursor_LabelRef: return "LabelRef"; + case CXCursor_OverloadedDeclRef: return "OverloadedDeclRef"; + case CXCursor_VariableRef: return "VariableRef"; + // case CXCursor_LastRef: return "LastRef"; + // case CXCursor_FirstInvalid: return "FirstInvalid"; + case CXCursor_InvalidFile: return "InvalidFile"; + case CXCursor_NoDeclFound: return "NoDeclFound"; + case CXCursor_NotImplemented: return "NotImplemented"; + case CXCursor_InvalidCode: return "InvalidCode"; + // case CXCursor_LastInvalid: return "LastInvalid"; + // case CXCursor_FirstExpr: return "FirstExpr"; + case CXCursor_UnexposedExpr: return "UnexposedExpr"; + case CXCursor_DeclRefExpr: return "DeclRefExpr"; + case CXCursor_MemberRefExpr: return "MemberRefExpr"; + case CXCursor_CallExpr: return "CallExpr"; + case CXCursor_ObjCMessageExpr: return "ObjCMessageExpr"; + case CXCursor_BlockExpr: return "BlockExpr"; + case CXCursor_IntegerLiteral: return "IntegerLiteral"; + case CXCursor_FloatingLiteral: return "FloatingLiteral"; + case CXCursor_ImaginaryLiteral: return "ImaginaryLiteral"; + case CXCursor_StringLiteral: return "StringLiteral"; + case CXCursor_CharacterLiteral: return "CharacterLiteral"; + case CXCursor_ParenExpr: return "ParenExpr"; + case CXCursor_UnaryOperator: return "UnaryOperator"; + case CXCursor_ArraySubscriptExpr: return "ArraySubscriptExpr"; + case CXCursor_BinaryOperator: return "BinaryOperator"; + case CXCursor_CompoundAssignOperator: return "CompoundAssignOperator"; + case CXCursor_ConditionalOperator: return "ConditionalOperator"; + case CXCursor_CStyleCastExpr: return "CStyleCastExpr"; + case CXCursor_CompoundLiteralExpr: return "CompoundLiteralExpr"; + case CXCursor_InitListExpr: return "InitListExpr"; + case CXCursor_AddrLabelExpr: return "AddrLabelExpr"; + case CXCursor_StmtExpr: return "StmtExpr"; + case CXCursor_GenericSelectionExpr: return "GenericSelectionExpr"; + case CXCursor_GNUNullExpr: return "GNUNullExpr"; + case CXCursor_CXXStaticCastExpr: return "CXXStaticCastExpr"; + case CXCursor_CXXDynamicCastExpr: return "CXXDynamicCastExpr"; + case CXCursor_CXXReinterpretCastExpr: return "CXXReinterpretCastExpr"; + case CXCursor_CXXConstCastExpr: return "CXXConstCastExpr"; + case CXCursor_CXXFunctionalCastExpr: return "CXXFunctionalCastExpr"; + case CXCursor_CXXTypeidExpr: return "CXXTypeidExpr"; + case CXCursor_CXXBoolLiteralExpr: return "CXXBoolLiteralExpr"; + case CXCursor_CXXNullPtrLiteralExpr: return "CXXNullPtrLiteralExpr"; + case CXCursor_CXXThisExpr: return "CXXThisExpr"; + case CXCursor_CXXThrowExpr: return "CXXThrowExpr"; + case CXCursor_CXXNewExpr: return "CXXNewExpr"; + case CXCursor_CXXDeleteExpr: return "CXXDeleteExpr"; + case CXCursor_UnaryExpr: return "UnaryExpr"; + case CXCursor_ObjCStringLiteral: return "ObjCStringLiteral"; + case CXCursor_ObjCEncodeExpr: return "ObjCEncodeExpr"; + case CXCursor_ObjCSelectorExpr: return "ObjCSelectorExpr"; + case CXCursor_ObjCProtocolExpr: return "ObjCProtocolExpr"; + case CXCursor_ObjCBridgedCastExpr: return "ObjCBridgedCastExpr"; + case CXCursor_PackExpansionExpr: return "PackExpansionExpr"; + case CXCursor_SizeOfPackExpr: return "SizeOfPackExpr"; + case CXCursor_LambdaExpr: return "LambdaExpr"; + case CXCursor_ObjCBoolLiteralExpr: return "ObjCBoolLiteralExpr"; + case CXCursor_ObjCSelfExpr: return "ObjCSelfExpr"; + // case CXCursor_LastExpr: return "LastExpr"; + // case CXCursor_FirstStmt: return "FirstStmt"; + case CXCursor_UnexposedStmt: return "UnexposedStmt"; + case CXCursor_LabelStmt: return "LabelStmt"; + case CXCursor_CompoundStmt: return "CompoundStmt"; + case CXCursor_CaseStmt: return "CaseStmt"; + case CXCursor_DefaultStmt: return "DefaultStmt"; + case CXCursor_IfStmt: return "IfStmt"; + case CXCursor_SwitchStmt: return "SwitchStmt"; + case CXCursor_WhileStmt: return "WhileStmt"; + case CXCursor_DoStmt: return "DoStmt"; + case CXCursor_ForStmt: return "ForStmt"; + case CXCursor_GotoStmt: return "GotoStmt"; + case CXCursor_IndirectGotoStmt: return "IndirectGotoStmt"; + case CXCursor_ContinueStmt: return "ContinueStmt"; + case CXCursor_BreakStmt: return "BreakStmt"; + case CXCursor_ReturnStmt: return "ReturnStmt"; + case CXCursor_GCCAsmStmt: return "GCCAsmStmt"; + // case CXCursor_AsmStmt: return "AsmStmt"; + case CXCursor_ObjCAtTryStmt: return "ObjCAtTryStmt"; + case CXCursor_ObjCAtCatchStmt: return "ObjCAtCatchStmt"; + case CXCursor_ObjCAtFinallyStmt: return "ObjCAtFinallyStmt"; + case CXCursor_ObjCAtThrowStmt: return "ObjCAtThrowStmt"; + case CXCursor_ObjCAtSynchronizedStmt: return "ObjCAtSynchronizedStmt"; + case CXCursor_ObjCAutoreleasePoolStmt: return "ObjCAutoreleasePoolStmt"; + case CXCursor_ObjCForCollectionStmt: return "ObjCForCollectionStmt"; + case CXCursor_CXXCatchStmt: return "CXXCatchStmt"; + case CXCursor_CXXTryStmt: return "CXXTryStmt"; + case CXCursor_CXXForRangeStmt: return "CXXForRangeStmt"; + case CXCursor_SEHTryStmt: return "SEHTryStmt"; + case CXCursor_SEHExceptStmt: return "SEHExceptStmt"; + case CXCursor_SEHFinallyStmt: return "SEHFinallyStmt"; + case CXCursor_MSAsmStmt: return "MSAsmStmt"; + case CXCursor_NullStmt: return "NullStmt"; + case CXCursor_DeclStmt: return "DeclStmt"; + case CXCursor_OMPParallelDirective: return "OMPParallelDirective"; + case CXCursor_OMPSimdDirective: return "OMPSimdDirective"; + case CXCursor_OMPForDirective: return "OMPForDirective"; + case CXCursor_OMPSectionsDirective: return "OMPSectionsDirective"; + case CXCursor_OMPSectionDirective: return "OMPSectionDirective"; + case CXCursor_OMPSingleDirective: return "OMPSingleDirective"; + case CXCursor_OMPParallelForDirective: return "OMPParallelForDirective"; + case CXCursor_OMPParallelSectionsDirective: return "OMPParallelSectionsDirective"; + case CXCursor_OMPTaskDirective: return "OMPTaskDirective"; + case CXCursor_OMPMasterDirective: return "OMPMasterDirective"; + case CXCursor_OMPCriticalDirective: return "OMPCriticalDirective"; + case CXCursor_OMPTaskyieldDirective: return "OMPTaskyieldDirective"; + case CXCursor_OMPBarrierDirective: return "OMPBarrierDirective"; + case CXCursor_OMPTaskwaitDirective: return "OMPTaskwaitDirective"; + case CXCursor_OMPFlushDirective: return "OMPFlushDirective"; + case CXCursor_SEHLeaveStmt: return "SEHLeaveStmt"; + case CXCursor_OMPOrderedDirective: return "OMPOrderedDirective"; + case CXCursor_OMPAtomicDirective: return "OMPAtomicDirective"; + case CXCursor_OMPForSimdDirective: return "OMPForSimdDirective"; + case CXCursor_OMPParallelForSimdDirective: return "OMPParallelForSimdDirective"; + case CXCursor_OMPTargetDirective: return "OMPTargetDirective"; + case CXCursor_OMPTeamsDirective: return "OMPTeamsDirective"; + // case CXCursor_LastStmt: return "LastStmt"; + case CXCursor_TranslationUnit: return "TranslationUnit"; + // case CXCursor_FirstAttr: return "FirstAttr"; + case CXCursor_UnexposedAttr: return "UnexposedAttr"; + case CXCursor_IBActionAttr: return "IBActionAttr"; + case CXCursor_IBOutletAttr: return "IBOutletAttr"; + case CXCursor_IBOutletCollectionAttr: return "IBOutletCollectionAttr"; + case CXCursor_CXXFinalAttr: return "CXXFinalAttr"; + case CXCursor_CXXOverrideAttr: return "CXXOverrideAttr"; + case CXCursor_AnnotateAttr: return "AnnotateAttr"; + case CXCursor_AsmLabelAttr: return "AsmLabelAttr"; + case CXCursor_PackedAttr: return "PackedAttr"; + case CXCursor_PureAttr: return "PureAttr"; + case CXCursor_ConstAttr: return "ConstAttr"; + case CXCursor_NoDuplicateAttr: return "NoDuplicateAttr"; + case CXCursor_CUDAConstantAttr: return "CUDAConstantAttr"; + case CXCursor_CUDADeviceAttr: return "CUDADeviceAttr"; + case CXCursor_CUDAGlobalAttr: return "CUDAGlobalAttr"; + case CXCursor_CUDAHostAttr: return "CUDAHostAttr"; + case CXCursor_CUDASharedAttr: return "CUDASharedAttr"; + // case CXCursor_LastAttr: return "LastAttr"; + case CXCursor_PreprocessingDirective: return "PreprocessingDirective"; + case CXCursor_MacroDefinition: return "MacroDefinition"; + case CXCursor_MacroExpansion: return "MacroExpansion"; + // NOTE duplicate + // case CXCursor_MacroInstantiation: return "MacroInstantiation"; + case CXCursor_InclusionDirective: return "InclusionDirective"; + // case CXCursor_FirstPreprocessing: return "FirstPreprocessing"; + // case CXCursor_LastPreprocessing: return "LastPreprocessing"; + case CXCursor_ModuleImportDecl: return "ModuleImportDecl"; + // case CXCursor_FirstExtraDecl: return "FirstExtraDecl"; + // case CXCursor_LastExtraDecl: return "LastExtraDecl"; + default: return "UnknownCursorKind"; + } +} + +} // namespace detail +} // namespace sexp diff --git a/server/src/support/Sexp_TypeKind.cpp b/server/src/support/Sexp_TypeKind.cpp new file mode 100644 index 00000000..ed78fd2e --- /dev/null +++ b/server/src/support/Sexp_TypeKind.cpp @@ -0,0 +1,73 @@ +#include "Sexp.h" + +namespace sexp { +namespace detail { + +/** + One big switch statement for pretty-printing CXTypeKind. + */ + +std::string typeKindName(CXTypeKind type_kind) { + switch (type_kind) { + case CXType_Invalid: return "Invalid"; + case CXType_Unexposed: return "Unexposed"; + case CXType_Void: return "Void"; + case CXType_Bool: return "Bool"; + case CXType_Char_U: return "U"; + case CXType_UChar: return "UChar"; + case CXType_Char16: return "Char16"; + case CXType_Char32: return "Char32"; + case CXType_UShort: return "UShort"; + case CXType_UInt: return "UInt"; + case CXType_ULong: return "ULong"; + case CXType_ULongLong: return "ULongLong"; + case CXType_UInt128: return "UInt128"; + case CXType_Char_S: return "S"; + case CXType_SChar: return "SChar"; + case CXType_WChar: return "WChar"; + case CXType_Short: return "Short"; + case CXType_Int: return "Int"; + case CXType_Long: return "Long"; + case CXType_LongLong: return "LongLong"; + case CXType_Int128: return "Int128"; + case CXType_Float: return "Float"; + case CXType_Double: return "Double"; + case CXType_LongDouble: return "LongDouble"; + case CXType_NullPtr: return "NullPtr"; + case CXType_Overload: return "Overload"; + case CXType_Dependent: return "Dependent"; + case CXType_ObjCId: return "ObjCId"; + case CXType_ObjCClass: return "ObjCClass"; + case CXType_ObjCSel: return "ObjCSel"; + // case CXType_FirstBuiltin: return "FirstBuiltin"; + // case CXType_LastBuiltin = case CXType_ObjCSel,; + case CXType_Complex: return "Complex"; + case CXType_Pointer: return "Pointer"; + case CXType_BlockPointer: return "BlockPointer"; + case CXType_LValueReference: return "LValueReference"; + case CXType_RValueReference: return "RValueReference"; + case CXType_Record: return "Record"; + case CXType_Enum: return "Enum"; + case CXType_Typedef: return "Typedef"; + case CXType_ObjCInterface: return "ObjCInterface"; + case CXType_ObjCObjectPointer: return "ObjCObjectPointer"; + case CXType_FunctionNoProto: return "FunctionNoProto"; + case CXType_FunctionProto: return "FunctionProto"; + case CXType_ConstantArray: return "ConstantArray"; + case CXType_Vector: return "Vector"; + case CXType_IncompleteArray: return "IncompleteArray"; + case CXType_VariableArray: return "VariableArray"; + case CXType_DependentSizedArray: return "DependentSizedArray"; + case CXType_MemberPointer: return "MemberPointer"; + default: return "UnknownCXTypeKind"; + } +} + +std::ostream& operator<<(std::ostream& out, + const sexp_proxy& type_kind) +{ + return out << typeKindName(type_kind.value); +} + +} // namespace detail +} // namespace sexp