c-index-test.c revision ce2ae88f834c740a28b7e074a4477039918f9bb0
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
42void free_remapped_files(struct CXUnsavedFile *unsaved_files,
43                         int num_unsaved_files) {
44  int i;
45  for (i = 0; i != num_unsaved_files; ++i) {
46    free((char *)unsaved_files[i].Filename);
47    free((char *)unsaved_files[i].Contents);
48  }
49}
50
51int parse_remapped_files(int argc, const char **argv, int start_arg,
52                         struct CXUnsavedFile **unsaved_files,
53                         int *num_unsaved_files) {
54  int i;
55  int arg;
56  int prefix_len = strlen("-remap-file=");
57  *unsaved_files = 0;
58  *num_unsaved_files = 0;
59
60  /* Count the number of remapped files. */
61  for (arg = start_arg; arg < argc; ++arg) {
62    if (strncmp(argv[arg], "-remap-file=", prefix_len))
63      break;
64
65    ++*num_unsaved_files;
66  }
67
68  if (*num_unsaved_files == 0)
69    return 0;
70
71  *unsaved_files
72  = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) *
73                                   *num_unsaved_files);
74  for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) {
75    struct CXUnsavedFile *unsaved = *unsaved_files + i;
76    const char *arg_string = argv[arg] + prefix_len;
77    int filename_len;
78    char *filename;
79    char *contents;
80    FILE *to_file;
81    const char *semi = strchr(arg_string, ';');
82    if (!semi) {
83      fprintf(stderr,
84              "error: -remap-file=from;to argument is missing semicolon\n");
85      free_remapped_files(*unsaved_files, i);
86      *unsaved_files = 0;
87      *num_unsaved_files = 0;
88      return -1;
89    }
90
91    /* Open the file that we're remapping to. */
92    to_file = fopen(semi + 1, "r");
93    if (!to_file) {
94      fprintf(stderr, "error: cannot open file %s that we are remapping to\n",
95              semi + 1);
96      free_remapped_files(*unsaved_files, i);
97      *unsaved_files = 0;
98      *num_unsaved_files = 0;
99      return -1;
100    }
101
102    /* Determine the length of the file we're remapping to. */
103    fseek(to_file, 0, SEEK_END);
104    unsaved->Length = ftell(to_file);
105    fseek(to_file, 0, SEEK_SET);
106
107    /* Read the contents of the file we're remapping to. */
108    contents = (char *)malloc(unsaved->Length + 1);
109    if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) {
110      fprintf(stderr, "error: unexpected %s reading 'to' file %s\n",
111              (feof(to_file) ? "EOF" : "error"), semi + 1);
112      fclose(to_file);
113      free_remapped_files(*unsaved_files, i);
114      *unsaved_files = 0;
115      *num_unsaved_files = 0;
116      return -1;
117    }
118    contents[unsaved->Length] = 0;
119    unsaved->Contents = contents;
120
121    /* Close the file. */
122    fclose(to_file);
123
124    /* Copy the file name that we're remapping from. */
125    filename_len = semi - arg_string;
126    filename = (char *)malloc(filename_len + 1);
127    memcpy(filename, arg_string, filename_len);
128    filename[filename_len] = 0;
129    unsaved->Filename = filename;
130  }
131
132  return 0;
133}
134
135/******************************************************************************/
136/* Pretty-printing.                                                           */
137/******************************************************************************/
138
139static void PrintCursor(CXCursor Cursor) {
140  if (clang_isInvalid(Cursor.kind))
141    printf("Invalid Cursor => %s", clang_getCursorKindSpelling(Cursor.kind));
142  else {
143    CXString string;
144    CXCursor Referenced;
145    unsigned line, column;
146    string = clang_getCursorSpelling(Cursor);
147    printf("%s=%s", clang_getCursorKindSpelling(Cursor.kind),
148                      clang_getCString(string));
149    clang_disposeString(string);
150
151    Referenced = clang_getCursorReferenced(Cursor);
152    if (!clang_equalCursors(Referenced, clang_getNullCursor())) {
153      CXSourceLocation Loc = clang_getCursorLocation(Referenced);
154      clang_getInstantiationLocation(Loc, 0, &line, &column);
155      printf(":%d:%d", line, column);
156    }
157
158    if (clang_isCursorDefinition(Cursor))
159      printf(" (Definition)");
160  }
161}
162
163static const char* GetCursorSource(CXCursor Cursor) {
164  CXSourceLocation Loc = clang_getCursorLocation(Cursor);
165  const char *source;
166  CXFile file;
167  clang_getInstantiationLocation(Loc, &file, 0, 0);
168  source = clang_getFileName(file);
169  if (!source)
170    return "<invalid loc>";
171  return basename(source);
172}
173
174/******************************************************************************/
175/* Callbacks.                                                                 */
176/******************************************************************************/
177
178typedef void (*PostVisitTU)(CXTranslationUnit);
179
180/******************************************************************************/
181/* Logic for testing traversal.                                               */
182/******************************************************************************/
183
184static const char *FileCheckPrefix = "CHECK";
185
186static void PrintCursorExtent(CXCursor C) {
187  CXSourceRange extent = clang_getCursorExtent(C);
188  CXFile begin_file, end_file;
189  unsigned begin_line, begin_column, end_line, end_column;
190
191  clang_getInstantiationLocation(clang_getRangeStart(extent),
192                                 &begin_file, &begin_line, &begin_column);
193  clang_getInstantiationLocation(clang_getRangeEnd(extent),
194                                 &end_file, &end_line, &end_column);
195  if (!begin_file || !end_file)
196    return;
197
198  printf(" [Extent=%d:%d:%d:%d]", begin_line, begin_column,
199         end_line, end_column);
200}
201
202/* Data used by all of the visitors. */
203typedef struct  {
204  CXTranslationUnit TU;
205  enum CXCursorKind *Filter;
206} VisitorData;
207
208
209enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor,
210                                                CXCursor Parent,
211                                                CXClientData ClientData) {
212  VisitorData *Data = (VisitorData *)ClientData;
213  if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) {
214    CXSourceLocation Loc = clang_getCursorLocation(Cursor);
215    unsigned line, column;
216    clang_getInstantiationLocation(Loc, 0, &line, &column);
217    printf("// %s: %s:%d:%d: ", FileCheckPrefix,
218           GetCursorSource(Cursor), line, column);
219    PrintCursor(Cursor);
220    PrintCursorExtent(Cursor);
221    printf("\n");
222    return CXChildVisit_Recurse;
223  }
224
225  return CXChildVisit_Continue;
226}
227
228static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor,
229                                                   CXCursor Parent,
230                                                   CXClientData ClientData) {
231  const char *startBuf, *endBuf;
232  unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn;
233  CXCursor Ref;
234  VisitorData *Data = (VisitorData *)ClientData;
235
236  if (Cursor.kind != CXCursor_FunctionDecl ||
237      !clang_isCursorDefinition(Cursor))
238    return CXChildVisit_Continue;
239
240  clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf,
241                                       &startLine, &startColumn,
242                                       &endLine, &endColumn);
243  /* Probe the entire body, looking for both decls and refs. */
244  curLine = startLine;
245  curColumn = startColumn;
246
247  while (startBuf < endBuf) {
248    CXSourceLocation Loc;
249    CXFile file;
250    const char *source = 0;
251
252    if (*startBuf == '\n') {
253      startBuf++;
254      curLine++;
255      curColumn = 1;
256    } else if (*startBuf != '\t')
257      curColumn++;
258
259    Loc = clang_getCursorLocation(Cursor);
260    clang_getInstantiationLocation(Loc, &file, 0, 0);
261    source = clang_getFileName(file);
262    if (source) {
263      CXSourceLocation RefLoc
264        = clang_getLocation(Data->TU, file, curLine, curColumn);
265      Ref = clang_getCursor(Data->TU, RefLoc);
266      if (Ref.kind == CXCursor_NoDeclFound) {
267        /* Nothing found here; that's fine. */
268      } else if (Ref.kind != CXCursor_FunctionDecl) {
269        printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref),
270               curLine, curColumn);
271        PrintCursor(Ref);
272        printf("\n");
273      }
274    }
275    startBuf++;
276  }
277
278  return CXChildVisit_Continue;
279}
280
281/******************************************************************************/
282/* USR testing.                                                               */
283/******************************************************************************/
284
285enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent,
286                                   CXClientData ClientData) {
287  VisitorData *Data = (VisitorData *)ClientData;
288  if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) {
289    CXString USR = clang_getCursorUSR(C);
290    if (!USR.Spelling) {
291      clang_disposeString(USR);
292      return CXChildVisit_Continue;
293    }
294    printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), USR.Spelling);
295    PrintCursorExtent(C);
296    printf("\n");
297    clang_disposeString(USR);
298
299    return CXChildVisit_Recurse;
300  }
301
302  return CXChildVisit_Continue;
303}
304
305/******************************************************************************/
306/* Loading ASTs/source.                                                       */
307/******************************************************************************/
308
309static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
310                             const char *filter, const char *prefix,
311                             CXCursorVisitor Visitor,
312                             PostVisitTU PV) {
313
314  if (prefix)
315    FileCheckPrefix = prefix;
316
317  if (Visitor) {
318    enum CXCursorKind K = CXCursor_NotImplemented;
319    enum CXCursorKind *ck = &K;
320    VisitorData Data;
321
322    /* Perform some simple filtering. */
323    if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL;
324    else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl;
325    else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl;
326    else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl;
327    else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl;
328    else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl;
329    else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor;
330    else {
331      fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter);
332      return 1;
333    }
334
335    Data.TU = TU;
336    Data.Filter = ck;
337    clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data);
338  }
339
340  if (PV)
341    PV(TU);
342
343  clang_disposeTranslationUnit(TU);
344  return 0;
345}
346
347int perform_test_load_tu(const char *file, const char *filter,
348                         const char *prefix, CXCursorVisitor Visitor,
349                         PostVisitTU PV) {
350  CXIndex Idx;
351  CXTranslationUnit TU;
352  Idx = clang_createIndex(/* excludeDeclsFromPCH */
353                          !strcmp(filter, "local") ? 1 : 0,
354                          /* displayDiagnostics */ 1);
355
356  if (!CreateTranslationUnit(Idx, file, &TU))
357    return 1;
358
359  return perform_test_load(Idx, TU, filter, prefix, Visitor, PV);
360}
361
362int perform_test_load_source(int argc, const char **argv,
363                             const char *filter, CXCursorVisitor Visitor,
364                             PostVisitTU PV) {
365  const char *UseExternalASTs =
366    getenv("CINDEXTEST_USE_EXTERNAL_AST_GENERATION");
367  CXIndex Idx;
368  CXTranslationUnit TU;
369  struct CXUnsavedFile *unsaved_files = 0;
370  int num_unsaved_files = 0;
371  int result;
372
373  Idx = clang_createIndex(/* excludeDeclsFromPCH */
374                          !strcmp(filter, "local") ? 1 : 0,
375                          /* displayDiagnostics */ 1);
376
377  if (UseExternalASTs && strlen(UseExternalASTs))
378    clang_setUseExternalASTGeneration(Idx, 1);
379
380  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files))
381    return -1;
382
383  TU = clang_createTranslationUnitFromSourceFile(Idx, 0,
384                                                 argc - num_unsaved_files,
385                                                 argv + num_unsaved_files,
386                                                 num_unsaved_files,
387                                                 unsaved_files);
388  if (!TU) {
389    fprintf(stderr, "Unable to load translation unit!\n");
390    return 1;
391  }
392
393  result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV);
394  free_remapped_files(unsaved_files, num_unsaved_files);
395  return result;
396}
397
398/******************************************************************************/
399/* Logic for testing clang_getCursor().                                       */
400/******************************************************************************/
401
402static void print_cursor_file_scan(CXCursor cursor,
403                                   unsigned start_line, unsigned start_col,
404                                   unsigned end_line, unsigned end_col,
405                                   const char *prefix) {
406  printf("// %s: ", FileCheckPrefix);
407  if (prefix)
408    printf("-%s", prefix);
409  printf("{start_line=%d start_col=%d end_line=%d end_col=%d} ",
410          start_line, start_col, end_line, end_col);
411  PrintCursor(cursor);
412  printf("\n");
413}
414
415static int perform_file_scan(const char *ast_file, const char *source_file,
416                             const char *prefix) {
417  CXIndex Idx;
418  CXTranslationUnit TU;
419  FILE *fp;
420  unsigned line;
421  CXCursor prevCursor;
422  CXFile file;
423  unsigned printed;
424  unsigned start_line, start_col, last_line, last_col;
425  size_t i;
426
427  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
428                                /* displayDiagnostics */ 1))) {
429    fprintf(stderr, "Could not create Index\n");
430    return 1;
431  }
432
433  if (!CreateTranslationUnit(Idx, ast_file, &TU))
434    return 1;
435
436  if ((fp = fopen(source_file, "r")) == NULL) {
437    fprintf(stderr, "Could not open '%s'\n", source_file);
438    return 1;
439  }
440
441  line = 0;
442  prevCursor = clang_getNullCursor();
443  printed = 0;
444  start_line = last_line = 1;
445  start_col = last_col = 1;
446
447  file = clang_getFile(TU, source_file);
448  while (!feof(fp)) {
449    size_t len = 0;
450    int c;
451
452    while ((c = fgetc(fp)) != EOF) {
453      len++;
454      if (c == '\n')
455        break;
456    }
457
458    ++line;
459
460    for (i = 0; i < len ; ++i) {
461      CXCursor cursor;
462      cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, i+1));
463
464      if (!clang_equalCursors(cursor, prevCursor) &&
465          prevCursor.kind != CXCursor_InvalidFile) {
466        print_cursor_file_scan(prevCursor, start_line, start_col,
467                               last_line, last_col, prefix);
468        printed = 1;
469        start_line = line;
470        start_col = (unsigned) i+1;
471      }
472      else {
473        printed = 0;
474      }
475
476      prevCursor = cursor;
477      last_line = line;
478      last_col = (unsigned) i+1;
479    }
480  }
481
482  if (!printed && prevCursor.kind != CXCursor_InvalidFile) {
483    print_cursor_file_scan(prevCursor, start_line, start_col,
484                           last_line, last_col, prefix);
485  }
486
487  fclose(fp);
488  return 0;
489}
490
491/******************************************************************************/
492/* Logic for testing clang_codeComplete().                                    */
493/******************************************************************************/
494
495/* Parse file:line:column from the input string. Returns 0 on success, non-zero
496   on failure. If successful, the pointer *filename will contain newly-allocated
497   memory (that will be owned by the caller) to store the file name. */
498int parse_file_line_column(const char *input, char **filename, unsigned *line,
499                           unsigned *column, unsigned *second_line,
500                           unsigned *second_column) {
501  /* Find the second colon. */
502  const char *last_colon = strrchr(input, ':');
503  unsigned values[4], i;
504  unsigned num_values = (second_line && second_column)? 4 : 2;
505
506  char *endptr = 0;
507  if (!last_colon || last_colon == input) {
508    if (num_values == 4)
509      fprintf(stderr, "could not parse filename:line:column:line:column in "
510              "'%s'\n", input);
511    else
512      fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
513    return 1;
514  }
515
516  for (i = 0; i != num_values; ++i) {
517    const char *prev_colon;
518
519    /* Parse the next line or column. */
520    values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10);
521    if (*endptr != 0 && *endptr != ':') {
522      fprintf(stderr, "could not parse %s in '%s'\n",
523              (i % 2 ? "column" : "line"), input);
524      return 1;
525    }
526
527    if (i + 1 == num_values)
528      break;
529
530    /* Find the previous colon. */
531    prev_colon = last_colon - 1;
532    while (prev_colon != input && *prev_colon != ':')
533      --prev_colon;
534    if (prev_colon == input) {
535      fprintf(stderr, "could not parse %s in '%s'\n",
536              (i % 2 == 0? "column" : "line"), input);
537      return 1;
538    }
539
540    last_colon = prev_colon;
541  }
542
543  *line = values[0];
544  *column = values[1];
545
546  if (second_line && second_column) {
547    *second_line = values[2];
548    *second_column = values[3];
549  }
550
551  /* Copy the file name. */
552  *filename = (char*)malloc(last_colon - input + 1);
553  memcpy(*filename, input, last_colon - input);
554  (*filename)[last_colon - input] = 0;
555  return 0;
556}
557
558const char *
559clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
560  switch (Kind) {
561  case CXCompletionChunk_Optional: return "Optional";
562  case CXCompletionChunk_TypedText: return "TypedText";
563  case CXCompletionChunk_Text: return "Text";
564  case CXCompletionChunk_Placeholder: return "Placeholder";
565  case CXCompletionChunk_Informative: return "Informative";
566  case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
567  case CXCompletionChunk_LeftParen: return "LeftParen";
568  case CXCompletionChunk_RightParen: return "RightParen";
569  case CXCompletionChunk_LeftBracket: return "LeftBracket";
570  case CXCompletionChunk_RightBracket: return "RightBracket";
571  case CXCompletionChunk_LeftBrace: return "LeftBrace";
572  case CXCompletionChunk_RightBrace: return "RightBrace";
573  case CXCompletionChunk_LeftAngle: return "LeftAngle";
574  case CXCompletionChunk_RightAngle: return "RightAngle";
575  case CXCompletionChunk_Comma: return "Comma";
576  case CXCompletionChunk_ResultType: return "ResultType";
577  case CXCompletionChunk_Colon: return "Colon";
578  case CXCompletionChunk_SemiColon: return "SemiColon";
579  case CXCompletionChunk_Equal: return "Equal";
580  case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace";
581  case CXCompletionChunk_VerticalSpace: return "VerticalSpace";
582  }
583
584  return "Unknown";
585}
586
587void print_completion_string(CXCompletionString completion_string, FILE *file) {
588  int I, N;
589
590  N = clang_getNumCompletionChunks(completion_string);
591  for (I = 0; I != N; ++I) {
592    const char *text = 0;
593    enum CXCompletionChunkKind Kind
594      = clang_getCompletionChunkKind(completion_string, I);
595
596    if (Kind == CXCompletionChunk_Optional) {
597      fprintf(file, "{Optional ");
598      print_completion_string(
599                clang_getCompletionChunkCompletionString(completion_string, I),
600                              file);
601      fprintf(file, "}");
602      continue;
603    }
604
605    text = clang_getCompletionChunkText(completion_string, I);
606    fprintf(file, "{%s %s}",
607            clang_getCompletionChunkKindSpelling(Kind),
608            text? text : "");
609  }
610}
611
612void print_completion_result(CXCompletionResult *completion_result,
613                             CXClientData client_data) {
614  FILE *file = (FILE *)client_data;
615  fprintf(file, "%s:",
616          clang_getCursorKindSpelling(completion_result->CursorKind));
617  print_completion_string(completion_result->CompletionString, file);
618  fprintf(file, "\n");
619}
620
621int perform_code_completion(int argc, const char **argv) {
622  const char *input = argv[1];
623  char *filename = 0;
624  unsigned line;
625  unsigned column;
626  CXIndex CIdx;
627  int errorCode;
628  struct CXUnsavedFile *unsaved_files = 0;
629  int num_unsaved_files = 0;
630  CXCodeCompleteResults *results = 0;
631
632  input += strlen("-code-completion-at=");
633  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
634                                          0, 0)))
635    return errorCode;
636
637  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
638    return -1;
639
640  CIdx = clang_createIndex(0, 0);
641  results = clang_codeComplete(CIdx,
642                               argv[argc - 1], argc - num_unsaved_files - 3,
643                               argv + num_unsaved_files + 2,
644                               num_unsaved_files, unsaved_files,
645                               filename, line, column);
646  if (results) {
647    unsigned i, n = results->NumResults;
648    for (i = 0; i != n; ++i)
649      print_completion_result(results->Results + i, stdout);
650    clang_disposeCodeCompleteResults(results);
651  }
652
653  clang_disposeIndex(CIdx);
654  free(filename);
655
656  free_remapped_files(unsaved_files, num_unsaved_files);
657
658  return 0;
659}
660
661typedef struct {
662  char *filename;
663  unsigned line;
664  unsigned column;
665} CursorSourceLocation;
666
667int inspect_cursor_at(int argc, const char **argv) {
668  CXIndex CIdx;
669  int errorCode;
670  struct CXUnsavedFile *unsaved_files = 0;
671  int num_unsaved_files = 0;
672  CXTranslationUnit TU;
673  CXCursor Cursor;
674  CursorSourceLocation *Locations = 0;
675  unsigned NumLocations = 0, Loc;
676
677  /* Count the number of locations. */
678  while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1])
679    ++NumLocations;
680
681  /* Parse the locations. */
682  assert(NumLocations > 0 && "Unable to count locations?");
683  Locations = (CursorSourceLocation *)malloc(
684                                  NumLocations * sizeof(CursorSourceLocation));
685  for (Loc = 0; Loc < NumLocations; ++Loc) {
686    const char *input = argv[Loc + 1] + strlen("-cursor-at=");
687    if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
688                                            &Locations[Loc].line,
689                                            &Locations[Loc].column, 0, 0)))
690      return errorCode;
691  }
692
693  if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
694                           &num_unsaved_files))
695    return -1;
696
697  CIdx = clang_createIndex(0, 1);
698  TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1],
699                                  argc - num_unsaved_files - 2 - NumLocations,
700                                   argv + num_unsaved_files + 1 + NumLocations,
701                                                 num_unsaved_files,
702                                                 unsaved_files);
703  if (!TU) {
704    fprintf(stderr, "unable to parse input\n");
705    return -1;
706  }
707
708  for (Loc = 0; Loc < NumLocations; ++Loc) {
709    CXFile file = clang_getFile(TU, Locations[Loc].filename);
710    if (!file)
711      continue;
712
713    Cursor = clang_getCursor(TU,
714                             clang_getLocation(TU, file, Locations[Loc].line,
715                                               Locations[Loc].column));
716    PrintCursor(Cursor);
717    printf("\n");
718    free(Locations[Loc].filename);
719  }
720
721  clang_disposeTranslationUnit(TU);
722  clang_disposeIndex(CIdx);
723  free(Locations);
724  free_remapped_files(unsaved_files, num_unsaved_files);
725  return 0;
726}
727
728int perform_token_annotation(int argc, const char **argv) {
729  const char *input = argv[1];
730  char *filename = 0;
731  unsigned line, second_line;
732  unsigned column, second_column;
733  CXIndex CIdx;
734  CXTranslationUnit TU = 0;
735  int errorCode;
736  struct CXUnsavedFile *unsaved_files = 0;
737  int num_unsaved_files = 0;
738  CXToken *tokens;
739  unsigned num_tokens;
740  CXSourceRange range;
741  CXSourceLocation startLoc, endLoc;
742  CXFile file = 0;
743  CXCursor *cursors = 0;
744  unsigned i;
745
746  input += strlen("-test-annotate-tokens=");
747  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
748                                          &second_line, &second_column)))
749    return errorCode;
750
751  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
752    return -1;
753
754  CIdx = clang_createIndex(0, 0);
755  TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1],
756                                                 argc - num_unsaved_files - 3,
757                                                 argv + num_unsaved_files + 2,
758                                                 num_unsaved_files,
759                                                 unsaved_files);
760  if (!TU) {
761    fprintf(stderr, "unable to parse input\n");
762    clang_disposeIndex(CIdx);
763    free(filename);
764    free_remapped_files(unsaved_files, num_unsaved_files);
765    return -1;
766  }
767  errorCode = 0;
768
769  file = clang_getFile(TU, filename);
770  if (!file) {
771    fprintf(stderr, "file %s is not in this translation unit\n", filename);
772    errorCode = -1;
773    goto teardown;
774  }
775
776  startLoc = clang_getLocation(TU, file, line, column);
777  if (clang_equalLocations(clang_getNullLocation(), startLoc)) {
778    fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line,
779            column);
780    errorCode = -1;
781    goto teardown;
782  }
783
784  endLoc = clang_getLocation(TU, file, second_line, second_column);
785  if (clang_equalLocations(clang_getNullLocation(), endLoc)) {
786    fprintf(stderr, "invalid source location %s:%d:%d\n", filename,
787            second_line, second_column);
788    errorCode = -1;
789    goto teardown;
790  }
791
792  range = clang_getRange(startLoc, endLoc);
793  clang_tokenize(TU, range, &tokens, &num_tokens);
794  cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor));
795  clang_annotateTokens(TU, tokens, num_tokens, cursors);
796  for (i = 0; i != num_tokens; ++i) {
797    const char *kind = "<unknown>";
798    CXString spelling = clang_getTokenSpelling(TU, tokens[i]);
799    CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]);
800    unsigned start_line, start_column, end_line, end_column;
801
802    switch (clang_getTokenKind(tokens[i])) {
803    case CXToken_Punctuation: kind = "Punctuation"; break;
804    case CXToken_Keyword: kind = "Keyword"; break;
805    case CXToken_Identifier: kind = "Identifier"; break;
806    case CXToken_Literal: kind = "Literal"; break;
807    case CXToken_Comment: kind = "Comment"; break;
808    }
809    clang_getInstantiationLocation(clang_getRangeStart(extent),
810                                   0, &start_line, &start_column);
811    clang_getInstantiationLocation(clang_getRangeEnd(extent),
812                                   0, &end_line, &end_column);
813    printf("%s: \"%s\" [%d:%d - %d:%d]\n", kind, clang_getCString(spelling),
814           start_line, start_column, end_line, end_column);
815  }
816  free(cursors);
817
818 teardown:
819  clang_disposeTranslationUnit(TU);
820  clang_disposeIndex(CIdx);
821  free(filename);
822  free_remapped_files(unsaved_files, num_unsaved_files);
823  return errorCode;
824}
825
826/******************************************************************************/
827/* Command line processing.                                                   */
828/******************************************************************************/
829
830static CXCursorVisitor GetVisitor(const char *s) {
831  if (s[0] == '\0')
832    return FilteredPrintingVisitor;
833  if (strcmp(s, "-usrs") == 0)
834    return USRVisitor;
835  return NULL;
836}
837
838static void print_usage(void) {
839  fprintf(stderr,
840    "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n"
841    "       c-index-test -cursor-at=<site> <compiler arguments>\n"
842    "       c-index-test -test-file-scan <AST file> <source file> "
843          "[FileCheck prefix]\n"
844    "       c-index-test -test-load-tu <AST file> <symbol filter> "
845          "[FileCheck prefix]\n"
846    "       c-index-test -test-load-tu-usrs <AST file> <symbol filter> "
847           "[FileCheck prefix]\n"
848    "       c-index-test -test-load-source <symbol filter> {<args>}*\n"
849    "       c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n");
850  fprintf(stderr,
851    "       c-index-test -test-annotate-tokens=<range> {<args>}* \n\n"
852    " <symbol filter> values:\n%s",
853    "   all - load all symbols, including those from PCH\n"
854    "   local - load all symbols except those in PCH\n"
855    "   category - only load ObjC categories (non-PCH)\n"
856    "   interface - only load ObjC interfaces (non-PCH)\n"
857    "   protocol - only load ObjC protocols (non-PCH)\n"
858    "   function - only load functions (non-PCH)\n"
859    "   typedef - only load typdefs (non-PCH)\n"
860    "   scan-function - scan function bodies (non-PCH)\n\n");
861}
862
863int main(int argc, const char **argv) {
864  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1])
865    return perform_code_completion(argc, argv);
866  if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1])
867    return inspect_cursor_at(argc, argv);
868  else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) {
869    CXCursorVisitor I = GetVisitor(argv[1] + 13);
870    if (I)
871      return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I,
872                                  NULL);
873  }
874  else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
875    CXCursorVisitor I = GetVisitor(argv[1] + 17);
876    if (I)
877      return perform_test_load_source(argc - 3, argv + 3, argv[2], I, NULL);
878  }
879  else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
880    return perform_file_scan(argv[2], argv[3],
881                             argc >= 5 ? argv[4] : 0);
882  else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1])
883    return perform_token_annotation(argc, argv);
884  print_usage();
885  return 1;
886}
887