c-index-test.c revision f8297f1512d29c88f20bf2901f04cc04182a3eee
1/* c-index-test.c */
2
3#include "clang-c/Index.h"
4#include <stdlib.h>
5#include <stdio.h>
6#include <string.h>
7
8#ifdef _MSC_VER
9char *basename(const char* path)
10{
11    char* base1 = (char*)strrchr(path, '/');
12    char* base2 = (char*)strrchr(path, '\\');
13    if (base1 && base2)
14        return((base1 > base2) ? base1 + 1 : base2 + 1);
15    else if (base1)
16        return(base1 + 1);
17    else if (base2)
18        return(base2 + 1);
19
20    return((char*)path);
21}
22#else
23extern char *basename(const char *);
24#endif
25
26static void PrintCursor(CXCursor Cursor) {
27  if (clang_isInvalid(Cursor.kind))
28    printf("Invalid Cursor => %s\n", clang_getCursorKindSpelling(Cursor.kind));
29  else {
30    CXDecl DeclReferenced;
31    printf("%s=%s", clang_getCursorKindSpelling(Cursor.kind),
32                      clang_getCursorSpelling(Cursor));
33    DeclReferenced = clang_getCursorDecl(Cursor);
34    if (DeclReferenced)
35      printf(":%d:%d", clang_getDeclLine(DeclReferenced),
36                       clang_getDeclColumn(DeclReferenced));
37  }
38}
39
40static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter)
41{
42  if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) {
43    printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Cursor)),
44                                 clang_getCursorLine(Cursor),
45                                 clang_getCursorColumn(Cursor));
46    PrintCursor(Cursor);
47    printf(" [Context=%s]\n", clang_getDeclSpelling(Dcl));
48  }
49}
50static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor,
51                                   CXClientData Filter)
52{
53  if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) {
54    printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Cursor)),
55                                 clang_getCursorLine(Cursor),
56                                 clang_getCursorColumn(Cursor));
57    PrintCursor(Cursor);
58    printf(" [Context=%s]\n", basename(clang_getTranslationUnitSpelling(Unit)));
59
60    clang_loadDeclaration(Cursor.decl, DeclVisitor, 0);
61
62    if (Cursor.kind == CXCursor_FunctionDefn) {
63      const char *startBuf, *endBuf;
64      unsigned startLine, startColumn, endLine, endColumn;
65      clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf,
66                                           &startLine, &startColumn,
67                                           &endLine, &endColumn);
68      {
69        /* Probe the entire body, looking for both decls and refs. */
70        unsigned curLine = startLine, curColumn = startColumn;
71        CXCursor Ref;
72
73        while (startBuf < endBuf) {
74          if (*startBuf == '\n') {
75            startBuf++;
76            curLine++;
77            curColumn = 1;
78          } else if (*startBuf != '\t')
79            curColumn++;
80
81          Ref = clang_getCursor(Unit, clang_getCursorSource(Cursor),
82                                curLine, curColumn);
83          if (Ref.kind == CXCursor_NoDeclFound) {
84            /* Nothing found here; that's fine. */
85          } else if (Ref.kind != CXCursor_FunctionDecl) {
86            printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Ref)),
87                                             curLine, curColumn);
88            PrintCursor(Ref);
89            printf(" [Context:%s]\n", clang_getDeclSpelling(Ref.decl));
90          }
91          startBuf++;
92        }
93      }
94    }
95  }
96}
97
98/* Parse file:line:column from the input string. Returns 0 on success, non-zero
99   on failure. If successful, the pointer *filename will contain newly-allocated
100   memory (that will be owned by the caller) to store the file name. */
101int parse_file_line_column(const char *input, char **filename, unsigned *line,
102                           unsigned *column) {
103  const char *colon = strchr(input, ':');
104  char *endptr = 0;
105  if (!colon) {
106    fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
107    return 1;
108  }
109
110  /* Copy the file name. */
111  *filename = (char*)malloc(colon - input);
112  strncpy(*filename, input, colon - input);
113  (*filename)[colon - input] = 0;
114  input = colon + 1;
115
116  /* Parse the line number. */
117  *line = strtol(input, &endptr, 10);
118  if (*endptr != ':') {
119    fprintf(stderr, "could not parse line:column in '%s'\n", input);
120    free(filename);
121    *filename = 0;
122    return 1;
123  }
124  input = endptr + 1;
125
126  /* Parse the column number. */
127  *column = strtol(input, &endptr, 10);
128  if (*endptr != 0) {
129    fprintf(stderr, "could not parse column in '%s'\n", input);
130    free(filename);
131    *filename = 0;
132    return 1;
133  }
134
135  return 0;
136}
137
138const char *
139clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
140  switch (Kind) {
141  case CXCompletionChunk_Optional: return "Optional";
142  case CXCompletionChunk_TypedText: return "TypedText";
143  case CXCompletionChunk_Text: return "Text";
144  case CXCompletionChunk_Placeholder: return "Placeholder";
145  case CXCompletionChunk_Informative: return "Informative";
146  case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
147  case CXCompletionChunk_LeftParen: return "LeftParen";
148  case CXCompletionChunk_RightParen: return "RightParen";
149  case CXCompletionChunk_LeftBracket: return "LeftBracket";
150  case CXCompletionChunk_RightBracket: return "RightBracket";
151  case CXCompletionChunk_LeftBrace: return "LeftBrace";
152  case CXCompletionChunk_RightBrace: return "RightBrace";
153  case CXCompletionChunk_LeftAngle: return "LeftAngle";
154  case CXCompletionChunk_RightAngle: return "RightAngle";
155  case CXCompletionChunk_Comma: return "Comma";
156  }
157
158  return "Unknown";
159}
160
161void print_completion_result(CXCompletionResult *completion_result,
162                             CXClientData client_data) {
163  FILE *file = (FILE *)client_data;
164  int I, N;
165
166  fprintf(file, "%s:",
167          clang_getCursorKindSpelling(completion_result->CursorKind));
168  N = clang_getNumCompletionChunks(completion_result->CompletionString);
169  for (I = 0; I != N; ++I) {
170    const char *text
171      = clang_getCompletionChunkText(completion_result->CompletionString, I);
172
173    enum CXCompletionChunkKind Kind
174      = clang_getCompletionChunkKind(completion_result->CompletionString, I);
175    fprintf(file, "{%s %s}",
176            clang_getCompletionChunkKindSpelling(Kind),
177            text? text : "");
178  }
179  fprintf(file, "\n");
180}
181
182void perform_code_completion(int argc, const char **argv) {
183  const char *input = argv[1];
184  char *filename = 0;
185  unsigned line;
186  unsigned column;
187  CXIndex CIdx;
188
189  input += strlen("-code-completion-at=");
190  if (parse_file_line_column(input, &filename, &line, &column))
191    return;
192
193  CIdx = clang_createIndex(0, 0);
194  clang_codeComplete(CIdx, argv[argc - 1], argc - 3, argv + 2,
195                     filename, line, column, &print_completion_result, stdout);
196  clang_disposeIndex(CIdx);
197  free(filename);
198}
199
200/*
201 * First sign of life:-)
202 */
203int main(int argc, char **argv) {
204  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) {
205    perform_code_completion(argc, (const char **)argv);
206    return 0;
207  }
208
209
210  if (argc != 3) {
211    printf("Incorrect usage of c-index-test (requires 3 arguments)\n");
212    return 0;
213  }
214  {
215  CXIndex Idx;
216  CXTranslationUnit TU;
217  enum CXCursorKind K = CXCursor_NotImplemented;
218
219  Idx = clang_createIndex(/* excludeDeclsFromPCH */ !strcmp(argv[2], "local") ? 1 : 0,
220                          /* displayDiagnostics */ 1);
221
222  TU = clang_createTranslationUnit(Idx, argv[1]);
223
224  if (!TU) {
225    fprintf(stderr, "Unable to load translation unit!\n");
226    return 1;
227  }
228
229  if (!strcmp(argv[2], "all") || !strcmp(argv[2], "local")) {
230    clang_loadTranslationUnit(TU, TranslationUnitVisitor, 0);
231    clang_disposeTranslationUnit(TU);
232    return 1;
233  }
234  /* Perform some simple filtering. */
235  if (!strcmp(argv[2], "category")) K = CXCursor_ObjCCategoryDecl;
236  else if (!strcmp(argv[2], "interface")) K = CXCursor_ObjCInterfaceDecl;
237  else if (!strcmp(argv[2], "protocol")) K = CXCursor_ObjCProtocolDecl;
238  else if (!strcmp(argv[2], "function")) K = CXCursor_FunctionDecl;
239  else if (!strcmp(argv[2], "typedef")) K = CXCursor_TypedefDecl;
240
241  clang_loadTranslationUnit(TU, TranslationUnitVisitor, &K);
242  clang_disposeTranslationUnit(TU);
243  return 1;
244  }
245}
246