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