blob: 98ed772ec6e8aefc52e380b76d97eae3ed4f43a9 [file] [log] [blame]
Steve Naroff69b10fd2009-09-01 15:55:40 +00001/* c-index-test.c */
Steve Naroffa1c72842009-08-28 15:28:48 +00002
3#include "clang-c/Index.h"
Douglas Gregor9eb77012009-11-07 00:00:49 +00004#include <stdlib.h>
Steve Naroff1054e602009-08-31 00:59:03 +00005#include <stdio.h>
Steve Naroff38c1a7b2009-09-03 15:49:00 +00006#include <string.h>
Douglas Gregor082c3e62010-01-15 19:40:17 +00007#include <assert.h>
Steve Naroff38c1a7b2009-09-03 15:49:00 +00008
Ted Kremenek1cd27d52009-11-17 18:13:31 +00009/******************************************************************************/
10/* Utility functions. */
11/******************************************************************************/
12
John Thompsonde258b52009-10-27 13:42:56 +000013#ifdef _MSC_VER
14char *basename(const char* path)
15{
16 char* base1 = (char*)strrchr(path, '/');
17 char* base2 = (char*)strrchr(path, '\\');
18 if (base1 && base2)
19 return((base1 > base2) ? base1 + 1 : base2 + 1);
20 else if (base1)
21 return(base1 + 1);
22 else if (base2)
23 return(base2 + 1);
24
25 return((char*)path);
26}
27#else
Steve Naroffa7753c42009-09-24 20:03:06 +000028extern char *basename(const char *);
John Thompsonde258b52009-10-27 13:42:56 +000029#endif
Steve Naroffa7753c42009-09-24 20:03:06 +000030
Ted Kremenek2df52dc2009-11-17 19:37:36 +000031static unsigned CreateTranslationUnit(CXIndex Idx, const char *file,
32 CXTranslationUnit *TU) {
33
34 *TU = clang_createTranslationUnit(Idx, file);
35 if (!TU) {
36 fprintf(stderr, "Unable to load translation unit from '%s'!\n", file);
37 return 0;
38 }
39 return 1;
40}
41
Ted Kremenek1cd27d52009-11-17 18:13:31 +000042/******************************************************************************/
43/* Pretty-printing. */
44/******************************************************************************/
45
Steve Naroff38c1a7b2009-09-03 15:49:00 +000046static void PrintCursor(CXCursor Cursor) {
Steve Naroff54f22fb2009-09-15 20:25:34 +000047 if (clang_isInvalid(Cursor.kind))
Ted Kremenek2df52dc2009-11-17 19:37:36 +000048 printf("Invalid Cursor => %s", clang_getCursorKindSpelling(Cursor.kind));
Steve Naroff63f475a2009-09-25 21:32:34 +000049 else {
Steve Naroff8675d5c2009-11-09 17:45:52 +000050 CXString string;
Douglas Gregorad27e8b2010-01-19 01:20:04 +000051 CXCursor Referenced;
Douglas Gregor4f46e782010-01-19 21:36:55 +000052 unsigned line, column;
Steve Naroff8675d5c2009-11-09 17:45:52 +000053 string = clang_getCursorSpelling(Cursor);
Steve Naroffa7753c42009-09-24 20:03:06 +000054 printf("%s=%s", clang_getCursorKindSpelling(Cursor.kind),
Steve Naroff8675d5c2009-11-09 17:45:52 +000055 clang_getCString(string));
56 clang_disposeString(string);
Douglas Gregorad27e8b2010-01-19 01:20:04 +000057
58 Referenced = clang_getCursorReferenced(Cursor);
59 if (!clang_equalCursors(Referenced, clang_getNullCursor())) {
60 CXSourceLocation Loc = clang_getCursorLocation(Referenced);
Douglas Gregor4f46e782010-01-19 21:36:55 +000061 clang_getInstantiationLocation(Loc, 0, &line, &column);
62 printf(":%d:%d", line, column);
Douglas Gregorad27e8b2010-01-19 01:20:04 +000063 }
Douglas Gregor6b8232f2010-01-19 19:34:47 +000064
65 if (clang_isCursorDefinition(Cursor))
66 printf(" (Definition)");
Steve Naroff63f475a2009-09-25 21:32:34 +000067 }
Steve Naroff38c1a7b2009-09-03 15:49:00 +000068}
Steve Naroff1054e602009-08-31 00:59:03 +000069
Ted Kremenek4c4d6432009-11-17 05:31:58 +000070static const char* GetCursorSource(CXCursor Cursor) {
Douglas Gregor4f46e782010-01-19 21:36:55 +000071 CXSourceLocation Loc = clang_getCursorLocation(Cursor);
72 const char *source;
73 CXFile file;
74 clang_getInstantiationLocation(Loc, &file, 0, 0);
75 source = clang_getFileName(file);
Ted Kremenek4c4d6432009-11-17 05:31:58 +000076 if (!source)
77 return "<invalid loc>";
78 return basename(source);
79}
80
Ted Kremenek1cd27d52009-11-17 18:13:31 +000081/******************************************************************************/
Douglas Gregor720d0052010-01-20 21:32:04 +000082/* Logic for testing traversal. */
Ted Kremenek1cd27d52009-11-17 18:13:31 +000083/******************************************************************************/
84
Ted Kremeneka44d99c2010-01-05 23:18:49 +000085static const char *FileCheckPrefix = "CHECK";
86
Douglas Gregor33c34ac2010-01-19 00:34:46 +000087static void PrintCursorExtent(CXCursor C) {
88 CXSourceRange extent = clang_getCursorExtent(C);
Douglas Gregor4f46e782010-01-19 21:36:55 +000089 CXFile begin_file, end_file;
90 unsigned begin_line, begin_column, end_line, end_column;
91
92 clang_getInstantiationLocation(clang_getRangeStart(extent),
93 &begin_file, &begin_line, &begin_column);
94 clang_getInstantiationLocation(clang_getRangeEnd(extent),
95 &end_file, &end_line, &end_column);
96 if (!begin_file || !end_file)
Ted Kremenek9cec0002010-01-16 01:44:12 +000097 return;
Douglas Gregor4f46e782010-01-19 21:36:55 +000098
99 printf(" [Extent=%d:%d:%d:%d]", begin_line, begin_column,
100 end_line, end_column);
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000101}
102
Douglas Gregor720d0052010-01-20 21:32:04 +0000103/* Data used by all of the visitors. */
104typedef struct {
105 CXTranslationUnit TU;
106 enum CXCursorKind *Filter;
107} VisitorData;
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000108
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000109
Douglas Gregor720d0052010-01-20 21:32:04 +0000110enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor,
111 CXCursor Parent,
112 CXClientData ClientData) {
113 VisitorData *Data = (VisitorData *)ClientData;
114 if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) {
Douglas Gregor66a58812010-01-18 22:46:11 +0000115 CXSourceLocation Loc = clang_getCursorLocation(Cursor);
Douglas Gregor4f46e782010-01-19 21:36:55 +0000116 unsigned line, column;
117 clang_getInstantiationLocation(Loc, 0, &line, &column);
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000118 printf("// %s: %s:%d:%d: ", FileCheckPrefix,
Douglas Gregor4f46e782010-01-19 21:36:55 +0000119 GetCursorSource(Cursor), line, column);
Steve Naroff38c1a7b2009-09-03 15:49:00 +0000120 PrintCursor(Cursor);
Douglas Gregor33c34ac2010-01-19 00:34:46 +0000121 PrintCursorExtent(Cursor);
Ted Kremenek9cec0002010-01-16 01:44:12 +0000122 printf("\n");
Douglas Gregor720d0052010-01-20 21:32:04 +0000123 return CXChildVisit_Recurse;
Steve Naroff772c1a42009-08-31 14:26:51 +0000124 }
Douglas Gregor720d0052010-01-20 21:32:04 +0000125
126 return CXChildVisit_Continue;
Steve Naroff1054e602009-08-31 00:59:03 +0000127}
Steve Naroffa1c72842009-08-28 15:28:48 +0000128
Douglas Gregor720d0052010-01-20 21:32:04 +0000129static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor,
130 CXCursor Parent,
131 CXClientData ClientData) {
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000132 const char *startBuf, *endBuf;
133 unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn;
134 CXCursor Ref;
Douglas Gregor720d0052010-01-20 21:32:04 +0000135 VisitorData *Data = (VisitorData *)ClientData;
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000136
Douglas Gregor6b8232f2010-01-19 19:34:47 +0000137 if (Cursor.kind != CXCursor_FunctionDecl ||
138 !clang_isCursorDefinition(Cursor))
Douglas Gregor720d0052010-01-20 21:32:04 +0000139 return CXChildVisit_Continue;
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000140
141 clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf,
142 &startLine, &startColumn,
143 &endLine, &endColumn);
144 /* Probe the entire body, looking for both decls and refs. */
145 curLine = startLine;
146 curColumn = startColumn;
147
148 while (startBuf < endBuf) {
Douglas Gregor66a58812010-01-18 22:46:11 +0000149 CXSourceLocation Loc;
Douglas Gregor4f46e782010-01-19 21:36:55 +0000150 CXFile file;
Douglas Gregor66a58812010-01-18 22:46:11 +0000151 const char *source = 0;
152
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000153 if (*startBuf == '\n') {
154 startBuf++;
155 curLine++;
156 curColumn = 1;
157 } else if (*startBuf != '\t')
158 curColumn++;
159
Douglas Gregor66a58812010-01-18 22:46:11 +0000160 Loc = clang_getCursorLocation(Cursor);
Douglas Gregor4f46e782010-01-19 21:36:55 +0000161 clang_getInstantiationLocation(Loc, &file, 0, 0);
162 source = clang_getFileName(file);
Douglas Gregor66a58812010-01-18 22:46:11 +0000163 if (source) {
Douglas Gregor816fd362010-01-22 21:44:22 +0000164 CXSourceLocation RefLoc
165 = clang_getLocation(Data->TU, file, curLine, curColumn);
166 Ref = clang_getCursor(Data->TU, RefLoc);
Douglas Gregor66a58812010-01-18 22:46:11 +0000167 if (Ref.kind == CXCursor_NoDeclFound) {
168 /* Nothing found here; that's fine. */
169 } else if (Ref.kind != CXCursor_FunctionDecl) {
170 printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref),
171 curLine, curColumn);
172 PrintCursor(Ref);
173 printf("\n");
174 }
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000175 }
176 startBuf++;
177 }
Douglas Gregor720d0052010-01-20 21:32:04 +0000178
179 return CXChildVisit_Continue;
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000180}
181
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000182/******************************************************************************/
183/* USR testing. */
184/******************************************************************************/
185
Douglas Gregor720d0052010-01-20 21:32:04 +0000186enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent,
187 CXClientData ClientData) {
188 VisitorData *Data = (VisitorData *)ClientData;
189 if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) {
Ted Kremenek473c7a72010-01-18 20:23:29 +0000190 CXString USR = clang_getCursorUSR(C);
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000191 if (!USR.Spelling) {
192 clang_disposeString(USR);
Douglas Gregor720d0052010-01-20 21:32:04 +0000193 return CXChildVisit_Continue;
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000194 }
195 printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), USR.Spelling);
Douglas Gregor33c34ac2010-01-19 00:34:46 +0000196 PrintCursorExtent(C);
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000197 printf("\n");
198 clang_disposeString(USR);
Douglas Gregor720d0052010-01-20 21:32:04 +0000199
200 return CXChildVisit_Recurse;
201 }
202
203 return CXChildVisit_Continue;
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000204}
205
206/******************************************************************************/
207/* Loading ASTs/source. */
208/******************************************************************************/
209
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000210static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
Ted Kremenek73eccd22010-01-12 18:53:15 +0000211 const char *filter, const char *prefix,
Douglas Gregor720d0052010-01-20 21:32:04 +0000212 CXCursorVisitor Visitor) {
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000213 enum CXCursorKind K = CXCursor_NotImplemented;
214 enum CXCursorKind *ck = &K;
Douglas Gregor720d0052010-01-20 21:32:04 +0000215 VisitorData Data;
216
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000217 if (prefix)
218 FileCheckPrefix = prefix;
219
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000220 /* Perform some simple filtering. */
221 if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL;
222 else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl;
223 else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl;
224 else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl;
225 else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl;
226 else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl;
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000227 else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor;
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000228 else {
229 fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter);
230 return 1;
231 }
232
Douglas Gregor720d0052010-01-20 21:32:04 +0000233 Data.TU = TU;
234 Data.Filter = ck;
Douglas Gregorfed36b12010-01-20 23:57:43 +0000235 clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data);
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000236 clang_disposeTranslationUnit(TU);
237 return 0;
238}
239
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000240int perform_test_load_tu(const char *file, const char *filter,
Ted Kremenek73eccd22010-01-12 18:53:15 +0000241 const char *prefix,
Douglas Gregor720d0052010-01-20 21:32:04 +0000242 CXCursorVisitor Visitor) {
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000243 CXIndex Idx;
244 CXTranslationUnit TU;
245 Idx = clang_createIndex(/* excludeDeclsFromPCH */
246 !strcmp(filter, "local") ? 1 : 0,
247 /* displayDiagnostics */ 1);
248
249 if (!CreateTranslationUnit(Idx, file, &TU))
250 return 1;
251
Ted Kremenek73eccd22010-01-12 18:53:15 +0000252 return perform_test_load(Idx, TU, filter, prefix, Visitor);
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000253}
254
Ted Kremenek73eccd22010-01-12 18:53:15 +0000255int perform_test_load_source(int argc, const char **argv, const char *filter,
Douglas Gregor720d0052010-01-20 21:32:04 +0000256 CXCursorVisitor Visitor) {
Daniel Dunbar11089662009-12-03 01:54:28 +0000257 const char *UseExternalASTs =
258 getenv("CINDEXTEST_USE_EXTERNAL_AST_GENERATION");
Daniel Dunbar3e535d72009-12-01 02:03:10 +0000259 CXIndex Idx;
260 CXTranslationUnit TU;
Daniel Dunbar3e535d72009-12-01 02:03:10 +0000261 Idx = clang_createIndex(/* excludeDeclsFromPCH */
262 !strcmp(filter, "local") ? 1 : 0,
263 /* displayDiagnostics */ 1);
264
Daniel Dunbar11089662009-12-03 01:54:28 +0000265 if (UseExternalASTs && strlen(UseExternalASTs))
266 clang_setUseExternalASTGeneration(Idx, 1);
267
Daniel Dunbar3e535d72009-12-01 02:03:10 +0000268 TU = clang_createTranslationUnitFromSourceFile(Idx, 0, argc, argv);
269 if (!TU) {
270 fprintf(stderr, "Unable to load translation unit!\n");
271 return 1;
272 }
273
Ted Kremenek73eccd22010-01-12 18:53:15 +0000274 return perform_test_load(Idx, TU, filter, NULL, Visitor);
Daniel Dunbar3e535d72009-12-01 02:03:10 +0000275}
276
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000277/******************************************************************************/
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000278/* Logic for testing clang_getCursor(). */
279/******************************************************************************/
280
281static void print_cursor_file_scan(CXCursor cursor,
282 unsigned start_line, unsigned start_col,
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000283 unsigned end_line, unsigned end_col,
284 const char *prefix) {
Ted Kremenekb58514e2010-01-07 01:17:12 +0000285 printf("// %s: ", FileCheckPrefix);
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000286 if (prefix)
287 printf("-%s", prefix);
288 printf("{start_line=%d start_col=%d end_line=%d end_col=%d} ",
289 start_line, start_col, end_line, end_col);
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000290 PrintCursor(cursor);
291 printf("\n");
292}
293
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000294static int perform_file_scan(const char *ast_file, const char *source_file,
295 const char *prefix) {
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000296 CXIndex Idx;
297 CXTranslationUnit TU;
298 FILE *fp;
299 unsigned line;
300 CXCursor prevCursor;
Douglas Gregor816fd362010-01-22 21:44:22 +0000301 CXFile file;
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000302 unsigned printed;
303 unsigned start_line, start_col, last_line, last_col;
304 size_t i;
305
306 if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
307 /* displayDiagnostics */ 1))) {
308 fprintf(stderr, "Could not create Index\n");
309 return 1;
310 }
311
312 if (!CreateTranslationUnit(Idx, ast_file, &TU))
313 return 1;
314
315 if ((fp = fopen(source_file, "r")) == NULL) {
316 fprintf(stderr, "Could not open '%s'\n", source_file);
317 return 1;
318 }
319
320 line = 0;
321 prevCursor = clang_getNullCursor();
322 printed = 0;
323 start_line = last_line = 1;
324 start_col = last_col = 1;
325
Douglas Gregor816fd362010-01-22 21:44:22 +0000326 file = clang_getFile(TU, source_file);
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000327 while (!feof(fp)) {
Benjamin Kramer10d083172009-11-17 20:51:40 +0000328 size_t len = 0;
329 int c;
330
331 while ((c = fgetc(fp)) != EOF) {
332 len++;
333 if (c == '\n')
334 break;
335 }
336
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000337 ++line;
338
339 for (i = 0; i < len ; ++i) {
340 CXCursor cursor;
Douglas Gregor816fd362010-01-22 21:44:22 +0000341 cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, i+1));
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000342
343 if (!clang_equalCursors(cursor, prevCursor) &&
344 prevCursor.kind != CXCursor_InvalidFile) {
345 print_cursor_file_scan(prevCursor, start_line, start_col,
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000346 last_line, last_col, prefix);
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000347 printed = 1;
348 start_line = line;
349 start_col = (unsigned) i+1;
350 }
351 else {
352 printed = 0;
353 }
354
355 prevCursor = cursor;
356 last_line = line;
357 last_col = (unsigned) i+1;
358 }
359 }
360
361 if (!printed && prevCursor.kind != CXCursor_InvalidFile) {
362 print_cursor_file_scan(prevCursor, start_line, start_col,
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000363 last_line, last_col, prefix);
Ted Kremenek2df52dc2009-11-17 19:37:36 +0000364 }
365
366 fclose(fp);
367 return 0;
368}
369
370/******************************************************************************/
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000371/* Logic for testing clang_codeComplete(). */
372/******************************************************************************/
373
Douglas Gregor9eb77012009-11-07 00:00:49 +0000374/* Parse file:line:column from the input string. Returns 0 on success, non-zero
375 on failure. If successful, the pointer *filename will contain newly-allocated
376 memory (that will be owned by the caller) to store the file name. */
377int parse_file_line_column(const char *input, char **filename, unsigned *line,
378 unsigned *column) {
Douglas Gregorf96ea292009-11-09 18:19:57 +0000379 /* Find the second colon. */
380 const char *second_colon = strrchr(input, ':'), *first_colon;
Douglas Gregor9eb77012009-11-07 00:00:49 +0000381 char *endptr = 0;
Douglas Gregorf96ea292009-11-09 18:19:57 +0000382 if (!second_colon || second_colon == input) {
Douglas Gregor9eb77012009-11-07 00:00:49 +0000383 fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
384 return 1;
385 }
386
Douglas Gregor9eb77012009-11-07 00:00:49 +0000387 /* Parse the column number. */
Douglas Gregorf96ea292009-11-09 18:19:57 +0000388 *column = strtol(second_colon + 1, &endptr, 10);
Douglas Gregor9eb77012009-11-07 00:00:49 +0000389 if (*endptr != 0) {
390 fprintf(stderr, "could not parse column in '%s'\n", input);
Douglas Gregorf96ea292009-11-09 18:19:57 +0000391 return 1;
392 }
393
394 /* Find the first colon. */
395 first_colon = second_colon - 1;
396 while (first_colon != input && *first_colon != ':')
397 --first_colon;
398 if (first_colon == input) {
399 fprintf(stderr, "could not parse line in '%s'\n", input);
400 return 1;
401 }
402
403 /* Parse the line number. */
404 *line = strtol(first_colon + 1, &endptr, 10);
405 if (*endptr != ':') {
406 fprintf(stderr, "could not parse line in '%s'\n", input);
Douglas Gregor9eb77012009-11-07 00:00:49 +0000407 return 1;
408 }
409
Douglas Gregorf96ea292009-11-09 18:19:57 +0000410 /* Copy the file name. */
411 *filename = (char*)malloc(first_colon - input + 1);
412 memcpy(*filename, input, first_colon - input);
413 (*filename)[first_colon - input] = 0;
Douglas Gregor9eb77012009-11-07 00:00:49 +0000414 return 0;
415}
416
417const char *
418clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
419 switch (Kind) {
420 case CXCompletionChunk_Optional: return "Optional";
421 case CXCompletionChunk_TypedText: return "TypedText";
422 case CXCompletionChunk_Text: return "Text";
423 case CXCompletionChunk_Placeholder: return "Placeholder";
424 case CXCompletionChunk_Informative: return "Informative";
425 case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
426 case CXCompletionChunk_LeftParen: return "LeftParen";
427 case CXCompletionChunk_RightParen: return "RightParen";
428 case CXCompletionChunk_LeftBracket: return "LeftBracket";
429 case CXCompletionChunk_RightBracket: return "RightBracket";
430 case CXCompletionChunk_LeftBrace: return "LeftBrace";
431 case CXCompletionChunk_RightBrace: return "RightBrace";
432 case CXCompletionChunk_LeftAngle: return "LeftAngle";
433 case CXCompletionChunk_RightAngle: return "RightAngle";
434 case CXCompletionChunk_Comma: return "Comma";
Douglas Gregorb3fa9192009-12-18 18:53:37 +0000435 case CXCompletionChunk_ResultType: return "ResultType";
Douglas Gregor504a6ae2010-01-10 23:08:15 +0000436 case CXCompletionChunk_Colon: return "Colon";
437 case CXCompletionChunk_SemiColon: return "SemiColon";
438 case CXCompletionChunk_Equal: return "Equal";
439 case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace";
440 case CXCompletionChunk_VerticalSpace: return "VerticalSpace";
Douglas Gregor9eb77012009-11-07 00:00:49 +0000441 }
442
443 return "Unknown";
444}
445
Douglas Gregor8b14f8f2009-11-09 16:04:45 +0000446void print_completion_string(CXCompletionString completion_string, FILE *file) {
Daniel Dunbar4ba3b292009-11-07 18:34:24 +0000447 int I, N;
Douglas Gregor8b14f8f2009-11-09 16:04:45 +0000448
449 N = clang_getNumCompletionChunks(completion_string);
Douglas Gregor9eb77012009-11-07 00:00:49 +0000450 for (I = 0; I != N; ++I) {
Douglas Gregorf81f5282009-11-09 17:05:28 +0000451 const char *text = 0;
Douglas Gregor9eb77012009-11-07 00:00:49 +0000452 enum CXCompletionChunkKind Kind
Douglas Gregor8b14f8f2009-11-09 16:04:45 +0000453 = clang_getCompletionChunkKind(completion_string, I);
454
455 if (Kind == CXCompletionChunk_Optional) {
456 fprintf(file, "{Optional ");
457 print_completion_string(
458 clang_getCompletionChunkCompletionString(completion_string, I),
459 file);
460 fprintf(file, "}");
461 continue;
462 }
463
Douglas Gregorf81f5282009-11-09 17:05:28 +0000464 text = clang_getCompletionChunkText(completion_string, I);
Douglas Gregor9eb77012009-11-07 00:00:49 +0000465 fprintf(file, "{%s %s}",
466 clang_getCompletionChunkKindSpelling(Kind),
467 text? text : "");
468 }
Douglas Gregor8b14f8f2009-11-09 16:04:45 +0000469}
470
471void print_completion_result(CXCompletionResult *completion_result,
472 CXClientData client_data) {
473 FILE *file = (FILE *)client_data;
474 fprintf(file, "%s:",
475 clang_getCursorKindSpelling(completion_result->CursorKind));
476 print_completion_string(completion_result->CompletionString, file);
Douglas Gregor9eb77012009-11-07 00:00:49 +0000477 fprintf(file, "\n");
478}
479
Douglas Gregor9485bf92009-12-02 09:21:34 +0000480void free_remapped_files(struct CXUnsavedFile *unsaved_files,
481 int num_unsaved_files) {
482 int i;
483 for (i = 0; i != num_unsaved_files; ++i) {
484 free((char *)unsaved_files[i].Filename);
485 free((char *)unsaved_files[i].Contents);
486 }
487}
488
489int parse_remapped_files(int argc, const char **argv, int start_arg,
490 struct CXUnsavedFile **unsaved_files,
491 int *num_unsaved_files) {
492 int i;
493 int arg;
494 int prefix_len = strlen("-remap-file=");
495 *unsaved_files = 0;
496 *num_unsaved_files = 0;
497
498 /* Count the number of remapped files. */
499 for (arg = start_arg; arg < argc; ++arg) {
500 if (strncmp(argv[arg], "-remap-file=", prefix_len))
501 break;
502
503 ++*num_unsaved_files;
504 }
505
506 if (*num_unsaved_files == 0)
507 return 0;
508
509 *unsaved_files
510 = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) *
511 *num_unsaved_files);
512 for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) {
513 struct CXUnsavedFile *unsaved = *unsaved_files + i;
514 const char *arg_string = argv[arg] + prefix_len;
515 int filename_len;
516 char *filename;
517 char *contents;
518 FILE *to_file;
519 const char *semi = strchr(arg_string, ';');
520 if (!semi) {
521 fprintf(stderr,
522 "error: -remap-file=from;to argument is missing semicolon\n");
523 free_remapped_files(*unsaved_files, i);
524 *unsaved_files = 0;
525 *num_unsaved_files = 0;
526 return -1;
527 }
528
529 /* Open the file that we're remapping to. */
530 to_file = fopen(semi + 1, "r");
531 if (!to_file) {
532 fprintf(stderr, "error: cannot open file %s that we are remapping to\n",
533 semi + 1);
534 free_remapped_files(*unsaved_files, i);
535 *unsaved_files = 0;
536 *num_unsaved_files = 0;
537 return -1;
538 }
539
540 /* Determine the length of the file we're remapping to. */
541 fseek(to_file, 0, SEEK_END);
542 unsaved->Length = ftell(to_file);
543 fseek(to_file, 0, SEEK_SET);
544
545 /* Read the contents of the file we're remapping to. */
546 contents = (char *)malloc(unsaved->Length + 1);
Chandler Carruth65ce1b72009-12-17 09:18:43 +0000547 if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) {
548 fprintf(stderr, "error: unexpected %s reading 'to' file %s\n",
549 (feof(to_file) ? "EOF" : "error"), semi + 1);
550 fclose(to_file);
551 free_remapped_files(*unsaved_files, i);
552 *unsaved_files = 0;
553 *num_unsaved_files = 0;
554 return -1;
555 }
Douglas Gregor9485bf92009-12-02 09:21:34 +0000556 contents[unsaved->Length] = 0;
557 unsaved->Contents = contents;
558
559 /* Close the file. */
560 fclose(to_file);
561
562 /* Copy the file name that we're remapping from. */
563 filename_len = semi - arg_string;
564 filename = (char *)malloc(filename_len + 1);
565 memcpy(filename, arg_string, filename_len);
566 filename[filename_len] = 0;
567 unsaved->Filename = filename;
568 }
569
570 return 0;
571}
572
Ted Kremenekef3339b2009-11-17 18:09:14 +0000573int perform_code_completion(int argc, const char **argv) {
Douglas Gregor9eb77012009-11-07 00:00:49 +0000574 const char *input = argv[1];
575 char *filename = 0;
576 unsigned line;
577 unsigned column;
Daniel Dunbar4ba3b292009-11-07 18:34:24 +0000578 CXIndex CIdx;
Ted Kremenekef3339b2009-11-17 18:09:14 +0000579 int errorCode;
Douglas Gregor9485bf92009-12-02 09:21:34 +0000580 struct CXUnsavedFile *unsaved_files = 0;
581 int num_unsaved_files = 0;
Douglas Gregorf72b6ac2009-12-18 16:20:58 +0000582 CXCodeCompleteResults *results = 0;
Daniel Dunbar4ba3b292009-11-07 18:34:24 +0000583
Douglas Gregor9eb77012009-11-07 00:00:49 +0000584 input += strlen("-code-completion-at=");
Ted Kremenekef3339b2009-11-17 18:09:14 +0000585 if ((errorCode = parse_file_line_column(input, &filename, &line, &column)))
586 return errorCode;
Douglas Gregor9eb77012009-11-07 00:00:49 +0000587
Douglas Gregor9485bf92009-12-02 09:21:34 +0000588 if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
589 return -1;
590
Daniel Dunbar4ba3b292009-11-07 18:34:24 +0000591 CIdx = clang_createIndex(0, 0);
Douglas Gregorf72b6ac2009-12-18 16:20:58 +0000592 results = clang_codeComplete(CIdx,
593 argv[argc - 1], argc - num_unsaved_files - 3,
594 argv + num_unsaved_files + 2,
595 num_unsaved_files, unsaved_files,
596 filename, line, column);
597 if (results) {
598 unsigned i, n = results->NumResults;
599 for (i = 0; i != n; ++i)
600 print_completion_result(results->Results + i, stdout);
601 clang_disposeCodeCompleteResults(results);
602 }
603
Douglas Gregor9eb77012009-11-07 00:00:49 +0000604 clang_disposeIndex(CIdx);
605 free(filename);
Ted Kremenekef3339b2009-11-17 18:09:14 +0000606
Douglas Gregor9485bf92009-12-02 09:21:34 +0000607 free_remapped_files(unsaved_files, num_unsaved_files);
608
Ted Kremenekef3339b2009-11-17 18:09:14 +0000609 return 0;
Douglas Gregor9eb77012009-11-07 00:00:49 +0000610}
611
Douglas Gregor082c3e62010-01-15 19:40:17 +0000612typedef struct {
613 char *filename;
614 unsigned line;
615 unsigned column;
616} CursorSourceLocation;
617
618int inspect_cursor_at(int argc, const char **argv) {
619 CXIndex CIdx;
620 int errorCode;
621 struct CXUnsavedFile *unsaved_files = 0;
622 int num_unsaved_files = 0;
623 CXTranslationUnit TU;
624 CXCursor Cursor;
625 CursorSourceLocation *Locations = 0;
626 unsigned NumLocations = 0, Loc;
627
628 /* Count the number of locations. */
629 while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1])
630 ++NumLocations;
631
632 /* Parse the locations. */
633 assert(NumLocations > 0 && "Unable to count locations?");
634 Locations = (CursorSourceLocation *)malloc(
635 NumLocations * sizeof(CursorSourceLocation));
636 for (Loc = 0; Loc < NumLocations; ++Loc) {
637 const char *input = argv[Loc + 1] + strlen("-cursor-at=");
638 if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
639 &Locations[Loc].line,
640 &Locations[Loc].column)))
641 return errorCode;
642 }
643
644 if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
645 &num_unsaved_files))
646 return -1;
647
648 if (num_unsaved_files > 0) {
649 fprintf(stderr, "cannot remap files when looking for a cursor\n");
650 return -1;
651 }
652
653 CIdx = clang_createIndex(0, 1);
654 TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1],
655 argc - num_unsaved_files - 2 - NumLocations,
656 argv + num_unsaved_files + 1 + NumLocations);
657 if (!TU) {
658 fprintf(stderr, "unable to parse input\n");
659 return -1;
660 }
661
662 for (Loc = 0; Loc < NumLocations; ++Loc) {
Douglas Gregor816fd362010-01-22 21:44:22 +0000663 CXFile file = clang_getFile(TU, Locations[Loc].filename);
664 if (!file)
665 continue;
666
667 Cursor = clang_getCursor(TU,
668 clang_getLocation(TU, file, Locations[Loc].line,
669 Locations[Loc].column));
Douglas Gregor082c3e62010-01-15 19:40:17 +0000670 PrintCursor(Cursor);
671 printf("\n");
672 free(Locations[Loc].filename);
673 }
674
675 clang_disposeTranslationUnit(TU);
676 clang_disposeIndex(CIdx);
677 free(Locations);
678 free_remapped_files(unsaved_files, num_unsaved_files);
679 return 0;
680}
681
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000682/******************************************************************************/
683/* Command line processing. */
684/******************************************************************************/
Ted Kremenekef3339b2009-11-17 18:09:14 +0000685
Douglas Gregor720d0052010-01-20 21:32:04 +0000686static CXCursorVisitor GetVisitor(const char *s) {
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000687 if (s[0] == '\0')
Douglas Gregor720d0052010-01-20 21:32:04 +0000688 return FilteredPrintingVisitor;
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000689 if (strcmp(s, "-usrs") == 0)
690 return USRVisitor;
691 return NULL;
692}
693
Ted Kremenekef3339b2009-11-17 18:09:14 +0000694static void print_usage(void) {
695 fprintf(stderr,
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000696 "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n"
Douglas Gregor082c3e62010-01-15 19:40:17 +0000697 " c-index-test -cursor-at=<site> <compiler arguments>\n"
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000698 " c-index-test -test-file-scan <AST file> <source file> "
699 "[FileCheck prefix]\n"
Ted Kremeneka44d99c2010-01-05 23:18:49 +0000700 " c-index-test -test-load-tu <AST file> <symbol filter> "
701 "[FileCheck prefix]\n"
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000702 " c-index-test -test-load-tu-usrs <AST file> <symbol filter> "
703 "[FileCheck prefix]\n"
704 " c-index-test -test-load-source <symbol filter> {<args>}*\n"
Douglas Gregor082c3e62010-01-15 19:40:17 +0000705 " c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n\n");
706 fprintf(stderr,
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000707 " <symbol filter> values:\n%s",
Ted Kremenek1cd27d52009-11-17 18:13:31 +0000708 " all - load all symbols, including those from PCH\n"
709 " local - load all symbols except those in PCH\n"
710 " category - only load ObjC categories (non-PCH)\n"
711 " interface - only load ObjC interfaces (non-PCH)\n"
712 " protocol - only load ObjC protocols (non-PCH)\n"
713 " function - only load functions (non-PCH)\n"
Daniel Dunbar5442bfc2009-12-01 02:35:37 +0000714 " typedef - only load typdefs (non-PCH)\n"
715 " scan-function - scan function bodies (non-PCH)\n\n");
Ted Kremenekef3339b2009-11-17 18:09:14 +0000716}
717
718int main(int argc, const char **argv) {
719 if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1])
720 return perform_code_completion(argc, argv);
Douglas Gregor082c3e62010-01-15 19:40:17 +0000721 if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1])
722 return inspect_cursor_at(argc, argv);
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000723 else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) {
Douglas Gregor720d0052010-01-20 21:32:04 +0000724 CXCursorVisitor I = GetVisitor(argv[1] + 13);
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000725 if (I)
726 return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I);
727 }
728 else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
Douglas Gregor720d0052010-01-20 21:32:04 +0000729 CXCursorVisitor I = GetVisitor(argv[1] + 17);
Ted Kremenek58a6a8e2010-01-12 23:34:26 +0000730 if (I)
731 return perform_test_load_source(argc - 3, argv + 3, argv[2], I);
732 }
733 else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
Ted Kremenek0469b7e2009-11-18 02:02:52 +0000734 return perform_file_scan(argv[2], argv[3],
735 argc >= 5 ? argv[4] : 0);
Ted Kremenekef3339b2009-11-17 18:09:14 +0000736
737 print_usage();
738 return 1;
Steve Naroffa1c72842009-08-28 15:28:48 +0000739}