c-index-test.c revision b699866820102a69d83d6ac6941985c5ef4e8c40
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#include <assert.h>
8
9/******************************************************************************/
10/* Utility functions.                                                         */
11/******************************************************************************/
12
13#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
28extern char *basename(const char *);
29#endif
30
31static 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
42/******************************************************************************/
43/* Pretty-printing.                                                           */
44/******************************************************************************/
45
46static void PrintCursor(CXCursor Cursor) {
47  if (clang_isInvalid(Cursor.kind))
48    printf("Invalid Cursor => %s", clang_getCursorKindSpelling(Cursor.kind));
49  else {
50    CXString string;
51    CXCursor Referenced;
52    string = clang_getCursorSpelling(Cursor);
53    printf("%s=%s", clang_getCursorKindSpelling(Cursor.kind),
54                      clang_getCString(string));
55    clang_disposeString(string);
56
57    Referenced = clang_getCursorReferenced(Cursor);
58    if (!clang_equalCursors(Referenced, clang_getNullCursor())) {
59      CXSourceLocation Loc = clang_getCursorLocation(Referenced);
60      printf(":%d:%d", Loc.line, Loc.column);
61    }
62
63    if (clang_isCursorDefinition(Cursor))
64      printf(" (Definition)");
65  }
66}
67
68static const char* GetCursorSource(CXCursor Cursor) {
69  const char *source = clang_getFileName(clang_getCursorLocation(Cursor).file);
70  if (!source)
71    return "<invalid loc>";
72  return basename(source);
73}
74
75/******************************************************************************/
76/* Logic for testing clang_loadTranslationUnit().                             */
77/******************************************************************************/
78
79static const char *FileCheckPrefix = "CHECK";
80
81static void PrintCursorExtent(CXCursor C) {
82  CXSourceRange extent = clang_getCursorExtent(C);
83  /* FIXME: Better way to check for empty extents? */
84  if (!extent.begin.file)
85    return;
86  printf(" [Extent=%d:%d:%d:%d]", extent.begin.line, extent.begin.column,
87         extent.end.line, extent.end.column);
88}
89
90static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) {
91  if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) {
92    CXSourceLocation Loc = clang_getCursorLocation(Cursor);
93    const char *source = clang_getFileName(Loc.file);
94    if (!source)
95      source = "<invalid loc>";
96    printf("// %s: %s:%d:%d: ", FileCheckPrefix, source, Loc.line, Loc.column);
97    PrintCursor(Cursor);
98    PrintCursorExtent(Cursor);
99
100    printf("\n");
101  }
102}
103
104static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor,
105                                   CXClientData Filter) {
106  if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) {
107    CXDecl D;
108    CXSourceLocation Loc = clang_getCursorLocation(Cursor);
109    printf("// %s: %s:%d:%d: ", FileCheckPrefix,
110           GetCursorSource(Cursor), Loc.line, Loc.column);
111    PrintCursor(Cursor);
112
113    D = clang_getCursorDecl(Cursor);
114    if (!D) {
115      printf("\n");
116      return;
117    }
118
119    PrintCursorExtent(Cursor);
120    printf("\n");
121    clang_loadDeclaration(D, DeclVisitor, 0);
122  }
123}
124
125static void FunctionScanVisitor(CXTranslationUnit Unit, CXCursor Cursor,
126                                CXClientData Filter) {
127  const char *startBuf, *endBuf;
128  unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn;
129  CXCursor Ref;
130
131  if (Cursor.kind != CXCursor_FunctionDecl ||
132      !clang_isCursorDefinition(Cursor))
133    return;
134
135  clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf,
136                                       &startLine, &startColumn,
137                                       &endLine, &endColumn);
138  /* Probe the entire body, looking for both decls and refs. */
139  curLine = startLine;
140  curColumn = startColumn;
141
142  while (startBuf < endBuf) {
143    CXSourceLocation Loc;
144    const char *source = 0;
145
146    if (*startBuf == '\n') {
147      startBuf++;
148      curLine++;
149      curColumn = 1;
150    } else if (*startBuf != '\t')
151      curColumn++;
152
153    Loc = clang_getCursorLocation(Cursor);
154    source = clang_getFileName(Loc.file);
155    if (source) {
156      Ref = clang_getCursor(Unit, source, curLine, curColumn);
157      if (Ref.kind == CXCursor_NoDeclFound) {
158        /* Nothing found here; that's fine. */
159      } else if (Ref.kind != CXCursor_FunctionDecl) {
160        printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref),
161               curLine, curColumn);
162        PrintCursor(Ref);
163        printf("\n");
164      }
165    }
166    startBuf++;
167  }
168}
169
170/******************************************************************************/
171/* USR testing.                                                               */
172/******************************************************************************/
173
174static void USRDeclVisitor(CXDecl D, CXCursor C, CXClientData Filter) {
175  if (!Filter || (C.kind == *(enum CXCursorKind *)Filter)) {
176    CXString USR = clang_getCursorUSR(C);
177    if (!USR.Spelling) {
178      clang_disposeString(USR);
179      return;
180    }
181    printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), USR.Spelling);
182    PrintCursorExtent(C);
183    printf("\n");
184    clang_disposeString(USR);
185  }
186}
187
188static void USRVisitor(CXTranslationUnit Unit, CXCursor Cursor,
189                       CXClientData Filter) {
190  CXDecl D = clang_getCursorDecl(Cursor);
191  if (D) {
192    /* USRDeclVisitor(Unit, Cursor.decl, Cursor, Filter);*/
193    clang_loadDeclaration(D, USRDeclVisitor, 0);
194  }
195}
196
197/******************************************************************************/
198/* Loading ASTs/source.                                                       */
199/******************************************************************************/
200
201static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
202                             const char *filter, const char *prefix,
203                             CXTranslationUnitIterator Visitor) {
204  enum CXCursorKind K = CXCursor_NotImplemented;
205  enum CXCursorKind *ck = &K;
206
207  if (prefix)
208    FileCheckPrefix = prefix;
209
210  /* Perform some simple filtering. */
211  if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL;
212  else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl;
213  else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl;
214  else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl;
215  else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl;
216  else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl;
217  else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor;
218  else {
219    fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter);
220    return 1;
221  }
222
223  clang_loadTranslationUnit(TU, Visitor, ck);
224  clang_disposeTranslationUnit(TU);
225  return 0;
226}
227
228int perform_test_load_tu(const char *file, const char *filter,
229                         const char *prefix,
230                         CXTranslationUnitIterator Visitor) {
231  CXIndex Idx;
232  CXTranslationUnit TU;
233  Idx = clang_createIndex(/* excludeDeclsFromPCH */
234                          !strcmp(filter, "local") ? 1 : 0,
235                          /* displayDiagnostics */ 1);
236
237  if (!CreateTranslationUnit(Idx, file, &TU))
238    return 1;
239
240  return perform_test_load(Idx, TU, filter, prefix, Visitor);
241}
242
243int perform_test_load_source(int argc, const char **argv, const char *filter,
244                             CXTranslationUnitIterator Visitor) {
245  const char *UseExternalASTs =
246    getenv("CINDEXTEST_USE_EXTERNAL_AST_GENERATION");
247  CXIndex Idx;
248  CXTranslationUnit TU;
249  Idx = clang_createIndex(/* excludeDeclsFromPCH */
250                          !strcmp(filter, "local") ? 1 : 0,
251                          /* displayDiagnostics */ 1);
252
253  if (UseExternalASTs && strlen(UseExternalASTs))
254    clang_setUseExternalASTGeneration(Idx, 1);
255
256  TU = clang_createTranslationUnitFromSourceFile(Idx, 0, argc, argv);
257  if (!TU) {
258    fprintf(stderr, "Unable to load translation unit!\n");
259    return 1;
260  }
261
262  return perform_test_load(Idx, TU, filter, NULL, Visitor);
263}
264
265/******************************************************************************/
266/* Logic for testing clang_getCursor().                                       */
267/******************************************************************************/
268
269static void print_cursor_file_scan(CXCursor cursor,
270                                   unsigned start_line, unsigned start_col,
271                                   unsigned end_line, unsigned end_col,
272                                   const char *prefix) {
273  printf("// %s: ", FileCheckPrefix);
274  if (prefix)
275    printf("-%s", prefix);
276  printf("{start_line=%d start_col=%d end_line=%d end_col=%d} ",
277          start_line, start_col, end_line, end_col);
278  PrintCursor(cursor);
279  printf("\n");
280}
281
282static int perform_file_scan(const char *ast_file, const char *source_file,
283                             const char *prefix) {
284  CXIndex Idx;
285  CXTranslationUnit TU;
286  FILE *fp;
287  unsigned line;
288  CXCursor prevCursor;
289  unsigned printed;
290  unsigned start_line, start_col, last_line, last_col;
291  size_t i;
292
293  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
294                                /* displayDiagnostics */ 1))) {
295    fprintf(stderr, "Could not create Index\n");
296    return 1;
297  }
298
299  if (!CreateTranslationUnit(Idx, ast_file, &TU))
300    return 1;
301
302  if ((fp = fopen(source_file, "r")) == NULL) {
303    fprintf(stderr, "Could not open '%s'\n", source_file);
304    return 1;
305  }
306
307  line = 0;
308  prevCursor = clang_getNullCursor();
309  printed = 0;
310  start_line = last_line = 1;
311  start_col = last_col = 1;
312
313  while (!feof(fp)) {
314    size_t len = 0;
315    int c;
316
317    while ((c = fgetc(fp)) != EOF) {
318      len++;
319      if (c == '\n')
320        break;
321    }
322
323    ++line;
324
325    for (i = 0; i < len ; ++i) {
326      CXCursor cursor;
327      cursor = clang_getCursor(TU, source_file, line, i+1);
328
329      if (!clang_equalCursors(cursor, prevCursor) &&
330          prevCursor.kind != CXCursor_InvalidFile) {
331        print_cursor_file_scan(prevCursor, start_line, start_col,
332                               last_line, last_col, prefix);
333        printed = 1;
334        start_line = line;
335        start_col = (unsigned) i+1;
336      }
337      else {
338        printed = 0;
339      }
340
341      prevCursor = cursor;
342      last_line = line;
343      last_col = (unsigned) i+1;
344    }
345  }
346
347  if (!printed && prevCursor.kind != CXCursor_InvalidFile) {
348    print_cursor_file_scan(prevCursor, start_line, start_col,
349                           last_line, last_col, prefix);
350  }
351
352  fclose(fp);
353  return 0;
354}
355
356/******************************************************************************/
357/* Logic for testing clang_codeComplete().                                    */
358/******************************************************************************/
359
360/* Parse file:line:column from the input string. Returns 0 on success, non-zero
361   on failure. If successful, the pointer *filename will contain newly-allocated
362   memory (that will be owned by the caller) to store the file name. */
363int parse_file_line_column(const char *input, char **filename, unsigned *line,
364                           unsigned *column) {
365  /* Find the second colon. */
366  const char *second_colon = strrchr(input, ':'), *first_colon;
367  char *endptr = 0;
368  if (!second_colon || second_colon == input) {
369    fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
370    return 1;
371  }
372
373  /* Parse the column number. */
374  *column = strtol(second_colon + 1, &endptr, 10);
375  if (*endptr != 0) {
376    fprintf(stderr, "could not parse column in '%s'\n", input);
377    return 1;
378  }
379
380  /* Find the first colon. */
381  first_colon = second_colon - 1;
382  while (first_colon != input && *first_colon != ':')
383    --first_colon;
384  if (first_colon == input) {
385    fprintf(stderr, "could not parse line in '%s'\n", input);
386    return 1;
387  }
388
389  /* Parse the line number. */
390  *line = strtol(first_colon + 1, &endptr, 10);
391  if (*endptr != ':') {
392    fprintf(stderr, "could not parse line in '%s'\n", input);
393    return 1;
394  }
395
396  /* Copy the file name. */
397  *filename = (char*)malloc(first_colon - input + 1);
398  memcpy(*filename, input, first_colon - input);
399  (*filename)[first_colon - input] = 0;
400  return 0;
401}
402
403const char *
404clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
405  switch (Kind) {
406  case CXCompletionChunk_Optional: return "Optional";
407  case CXCompletionChunk_TypedText: return "TypedText";
408  case CXCompletionChunk_Text: return "Text";
409  case CXCompletionChunk_Placeholder: return "Placeholder";
410  case CXCompletionChunk_Informative: return "Informative";
411  case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
412  case CXCompletionChunk_LeftParen: return "LeftParen";
413  case CXCompletionChunk_RightParen: return "RightParen";
414  case CXCompletionChunk_LeftBracket: return "LeftBracket";
415  case CXCompletionChunk_RightBracket: return "RightBracket";
416  case CXCompletionChunk_LeftBrace: return "LeftBrace";
417  case CXCompletionChunk_RightBrace: return "RightBrace";
418  case CXCompletionChunk_LeftAngle: return "LeftAngle";
419  case CXCompletionChunk_RightAngle: return "RightAngle";
420  case CXCompletionChunk_Comma: return "Comma";
421  case CXCompletionChunk_ResultType: return "ResultType";
422  case CXCompletionChunk_Colon: return "Colon";
423  case CXCompletionChunk_SemiColon: return "SemiColon";
424  case CXCompletionChunk_Equal: return "Equal";
425  case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace";
426  case CXCompletionChunk_VerticalSpace: return "VerticalSpace";
427  }
428
429  return "Unknown";
430}
431
432void print_completion_string(CXCompletionString completion_string, FILE *file) {
433  int I, N;
434
435  N = clang_getNumCompletionChunks(completion_string);
436  for (I = 0; I != N; ++I) {
437    const char *text = 0;
438    enum CXCompletionChunkKind Kind
439      = clang_getCompletionChunkKind(completion_string, I);
440
441    if (Kind == CXCompletionChunk_Optional) {
442      fprintf(file, "{Optional ");
443      print_completion_string(
444                clang_getCompletionChunkCompletionString(completion_string, I),
445                              file);
446      fprintf(file, "}");
447      continue;
448    }
449
450    text = clang_getCompletionChunkText(completion_string, I);
451    fprintf(file, "{%s %s}",
452            clang_getCompletionChunkKindSpelling(Kind),
453            text? text : "");
454  }
455}
456
457void print_completion_result(CXCompletionResult *completion_result,
458                             CXClientData client_data) {
459  FILE *file = (FILE *)client_data;
460  fprintf(file, "%s:",
461          clang_getCursorKindSpelling(completion_result->CursorKind));
462  print_completion_string(completion_result->CompletionString, file);
463  fprintf(file, "\n");
464}
465
466void free_remapped_files(struct CXUnsavedFile *unsaved_files,
467                         int num_unsaved_files) {
468  int i;
469  for (i = 0; i != num_unsaved_files; ++i) {
470    free((char *)unsaved_files[i].Filename);
471    free((char *)unsaved_files[i].Contents);
472  }
473}
474
475int parse_remapped_files(int argc, const char **argv, int start_arg,
476                         struct CXUnsavedFile **unsaved_files,
477                          int *num_unsaved_files) {
478  int i;
479  int arg;
480  int prefix_len = strlen("-remap-file=");
481  *unsaved_files = 0;
482  *num_unsaved_files = 0;
483
484  /* Count the number of remapped files. */
485  for (arg = start_arg; arg < argc; ++arg) {
486    if (strncmp(argv[arg], "-remap-file=", prefix_len))
487      break;
488
489    ++*num_unsaved_files;
490  }
491
492  if (*num_unsaved_files == 0)
493    return 0;
494
495  *unsaved_files
496    = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) *
497                                     *num_unsaved_files);
498  for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) {
499    struct CXUnsavedFile *unsaved = *unsaved_files + i;
500    const char *arg_string = argv[arg] + prefix_len;
501    int filename_len;
502    char *filename;
503    char *contents;
504    FILE *to_file;
505    const char *semi = strchr(arg_string, ';');
506    if (!semi) {
507      fprintf(stderr,
508              "error: -remap-file=from;to argument is missing semicolon\n");
509      free_remapped_files(*unsaved_files, i);
510      *unsaved_files = 0;
511      *num_unsaved_files = 0;
512      return -1;
513    }
514
515    /* Open the file that we're remapping to. */
516    to_file = fopen(semi + 1, "r");
517    if (!to_file) {
518      fprintf(stderr, "error: cannot open file %s that we are remapping to\n",
519              semi + 1);
520      free_remapped_files(*unsaved_files, i);
521      *unsaved_files = 0;
522      *num_unsaved_files = 0;
523      return -1;
524    }
525
526    /* Determine the length of the file we're remapping to. */
527    fseek(to_file, 0, SEEK_END);
528    unsaved->Length = ftell(to_file);
529    fseek(to_file, 0, SEEK_SET);
530
531    /* Read the contents of the file we're remapping to. */
532    contents = (char *)malloc(unsaved->Length + 1);
533    if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) {
534      fprintf(stderr, "error: unexpected %s reading 'to' file %s\n",
535              (feof(to_file) ? "EOF" : "error"), semi + 1);
536      fclose(to_file);
537      free_remapped_files(*unsaved_files, i);
538      *unsaved_files = 0;
539      *num_unsaved_files = 0;
540      return -1;
541    }
542    contents[unsaved->Length] = 0;
543    unsaved->Contents = contents;
544
545    /* Close the file. */
546    fclose(to_file);
547
548    /* Copy the file name that we're remapping from. */
549    filename_len = semi - arg_string;
550    filename = (char *)malloc(filename_len + 1);
551    memcpy(filename, arg_string, filename_len);
552    filename[filename_len] = 0;
553    unsaved->Filename = filename;
554  }
555
556  return 0;
557}
558
559int perform_code_completion(int argc, const char **argv) {
560  const char *input = argv[1];
561  char *filename = 0;
562  unsigned line;
563  unsigned column;
564  CXIndex CIdx;
565  int errorCode;
566  struct CXUnsavedFile *unsaved_files = 0;
567  int num_unsaved_files = 0;
568  CXCodeCompleteResults *results = 0;
569
570  input += strlen("-code-completion-at=");
571  if ((errorCode = parse_file_line_column(input, &filename, &line, &column)))
572    return errorCode;
573
574  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
575    return -1;
576
577  CIdx = clang_createIndex(0, 0);
578  results = clang_codeComplete(CIdx,
579                               argv[argc - 1], argc - num_unsaved_files - 3,
580                               argv + num_unsaved_files + 2,
581                               num_unsaved_files, unsaved_files,
582                               filename, line, column);
583  if (results) {
584    unsigned i, n = results->NumResults;
585    for (i = 0; i != n; ++i)
586      print_completion_result(results->Results + i, stdout);
587    clang_disposeCodeCompleteResults(results);
588  }
589
590  clang_disposeIndex(CIdx);
591  free(filename);
592
593  free_remapped_files(unsaved_files, num_unsaved_files);
594
595  return 0;
596}
597
598typedef struct {
599  char *filename;
600  unsigned line;
601  unsigned column;
602} CursorSourceLocation;
603
604int inspect_cursor_at(int argc, const char **argv) {
605  CXIndex CIdx;
606  int errorCode;
607  struct CXUnsavedFile *unsaved_files = 0;
608  int num_unsaved_files = 0;
609  CXTranslationUnit TU;
610  CXCursor Cursor;
611  CursorSourceLocation *Locations = 0;
612  unsigned NumLocations = 0, Loc;
613
614  /* Count the number of locations. */
615  while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1])
616    ++NumLocations;
617
618  /* Parse the locations. */
619  assert(NumLocations > 0 && "Unable to count locations?");
620  Locations = (CursorSourceLocation *)malloc(
621                                  NumLocations * sizeof(CursorSourceLocation));
622  for (Loc = 0; Loc < NumLocations; ++Loc) {
623    const char *input = argv[Loc + 1] + strlen("-cursor-at=");
624    if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
625                                            &Locations[Loc].line,
626                                            &Locations[Loc].column)))
627      return errorCode;
628  }
629
630  if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
631                           &num_unsaved_files))
632    return -1;
633
634  if (num_unsaved_files > 0) {
635    fprintf(stderr, "cannot remap files when looking for a cursor\n");
636    return -1;
637  }
638
639  CIdx = clang_createIndex(0, 1);
640  TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1],
641                                  argc - num_unsaved_files - 2 - NumLocations,
642                                   argv + num_unsaved_files + 1 + NumLocations);
643  if (!TU) {
644    fprintf(stderr, "unable to parse input\n");
645    return -1;
646  }
647
648  for (Loc = 0; Loc < NumLocations; ++Loc) {
649    Cursor = clang_getCursor(TU, Locations[Loc].filename,
650                             Locations[Loc].line, Locations[Loc].column);
651    PrintCursor(Cursor);
652    printf("\n");
653    free(Locations[Loc].filename);
654  }
655
656  clang_disposeTranslationUnit(TU);
657  clang_disposeIndex(CIdx);
658  free(Locations);
659  free_remapped_files(unsaved_files, num_unsaved_files);
660  return 0;
661}
662
663/******************************************************************************/
664/* Command line processing.                                                   */
665/******************************************************************************/
666
667static CXTranslationUnitIterator GetVisitor(const char *s) {
668  if (s[0] == '\0')
669    return TranslationUnitVisitor;
670  if (strcmp(s, "-usrs") == 0)
671    return USRVisitor;
672  return NULL;
673}
674
675static void print_usage(void) {
676  fprintf(stderr,
677    "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n"
678    "       c-index-test -cursor-at=<site> <compiler arguments>\n"
679    "       c-index-test -test-file-scan <AST file> <source file> "
680          "[FileCheck prefix]\n"
681    "       c-index-test -test-load-tu <AST file> <symbol filter> "
682          "[FileCheck prefix]\n"
683    "       c-index-test -test-load-tu-usrs <AST file> <symbol filter> "
684           "[FileCheck prefix]\n"
685    "       c-index-test -test-load-source <symbol filter> {<args>}*\n"
686    "       c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n\n");
687  fprintf(stderr,
688    " <symbol filter> values:\n%s",
689    "   all - load all symbols, including those from PCH\n"
690    "   local - load all symbols except those in PCH\n"
691    "   category - only load ObjC categories (non-PCH)\n"
692    "   interface - only load ObjC interfaces (non-PCH)\n"
693    "   protocol - only load ObjC protocols (non-PCH)\n"
694    "   function - only load functions (non-PCH)\n"
695    "   typedef - only load typdefs (non-PCH)\n"
696    "   scan-function - scan function bodies (non-PCH)\n\n");
697}
698
699int main(int argc, const char **argv) {
700  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1])
701    return perform_code_completion(argc, argv);
702  if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1])
703    return inspect_cursor_at(argc, argv);
704  else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) {
705    CXTranslationUnitIterator I = GetVisitor(argv[1] + 13);
706    if (I)
707      return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I);
708  }
709  else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
710    CXTranslationUnitIterator I = GetVisitor(argv[1] + 17);
711    if (I)
712      return perform_test_load_source(argc - 3, argv + 3, argv[2], I);
713  }
714  else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
715    return perform_file_scan(argv[2], argv[3],
716                             argc >= 5 ? argv[4] : 0);
717
718  print_usage();
719  return 1;
720}
721