c-index-test.c revision e4330a302ac20b41b9800267ebd4b5b01f8553f8
1/* c-index-test.c */
2
3#include "clang-c/Index.h"
4#include "clang-c/CXCompilationDatabase.h"
5#include "llvm/Config/config.h"
6#include <ctype.h>
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
10#include <assert.h>
11
12#ifdef CLANG_HAVE_LIBXML
13#include <libxml/parser.h>
14#include <libxml/relaxng.h>
15#include <libxml/xmlerror.h>
16#endif
17
18/******************************************************************************/
19/* Utility functions.                                                         */
20/******************************************************************************/
21
22#ifdef _MSC_VER
23char *basename(const char* path)
24{
25    char* base1 = (char*)strrchr(path, '/');
26    char* base2 = (char*)strrchr(path, '\\');
27    if (base1 && base2)
28        return((base1 > base2) ? base1 + 1 : base2 + 1);
29    else if (base1)
30        return(base1 + 1);
31    else if (base2)
32        return(base2 + 1);
33
34    return((char*)path);
35}
36char *dirname(char* path)
37{
38    char* base1 = (char*)strrchr(path, '/');
39    char* base2 = (char*)strrchr(path, '\\');
40    if (base1 && base2)
41        if (base1 > base2)
42          *base1 = 0;
43        else
44          *base2 = 0;
45    else if (base1)
46        *base1 = 0;
47    else if (base2)
48        *base2 = 0;
49
50    return path;
51}
52#else
53extern char *basename(const char *);
54extern char *dirname(char *);
55#endif
56
57/** \brief Return the default parsing options. */
58static unsigned getDefaultParsingOptions() {
59  unsigned options = CXTranslationUnit_DetailedPreprocessingRecord;
60
61  if (getenv("CINDEXTEST_EDITING"))
62    options |= clang_defaultEditingTranslationUnitOptions();
63  if (getenv("CINDEXTEST_COMPLETION_CACHING"))
64    options |= CXTranslationUnit_CacheCompletionResults;
65  if (getenv("CINDEXTEST_COMPLETION_NO_CACHING"))
66    options &= ~CXTranslationUnit_CacheCompletionResults;
67  if (getenv("CINDEXTEST_SKIP_FUNCTION_BODIES"))
68    options |= CXTranslationUnit_SkipFunctionBodies;
69  if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS"))
70    options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
71
72  return options;
73}
74
75static int checkForErrors(CXTranslationUnit TU);
76
77static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column,
78                        unsigned end_line, unsigned end_column) {
79  fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column,
80          end_line, end_column);
81}
82
83static unsigned CreateTranslationUnit(CXIndex Idx, const char *file,
84                                      CXTranslationUnit *TU) {
85
86  *TU = clang_createTranslationUnit(Idx, file);
87  if (!*TU) {
88    fprintf(stderr, "Unable to load translation unit from '%s'!\n", file);
89    return 0;
90  }
91  return 1;
92}
93
94void free_remapped_files(struct CXUnsavedFile *unsaved_files,
95                         int num_unsaved_files) {
96  int i;
97  for (i = 0; i != num_unsaved_files; ++i) {
98    free((char *)unsaved_files[i].Filename);
99    free((char *)unsaved_files[i].Contents);
100  }
101  free(unsaved_files);
102}
103
104int parse_remapped_files(int argc, const char **argv, int start_arg,
105                         struct CXUnsavedFile **unsaved_files,
106                         int *num_unsaved_files) {
107  int i;
108  int arg;
109  int prefix_len = strlen("-remap-file=");
110  *unsaved_files = 0;
111  *num_unsaved_files = 0;
112
113  /* Count the number of remapped files. */
114  for (arg = start_arg; arg < argc; ++arg) {
115    if (strncmp(argv[arg], "-remap-file=", prefix_len))
116      break;
117
118    ++*num_unsaved_files;
119  }
120
121  if (*num_unsaved_files == 0)
122    return 0;
123
124  *unsaved_files
125    = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) *
126                                     *num_unsaved_files);
127  for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) {
128    struct CXUnsavedFile *unsaved = *unsaved_files + i;
129    const char *arg_string = argv[arg] + prefix_len;
130    int filename_len;
131    char *filename;
132    char *contents;
133    FILE *to_file;
134    const char *semi = strchr(arg_string, ';');
135    if (!semi) {
136      fprintf(stderr,
137              "error: -remap-file=from;to argument is missing semicolon\n");
138      free_remapped_files(*unsaved_files, i);
139      *unsaved_files = 0;
140      *num_unsaved_files = 0;
141      return -1;
142    }
143
144    /* Open the file that we're remapping to. */
145    to_file = fopen(semi + 1, "rb");
146    if (!to_file) {
147      fprintf(stderr, "error: cannot open file %s that we are remapping to\n",
148              semi + 1);
149      free_remapped_files(*unsaved_files, i);
150      *unsaved_files = 0;
151      *num_unsaved_files = 0;
152      return -1;
153    }
154
155    /* Determine the length of the file we're remapping to. */
156    fseek(to_file, 0, SEEK_END);
157    unsaved->Length = ftell(to_file);
158    fseek(to_file, 0, SEEK_SET);
159
160    /* Read the contents of the file we're remapping to. */
161    contents = (char *)malloc(unsaved->Length + 1);
162    if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) {
163      fprintf(stderr, "error: unexpected %s reading 'to' file %s\n",
164              (feof(to_file) ? "EOF" : "error"), semi + 1);
165      fclose(to_file);
166      free_remapped_files(*unsaved_files, i);
167      free(contents);
168      *unsaved_files = 0;
169      *num_unsaved_files = 0;
170      return -1;
171    }
172    contents[unsaved->Length] = 0;
173    unsaved->Contents = contents;
174
175    /* Close the file. */
176    fclose(to_file);
177
178    /* Copy the file name that we're remapping from. */
179    filename_len = semi - arg_string;
180    filename = (char *)malloc(filename_len + 1);
181    memcpy(filename, arg_string, filename_len);
182    filename[filename_len] = 0;
183    unsaved->Filename = filename;
184  }
185
186  return 0;
187}
188
189static const char *parse_comments_schema(int argc, const char **argv) {
190  const char *CommentsSchemaArg = "-comments-xml-schema=";
191  const char *CommentSchemaFile = NULL;
192
193  if (argc == 0)
194    return CommentSchemaFile;
195
196  if (!strncmp(argv[0], CommentsSchemaArg, strlen(CommentsSchemaArg)))
197    CommentSchemaFile = argv[0] + strlen(CommentsSchemaArg);
198
199  return CommentSchemaFile;
200}
201
202/******************************************************************************/
203/* Pretty-printing.                                                           */
204/******************************************************************************/
205
206static const char *FileCheckPrefix = "CHECK";
207
208static void PrintCString(const char *CStr) {
209  if (CStr != NULL && CStr[0] != '\0') {
210    for ( ; *CStr; ++CStr) {
211      const char C = *CStr;
212      switch (C) {
213        case '\n': printf("\\n"); break;
214        case '\r': printf("\\r"); break;
215        case '\t': printf("\\t"); break;
216        case '\v': printf("\\v"); break;
217        case '\f': printf("\\f"); break;
218        default:   putchar(C);    break;
219      }
220    }
221  }
222}
223
224static void PrintCStringWithPrefix(const char *Prefix, const char *CStr) {
225  printf(" %s=[", Prefix);
226  PrintCString(CStr);
227  printf("]");
228}
229
230static void PrintCXStringAndDispose(CXString Str) {
231  PrintCString(clang_getCString(Str));
232  clang_disposeString(Str);
233}
234
235static void PrintCXStringWithPrefix(const char *Prefix, CXString Str) {
236  PrintCStringWithPrefix(Prefix, clang_getCString(Str));
237}
238
239static void PrintCXStringWithPrefixAndDispose(const char *Prefix,
240                                              CXString Str) {
241  PrintCStringWithPrefix(Prefix, clang_getCString(Str));
242  clang_disposeString(Str);
243}
244
245static void PrintRange(CXSourceRange R, const char *str) {
246  CXFile begin_file, end_file;
247  unsigned begin_line, begin_column, end_line, end_column;
248
249  clang_getSpellingLocation(clang_getRangeStart(R),
250                            &begin_file, &begin_line, &begin_column, 0);
251  clang_getSpellingLocation(clang_getRangeEnd(R),
252                            &end_file, &end_line, &end_column, 0);
253  if (!begin_file || !end_file)
254    return;
255
256  if (str)
257    printf(" %s=", str);
258  PrintExtent(stdout, begin_line, begin_column, end_line, end_column);
259}
260
261int want_display_name = 0;
262
263static void printVersion(const char *Prefix, CXVersion Version) {
264  if (Version.Major < 0)
265    return;
266  printf("%s%d", Prefix, Version.Major);
267
268  if (Version.Minor < 0)
269    return;
270  printf(".%d", Version.Minor);
271
272  if (Version.Subminor < 0)
273    return;
274  printf(".%d", Version.Subminor);
275}
276
277struct CommentASTDumpingContext {
278  int IndentLevel;
279};
280
281static void DumpCXCommentInternal(struct CommentASTDumpingContext *Ctx,
282                                  CXComment Comment) {
283  unsigned i;
284  unsigned e;
285  enum CXCommentKind Kind = clang_Comment_getKind(Comment);
286
287  Ctx->IndentLevel++;
288  for (i = 0, e = Ctx->IndentLevel; i != e; ++i)
289    printf("  ");
290
291  printf("(");
292  switch (Kind) {
293  case CXComment_Null:
294    printf("CXComment_Null");
295    break;
296  case CXComment_Text:
297    printf("CXComment_Text");
298    PrintCXStringWithPrefixAndDispose("Text",
299                                      clang_TextComment_getText(Comment));
300    if (clang_Comment_isWhitespace(Comment))
301      printf(" IsWhitespace");
302    if (clang_InlineContentComment_hasTrailingNewline(Comment))
303      printf(" HasTrailingNewline");
304    break;
305  case CXComment_InlineCommand:
306    printf("CXComment_InlineCommand");
307    PrintCXStringWithPrefixAndDispose(
308        "CommandName",
309        clang_InlineCommandComment_getCommandName(Comment));
310    switch (clang_InlineCommandComment_getRenderKind(Comment)) {
311    case CXCommentInlineCommandRenderKind_Normal:
312      printf(" RenderNormal");
313      break;
314    case CXCommentInlineCommandRenderKind_Bold:
315      printf(" RenderBold");
316      break;
317    case CXCommentInlineCommandRenderKind_Monospaced:
318      printf(" RenderMonospaced");
319      break;
320    case CXCommentInlineCommandRenderKind_Emphasized:
321      printf(" RenderEmphasized");
322      break;
323    }
324    for (i = 0, e = clang_InlineCommandComment_getNumArgs(Comment);
325         i != e; ++i) {
326      printf(" Arg[%u]=", i);
327      PrintCXStringAndDispose(
328          clang_InlineCommandComment_getArgText(Comment, i));
329    }
330    if (clang_InlineContentComment_hasTrailingNewline(Comment))
331      printf(" HasTrailingNewline");
332    break;
333  case CXComment_HTMLStartTag: {
334    unsigned NumAttrs;
335    printf("CXComment_HTMLStartTag");
336    PrintCXStringWithPrefixAndDispose(
337        "Name",
338        clang_HTMLTagComment_getTagName(Comment));
339    NumAttrs = clang_HTMLStartTag_getNumAttrs(Comment);
340    if (NumAttrs != 0) {
341      printf(" Attrs:");
342      for (i = 0; i != NumAttrs; ++i) {
343        printf(" ");
344        PrintCXStringAndDispose(clang_HTMLStartTag_getAttrName(Comment, i));
345        printf("=");
346        PrintCXStringAndDispose(clang_HTMLStartTag_getAttrValue(Comment, i));
347      }
348    }
349    if (clang_HTMLStartTagComment_isSelfClosing(Comment))
350      printf(" SelfClosing");
351    if (clang_InlineContentComment_hasTrailingNewline(Comment))
352      printf(" HasTrailingNewline");
353    break;
354  }
355  case CXComment_HTMLEndTag:
356    printf("CXComment_HTMLEndTag");
357    PrintCXStringWithPrefixAndDispose(
358        "Name",
359        clang_HTMLTagComment_getTagName(Comment));
360    if (clang_InlineContentComment_hasTrailingNewline(Comment))
361      printf(" HasTrailingNewline");
362    break;
363  case CXComment_Paragraph:
364    printf("CXComment_Paragraph");
365    if (clang_Comment_isWhitespace(Comment))
366      printf(" IsWhitespace");
367    break;
368  case CXComment_BlockCommand:
369    printf("CXComment_BlockCommand");
370    PrintCXStringWithPrefixAndDispose(
371        "CommandName",
372        clang_BlockCommandComment_getCommandName(Comment));
373    for (i = 0, e = clang_BlockCommandComment_getNumArgs(Comment);
374         i != e; ++i) {
375      printf(" Arg[%u]=", i);
376      PrintCXStringAndDispose(
377          clang_BlockCommandComment_getArgText(Comment, i));
378    }
379    break;
380  case CXComment_ParamCommand:
381    printf("CXComment_ParamCommand");
382    switch (clang_ParamCommandComment_getDirection(Comment)) {
383    case CXCommentParamPassDirection_In:
384      printf(" in");
385      break;
386    case CXCommentParamPassDirection_Out:
387      printf(" out");
388      break;
389    case CXCommentParamPassDirection_InOut:
390      printf(" in,out");
391      break;
392    }
393    if (clang_ParamCommandComment_isDirectionExplicit(Comment))
394      printf(" explicitly");
395    else
396      printf(" implicitly");
397    PrintCXStringWithPrefixAndDispose(
398        "ParamName",
399        clang_ParamCommandComment_getParamName(Comment));
400    if (clang_ParamCommandComment_isParamIndexValid(Comment))
401      printf(" ParamIndex=%u", clang_ParamCommandComment_getParamIndex(Comment));
402    else
403      printf(" ParamIndex=Invalid");
404    break;
405  case CXComment_TParamCommand:
406    printf("CXComment_TParamCommand");
407    PrintCXStringWithPrefixAndDispose(
408        "ParamName",
409        clang_TParamCommandComment_getParamName(Comment));
410    if (clang_TParamCommandComment_isParamPositionValid(Comment)) {
411      printf(" ParamPosition={");
412      for (i = 0, e = clang_TParamCommandComment_getDepth(Comment);
413           i != e; ++i) {
414        printf("%u", clang_TParamCommandComment_getIndex(Comment, i));
415        if (i != e - 1)
416          printf(", ");
417      }
418      printf("}");
419    } else
420      printf(" ParamPosition=Invalid");
421    break;
422  case CXComment_VerbatimBlockCommand:
423    printf("CXComment_VerbatimBlockCommand");
424    PrintCXStringWithPrefixAndDispose(
425        "CommandName",
426        clang_BlockCommandComment_getCommandName(Comment));
427    break;
428  case CXComment_VerbatimBlockLine:
429    printf("CXComment_VerbatimBlockLine");
430    PrintCXStringWithPrefixAndDispose(
431        "Text",
432        clang_VerbatimBlockLineComment_getText(Comment));
433    break;
434  case CXComment_VerbatimLine:
435    printf("CXComment_VerbatimLine");
436    PrintCXStringWithPrefixAndDispose(
437        "Text",
438        clang_VerbatimLineComment_getText(Comment));
439    break;
440  case CXComment_FullComment:
441    printf("CXComment_FullComment");
442    break;
443  }
444  if (Kind != CXComment_Null) {
445    const unsigned NumChildren = clang_Comment_getNumChildren(Comment);
446    unsigned i;
447    for (i = 0; i != NumChildren; ++i) {
448      printf("\n// %s: ", FileCheckPrefix);
449      DumpCXCommentInternal(Ctx, clang_Comment_getChild(Comment, i));
450    }
451  }
452  printf(")");
453  Ctx->IndentLevel--;
454}
455
456static void DumpCXComment(CXComment Comment) {
457  struct CommentASTDumpingContext Ctx;
458  Ctx.IndentLevel = 1;
459  printf("\n// %s:  CommentAST=[\n// %s:", FileCheckPrefix, FileCheckPrefix);
460  DumpCXCommentInternal(&Ctx, Comment);
461  printf("]");
462}
463
464typedef struct {
465  const char *CommentSchemaFile;
466#ifdef CLANG_HAVE_LIBXML
467  xmlRelaxNGParserCtxtPtr RNGParser;
468  xmlRelaxNGPtr Schema;
469#endif
470} CommentXMLValidationData;
471
472static void ValidateCommentXML(const char *Str,
473                               CommentXMLValidationData *ValidationData) {
474#ifdef CLANG_HAVE_LIBXML
475  xmlDocPtr Doc;
476  xmlRelaxNGValidCtxtPtr ValidationCtxt;
477  int status;
478
479  if (!ValidationData || !ValidationData->CommentSchemaFile)
480    return;
481
482  if (!ValidationData->RNGParser) {
483    ValidationData->RNGParser =
484        xmlRelaxNGNewParserCtxt(ValidationData->CommentSchemaFile);
485    ValidationData->Schema = xmlRelaxNGParse(ValidationData->RNGParser);
486  }
487  if (!ValidationData->RNGParser) {
488    printf(" libXMLError");
489    return;
490  }
491
492  Doc = xmlParseDoc((const xmlChar *) Str);
493
494  if (!Doc) {
495    xmlErrorPtr Error = xmlGetLastError();
496    printf(" CommentXMLInvalid [not well-formed XML: %s]", Error->message);
497    return;
498  }
499
500  ValidationCtxt = xmlRelaxNGNewValidCtxt(ValidationData->Schema);
501  status = xmlRelaxNGValidateDoc(ValidationCtxt, Doc);
502  if (!status)
503    printf(" CommentXMLValid");
504  else if (status > 0) {
505    xmlErrorPtr Error = xmlGetLastError();
506    printf(" CommentXMLInvalid [not vaild XML: %s]", Error->message);
507  } else
508    printf(" libXMLError");
509
510  xmlRelaxNGFreeValidCtxt(ValidationCtxt);
511  xmlFreeDoc(Doc);
512#endif
513}
514
515static void PrintCursorComments(CXCursor Cursor,
516                                CommentXMLValidationData *ValidationData) {
517  {
518    CXString RawComment;
519    const char *RawCommentCString;
520    CXString BriefComment;
521    const char *BriefCommentCString;
522
523    RawComment = clang_Cursor_getRawCommentText(Cursor);
524    RawCommentCString = clang_getCString(RawComment);
525    if (RawCommentCString != NULL && RawCommentCString[0] != '\0') {
526      PrintCStringWithPrefix("RawComment", RawCommentCString);
527      PrintRange(clang_Cursor_getCommentRange(Cursor), "RawCommentRange");
528
529      BriefComment = clang_Cursor_getBriefCommentText(Cursor);
530      BriefCommentCString = clang_getCString(BriefComment);
531      if (BriefCommentCString != NULL && BriefCommentCString[0] != '\0')
532        PrintCStringWithPrefix("BriefComment", BriefCommentCString);
533      clang_disposeString(BriefComment);
534    }
535    clang_disposeString(RawComment);
536  }
537
538  {
539    CXComment Comment = clang_Cursor_getParsedComment(Cursor);
540    if (clang_Comment_getKind(Comment) != CXComment_Null) {
541      PrintCXStringWithPrefixAndDispose("FullCommentAsHTML",
542                                        clang_FullComment_getAsHTML(Comment));
543      {
544        CXString XML;
545        XML = clang_FullComment_getAsXML(Comment);
546        PrintCXStringWithPrefix("FullCommentAsXML", XML);
547        ValidateCommentXML(clang_getCString(XML), ValidationData);
548        clang_disposeString(XML);
549      }
550
551      DumpCXComment(Comment);
552    }
553  }
554}
555
556typedef struct {
557  unsigned line;
558  unsigned col;
559} LineCol;
560
561static int lineCol_cmp(const void *p1, const void *p2) {
562  const LineCol *lhs = p1;
563  const LineCol *rhs = p2;
564  if (lhs->line != rhs->line)
565    return (int)lhs->line - (int)rhs->line;
566  return (int)lhs->col - (int)rhs->col;
567}
568
569static void PrintCursor(CXCursor Cursor,
570                        CommentXMLValidationData *ValidationData) {
571  CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor);
572  if (clang_isInvalid(Cursor.kind)) {
573    CXString ks = clang_getCursorKindSpelling(Cursor.kind);
574    printf("Invalid Cursor => %s", clang_getCString(ks));
575    clang_disposeString(ks);
576  }
577  else {
578    CXString string, ks;
579    CXCursor Referenced;
580    unsigned line, column;
581    CXCursor SpecializationOf;
582    CXCursor *overridden;
583    unsigned num_overridden;
584    unsigned RefNameRangeNr;
585    CXSourceRange CursorExtent;
586    CXSourceRange RefNameRange;
587    int AlwaysUnavailable;
588    int AlwaysDeprecated;
589    CXString UnavailableMessage;
590    CXString DeprecatedMessage;
591    CXPlatformAvailability PlatformAvailability[2];
592    int NumPlatformAvailability;
593    int I;
594
595    ks = clang_getCursorKindSpelling(Cursor.kind);
596    string = want_display_name? clang_getCursorDisplayName(Cursor)
597                              : clang_getCursorSpelling(Cursor);
598    printf("%s=%s", clang_getCString(ks),
599                    clang_getCString(string));
600    clang_disposeString(ks);
601    clang_disposeString(string);
602
603    Referenced = clang_getCursorReferenced(Cursor);
604    if (!clang_equalCursors(Referenced, clang_getNullCursor())) {
605      if (clang_getCursorKind(Referenced) == CXCursor_OverloadedDeclRef) {
606        unsigned I, N = clang_getNumOverloadedDecls(Referenced);
607        printf("[");
608        for (I = 0; I != N; ++I) {
609          CXCursor Ovl = clang_getOverloadedDecl(Referenced, I);
610          CXSourceLocation Loc;
611          if (I)
612            printf(", ");
613
614          Loc = clang_getCursorLocation(Ovl);
615          clang_getSpellingLocation(Loc, 0, &line, &column, 0);
616          printf("%d:%d", line, column);
617        }
618        printf("]");
619      } else {
620        CXSourceLocation Loc = clang_getCursorLocation(Referenced);
621        clang_getSpellingLocation(Loc, 0, &line, &column, 0);
622        printf(":%d:%d", line, column);
623      }
624    }
625
626    if (clang_isCursorDefinition(Cursor))
627      printf(" (Definition)");
628
629    switch (clang_getCursorAvailability(Cursor)) {
630      case CXAvailability_Available:
631        break;
632
633      case CXAvailability_Deprecated:
634        printf(" (deprecated)");
635        break;
636
637      case CXAvailability_NotAvailable:
638        printf(" (unavailable)");
639        break;
640
641      case CXAvailability_NotAccessible:
642        printf(" (inaccessible)");
643        break;
644    }
645
646    NumPlatformAvailability
647      = clang_getCursorPlatformAvailability(Cursor,
648                                            &AlwaysDeprecated,
649                                            &DeprecatedMessage,
650                                            &AlwaysUnavailable,
651                                            &UnavailableMessage,
652                                            PlatformAvailability, 2);
653    if (AlwaysUnavailable) {
654      printf("  (always unavailable: \"%s\")",
655             clang_getCString(UnavailableMessage));
656    } else if (AlwaysDeprecated) {
657      printf("  (always deprecated: \"%s\")",
658             clang_getCString(DeprecatedMessage));
659    } else {
660      for (I = 0; I != NumPlatformAvailability; ++I) {
661        if (I >= 2)
662          break;
663
664        printf("  (%s", clang_getCString(PlatformAvailability[I].Platform));
665        if (PlatformAvailability[I].Unavailable)
666          printf(", unavailable");
667        else {
668          printVersion(", introduced=", PlatformAvailability[I].Introduced);
669          printVersion(", deprecated=", PlatformAvailability[I].Deprecated);
670          printVersion(", obsoleted=", PlatformAvailability[I].Obsoleted);
671        }
672        if (clang_getCString(PlatformAvailability[I].Message)[0])
673          printf(", message=\"%s\"",
674                 clang_getCString(PlatformAvailability[I].Message));
675        printf(")");
676      }
677    }
678    for (I = 0; I != NumPlatformAvailability; ++I) {
679      if (I >= 2)
680        break;
681      clang_disposeCXPlatformAvailability(PlatformAvailability + I);
682    }
683
684    clang_disposeString(DeprecatedMessage);
685    clang_disposeString(UnavailableMessage);
686
687    if (clang_CXXMethod_isStatic(Cursor))
688      printf(" (static)");
689    if (clang_CXXMethod_isVirtual(Cursor))
690      printf(" (virtual)");
691
692    if (Cursor.kind == CXCursor_IBOutletCollectionAttr) {
693      CXType T =
694        clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor));
695      CXString S = clang_getTypeKindSpelling(T.kind);
696      printf(" [IBOutletCollection=%s]", clang_getCString(S));
697      clang_disposeString(S);
698    }
699
700    if (Cursor.kind == CXCursor_CXXBaseSpecifier) {
701      enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor);
702      unsigned isVirtual = clang_isVirtualBase(Cursor);
703      const char *accessStr = 0;
704
705      switch (access) {
706        case CX_CXXInvalidAccessSpecifier:
707          accessStr = "invalid"; break;
708        case CX_CXXPublic:
709          accessStr = "public"; break;
710        case CX_CXXProtected:
711          accessStr = "protected"; break;
712        case CX_CXXPrivate:
713          accessStr = "private"; break;
714      }
715
716      printf(" [access=%s isVirtual=%s]", accessStr,
717             isVirtual ? "true" : "false");
718    }
719
720    SpecializationOf = clang_getSpecializedCursorTemplate(Cursor);
721    if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) {
722      CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf);
723      CXString Name = clang_getCursorSpelling(SpecializationOf);
724      clang_getSpellingLocation(Loc, 0, &line, &column, 0);
725      printf(" [Specialization of %s:%d:%d]",
726             clang_getCString(Name), line, column);
727      clang_disposeString(Name);
728    }
729
730    clang_getOverriddenCursors(Cursor, &overridden, &num_overridden);
731    if (num_overridden) {
732      unsigned I;
733      LineCol lineCols[50];
734      assert(num_overridden <= 50);
735      printf(" [Overrides ");
736      for (I = 0; I != num_overridden; ++I) {
737        CXSourceLocation Loc = clang_getCursorLocation(overridden[I]);
738        clang_getSpellingLocation(Loc, 0, &line, &column, 0);
739        lineCols[I].line = line;
740        lineCols[I].col = column;
741      }
742      /* Make the order of the override list deterministic. */
743      qsort(lineCols, num_overridden, sizeof(LineCol), lineCol_cmp);
744      for (I = 0; I != num_overridden; ++I) {
745        if (I)
746          printf(", ");
747        printf("@%d:%d", lineCols[I].line, lineCols[I].col);
748      }
749      printf("]");
750      clang_disposeOverriddenCursors(overridden);
751    }
752
753    if (Cursor.kind == CXCursor_InclusionDirective) {
754      CXFile File = clang_getIncludedFile(Cursor);
755      CXString Included = clang_getFileName(File);
756      printf(" (%s)", clang_getCString(Included));
757      clang_disposeString(Included);
758
759      if (clang_isFileMultipleIncludeGuarded(TU, File))
760        printf("  [multi-include guarded]");
761    }
762
763    CursorExtent = clang_getCursorExtent(Cursor);
764    RefNameRange = clang_getCursorReferenceNameRange(Cursor,
765                                                   CXNameRange_WantQualifier
766                                                 | CXNameRange_WantSinglePiece
767                                                 | CXNameRange_WantTemplateArgs,
768                                                     0);
769    if (!clang_equalRanges(CursorExtent, RefNameRange))
770      PrintRange(RefNameRange, "SingleRefName");
771
772    for (RefNameRangeNr = 0; 1; RefNameRangeNr++) {
773      RefNameRange = clang_getCursorReferenceNameRange(Cursor,
774                                                   CXNameRange_WantQualifier
775                                                 | CXNameRange_WantTemplateArgs,
776                                                       RefNameRangeNr);
777      if (clang_equalRanges(clang_getNullRange(), RefNameRange))
778        break;
779      if (!clang_equalRanges(CursorExtent, RefNameRange))
780        PrintRange(RefNameRange, "RefName");
781    }
782
783    PrintCursorComments(Cursor, ValidationData);
784  }
785}
786
787static const char* GetCursorSource(CXCursor Cursor) {
788  CXSourceLocation Loc = clang_getCursorLocation(Cursor);
789  CXString source;
790  CXFile file;
791  clang_getExpansionLocation(Loc, &file, 0, 0, 0);
792  source = clang_getFileName(file);
793  if (!clang_getCString(source)) {
794    clang_disposeString(source);
795    return "<invalid loc>";
796  }
797  else {
798    const char *b = basename(clang_getCString(source));
799    clang_disposeString(source);
800    return b;
801  }
802}
803
804/******************************************************************************/
805/* Callbacks.                                                                 */
806/******************************************************************************/
807
808typedef void (*PostVisitTU)(CXTranslationUnit);
809
810void PrintDiagnostic(CXDiagnostic Diagnostic) {
811  FILE *out = stderr;
812  CXFile file;
813  CXString Msg;
814  unsigned display_opts = CXDiagnostic_DisplaySourceLocation
815    | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges
816    | CXDiagnostic_DisplayOption;
817  unsigned i, num_fixits;
818
819  if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored)
820    return;
821
822  Msg = clang_formatDiagnostic(Diagnostic, display_opts);
823  fprintf(stderr, "%s\n", clang_getCString(Msg));
824  clang_disposeString(Msg);
825
826  clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic),
827                            &file, 0, 0, 0);
828  if (!file)
829    return;
830
831  num_fixits = clang_getDiagnosticNumFixIts(Diagnostic);
832  fprintf(stderr, "Number FIX-ITs = %d\n", num_fixits);
833  for (i = 0; i != num_fixits; ++i) {
834    CXSourceRange range;
835    CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range);
836    CXSourceLocation start = clang_getRangeStart(range);
837    CXSourceLocation end = clang_getRangeEnd(range);
838    unsigned start_line, start_column, end_line, end_column;
839    CXFile start_file, end_file;
840    clang_getSpellingLocation(start, &start_file, &start_line,
841                              &start_column, 0);
842    clang_getSpellingLocation(end, &end_file, &end_line, &end_column, 0);
843    if (clang_equalLocations(start, end)) {
844      /* Insertion. */
845      if (start_file == file)
846        fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n",
847                clang_getCString(insertion_text), start_line, start_column);
848    } else if (strcmp(clang_getCString(insertion_text), "") == 0) {
849      /* Removal. */
850      if (start_file == file && end_file == file) {
851        fprintf(out, "FIX-IT: Remove ");
852        PrintExtent(out, start_line, start_column, end_line, end_column);
853        fprintf(out, "\n");
854      }
855    } else {
856      /* Replacement. */
857      if (start_file == end_file) {
858        fprintf(out, "FIX-IT: Replace ");
859        PrintExtent(out, start_line, start_column, end_line, end_column);
860        fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text));
861      }
862      break;
863    }
864    clang_disposeString(insertion_text);
865  }
866}
867
868void PrintDiagnosticSet(CXDiagnosticSet Set) {
869  int i = 0, n = clang_getNumDiagnosticsInSet(Set);
870  for ( ; i != n ; ++i) {
871    CXDiagnostic Diag = clang_getDiagnosticInSet(Set, i);
872    CXDiagnosticSet ChildDiags = clang_getChildDiagnostics(Diag);
873    PrintDiagnostic(Diag);
874    if (ChildDiags)
875      PrintDiagnosticSet(ChildDiags);
876  }
877}
878
879void PrintDiagnostics(CXTranslationUnit TU) {
880  CXDiagnosticSet TUSet = clang_getDiagnosticSetFromTU(TU);
881  PrintDiagnosticSet(TUSet);
882  clang_disposeDiagnosticSet(TUSet);
883}
884
885void PrintMemoryUsage(CXTranslationUnit TU) {
886  unsigned long total = 0;
887  unsigned i = 0;
888  CXTUResourceUsage usage = clang_getCXTUResourceUsage(TU);
889  fprintf(stderr, "Memory usage:\n");
890  for (i = 0 ; i != usage.numEntries; ++i) {
891    const char *name = clang_getTUResourceUsageName(usage.entries[i].kind);
892    unsigned long amount = usage.entries[i].amount;
893    total += amount;
894    fprintf(stderr, "  %s : %ld bytes (%f MBytes)\n", name, amount,
895            ((double) amount)/(1024*1024));
896  }
897  fprintf(stderr, "  TOTAL = %ld bytes (%f MBytes)\n", total,
898          ((double) total)/(1024*1024));
899  clang_disposeCXTUResourceUsage(usage);
900}
901
902/******************************************************************************/
903/* Logic for testing traversal.                                               */
904/******************************************************************************/
905
906static void PrintCursorExtent(CXCursor C) {
907  CXSourceRange extent = clang_getCursorExtent(C);
908  PrintRange(extent, "Extent");
909}
910
911/* Data used by the visitors. */
912typedef struct {
913  CXTranslationUnit TU;
914  enum CXCursorKind *Filter;
915  CommentXMLValidationData ValidationData;
916} VisitorData;
917
918
919enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor,
920                                                CXCursor Parent,
921                                                CXClientData ClientData) {
922  VisitorData *Data = (VisitorData *)ClientData;
923  if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) {
924    CXSourceLocation Loc = clang_getCursorLocation(Cursor);
925    unsigned line, column;
926    clang_getSpellingLocation(Loc, 0, &line, &column, 0);
927    printf("// %s: %s:%d:%d: ", FileCheckPrefix,
928           GetCursorSource(Cursor), line, column);
929    PrintCursor(Cursor, &Data->ValidationData);
930    PrintCursorExtent(Cursor);
931    printf("\n");
932    return CXChildVisit_Recurse;
933  }
934
935  return CXChildVisit_Continue;
936}
937
938static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor,
939                                                   CXCursor Parent,
940                                                   CXClientData ClientData) {
941  const char *startBuf, *endBuf;
942  unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn;
943  CXCursor Ref;
944  VisitorData *Data = (VisitorData *)ClientData;
945
946  if (Cursor.kind != CXCursor_FunctionDecl ||
947      !clang_isCursorDefinition(Cursor))
948    return CXChildVisit_Continue;
949
950  clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf,
951                                       &startLine, &startColumn,
952                                       &endLine, &endColumn);
953  /* Probe the entire body, looking for both decls and refs. */
954  curLine = startLine;
955  curColumn = startColumn;
956
957  while (startBuf < endBuf) {
958    CXSourceLocation Loc;
959    CXFile file;
960    CXString source;
961
962    if (*startBuf == '\n') {
963      startBuf++;
964      curLine++;
965      curColumn = 1;
966    } else if (*startBuf != '\t')
967      curColumn++;
968
969    Loc = clang_getCursorLocation(Cursor);
970    clang_getSpellingLocation(Loc, &file, 0, 0, 0);
971
972    source = clang_getFileName(file);
973    if (clang_getCString(source)) {
974      CXSourceLocation RefLoc
975        = clang_getLocation(Data->TU, file, curLine, curColumn);
976      Ref = clang_getCursor(Data->TU, RefLoc);
977      if (Ref.kind == CXCursor_NoDeclFound) {
978        /* Nothing found here; that's fine. */
979      } else if (Ref.kind != CXCursor_FunctionDecl) {
980        printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref),
981               curLine, curColumn);
982        PrintCursor(Ref, &Data->ValidationData);
983        printf("\n");
984      }
985    }
986    clang_disposeString(source);
987    startBuf++;
988  }
989
990  return CXChildVisit_Continue;
991}
992
993/******************************************************************************/
994/* USR testing.                                                               */
995/******************************************************************************/
996
997enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent,
998                                   CXClientData ClientData) {
999  VisitorData *Data = (VisitorData *)ClientData;
1000  if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) {
1001    CXString USR = clang_getCursorUSR(C);
1002    const char *cstr = clang_getCString(USR);
1003    if (!cstr || cstr[0] == '\0') {
1004      clang_disposeString(USR);
1005      return CXChildVisit_Recurse;
1006    }
1007    printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr);
1008
1009    PrintCursorExtent(C);
1010    printf("\n");
1011    clang_disposeString(USR);
1012
1013    return CXChildVisit_Recurse;
1014  }
1015
1016  return CXChildVisit_Continue;
1017}
1018
1019/******************************************************************************/
1020/* Inclusion stack testing.                                                   */
1021/******************************************************************************/
1022
1023void InclusionVisitor(CXFile includedFile, CXSourceLocation *includeStack,
1024                      unsigned includeStackLen, CXClientData data) {
1025
1026  unsigned i;
1027  CXString fname;
1028
1029  fname = clang_getFileName(includedFile);
1030  printf("file: %s\nincluded by:\n", clang_getCString(fname));
1031  clang_disposeString(fname);
1032
1033  for (i = 0; i < includeStackLen; ++i) {
1034    CXFile includingFile;
1035    unsigned line, column;
1036    clang_getSpellingLocation(includeStack[i], &includingFile, &line,
1037                              &column, 0);
1038    fname = clang_getFileName(includingFile);
1039    printf("  %s:%d:%d\n", clang_getCString(fname), line, column);
1040    clang_disposeString(fname);
1041  }
1042  printf("\n");
1043}
1044
1045void PrintInclusionStack(CXTranslationUnit TU) {
1046  clang_getInclusions(TU, InclusionVisitor, NULL);
1047}
1048
1049/******************************************************************************/
1050/* Linkage testing.                                                           */
1051/******************************************************************************/
1052
1053static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p,
1054                                            CXClientData d) {
1055  const char *linkage = 0;
1056
1057  if (clang_isInvalid(clang_getCursorKind(cursor)))
1058    return CXChildVisit_Recurse;
1059
1060  switch (clang_getCursorLinkage(cursor)) {
1061    case CXLinkage_Invalid: break;
1062    case CXLinkage_NoLinkage: linkage = "NoLinkage"; break;
1063    case CXLinkage_Internal: linkage = "Internal"; break;
1064    case CXLinkage_UniqueExternal: linkage = "UniqueExternal"; break;
1065    case CXLinkage_External: linkage = "External"; break;
1066  }
1067
1068  if (linkage) {
1069    PrintCursor(cursor, NULL);
1070    printf("linkage=%s\n", linkage);
1071  }
1072
1073  return CXChildVisit_Recurse;
1074}
1075
1076/******************************************************************************/
1077/* Typekind testing.                                                          */
1078/******************************************************************************/
1079
1080static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p,
1081                                             CXClientData d) {
1082  if (!clang_isInvalid(clang_getCursorKind(cursor))) {
1083    CXType T = clang_getCursorType(cursor);
1084    CXString S = clang_getTypeKindSpelling(T.kind);
1085    PrintCursor(cursor, NULL);
1086    printf(" typekind=%s", clang_getCString(S));
1087    if (clang_isConstQualifiedType(T))
1088      printf(" const");
1089    if (clang_isVolatileQualifiedType(T))
1090      printf(" volatile");
1091    if (clang_isRestrictQualifiedType(T))
1092      printf(" restrict");
1093    clang_disposeString(S);
1094    /* Print the canonical type if it is different. */
1095    {
1096      CXType CT = clang_getCanonicalType(T);
1097      if (!clang_equalTypes(T, CT)) {
1098        CXString CS = clang_getTypeKindSpelling(CT.kind);
1099        printf(" [canonical=%s]", clang_getCString(CS));
1100        clang_disposeString(CS);
1101      }
1102    }
1103    /* Print the return type if it exists. */
1104    {
1105      CXType RT = clang_getCursorResultType(cursor);
1106      if (RT.kind != CXType_Invalid) {
1107        CXString RS = clang_getTypeKindSpelling(RT.kind);
1108        printf(" [result=%s]", clang_getCString(RS));
1109        clang_disposeString(RS);
1110      }
1111    }
1112    /* Print the argument types if they exist. */
1113    {
1114      int numArgs = clang_Cursor_getNumArguments(cursor);
1115      if (numArgs != -1 && numArgs != 0) {
1116        int i;
1117        printf(" [args=");
1118        for (i = 0; i < numArgs; ++i) {
1119          CXType T = clang_getCursorType(clang_Cursor_getArgument(cursor, i));
1120          if (T.kind != CXType_Invalid) {
1121            CXString S = clang_getTypeKindSpelling(T.kind);
1122            printf(" %s", clang_getCString(S));
1123            clang_disposeString(S);
1124          }
1125        }
1126        printf("]");
1127      }
1128    }
1129    /* Print if this is a non-POD type. */
1130    printf(" [isPOD=%d]", clang_isPODType(T));
1131
1132    printf("\n");
1133  }
1134  return CXChildVisit_Recurse;
1135}
1136
1137
1138/******************************************************************************/
1139/* Loading ASTs/source.                                                       */
1140/******************************************************************************/
1141
1142static int perform_test_load(CXIndex Idx, CXTranslationUnit TU,
1143                             const char *filter, const char *prefix,
1144                             CXCursorVisitor Visitor,
1145                             PostVisitTU PV,
1146                             const char *CommentSchemaFile) {
1147
1148  if (prefix)
1149    FileCheckPrefix = prefix;
1150
1151  if (Visitor) {
1152    enum CXCursorKind K = CXCursor_NotImplemented;
1153    enum CXCursorKind *ck = &K;
1154    VisitorData Data;
1155
1156    /* Perform some simple filtering. */
1157    if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL;
1158    else if (!strcmp(filter, "all-display") ||
1159             !strcmp(filter, "local-display")) {
1160      ck = NULL;
1161      want_display_name = 1;
1162    }
1163    else if (!strcmp(filter, "none")) K = (enum CXCursorKind) ~0;
1164    else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl;
1165    else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl;
1166    else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl;
1167    else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl;
1168    else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl;
1169    else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor;
1170    else {
1171      fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter);
1172      return 1;
1173    }
1174
1175    Data.TU = TU;
1176    Data.Filter = ck;
1177    Data.ValidationData.CommentSchemaFile = CommentSchemaFile;
1178#ifdef CLANG_HAVE_LIBXML
1179    Data.ValidationData.RNGParser = NULL;
1180    Data.ValidationData.Schema = NULL;
1181#endif
1182    clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data);
1183  }
1184
1185  if (PV)
1186    PV(TU);
1187
1188  PrintDiagnostics(TU);
1189  if (checkForErrors(TU) != 0) {
1190    clang_disposeTranslationUnit(TU);
1191    return -1;
1192  }
1193
1194  clang_disposeTranslationUnit(TU);
1195  return 0;
1196}
1197
1198int perform_test_load_tu(const char *file, const char *filter,
1199                         const char *prefix, CXCursorVisitor Visitor,
1200                         PostVisitTU PV) {
1201  CXIndex Idx;
1202  CXTranslationUnit TU;
1203  int result;
1204  Idx = clang_createIndex(/* excludeDeclsFromPCH */
1205                          !strcmp(filter, "local") ? 1 : 0,
1206                          /* displayDiagnosics=*/1);
1207
1208  if (!CreateTranslationUnit(Idx, file, &TU)) {
1209    clang_disposeIndex(Idx);
1210    return 1;
1211  }
1212
1213  result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV, NULL);
1214  clang_disposeIndex(Idx);
1215  return result;
1216}
1217
1218int perform_test_load_source(int argc, const char **argv,
1219                             const char *filter, CXCursorVisitor Visitor,
1220                             PostVisitTU PV) {
1221  CXIndex Idx;
1222  CXTranslationUnit TU;
1223  const char *CommentSchemaFile;
1224  struct CXUnsavedFile *unsaved_files = 0;
1225  int num_unsaved_files = 0;
1226  int result;
1227
1228  Idx = clang_createIndex(/* excludeDeclsFromPCH */
1229                          (!strcmp(filter, "local") ||
1230                           !strcmp(filter, "local-display"))? 1 : 0,
1231                          /* displayDiagnosics=*/0);
1232
1233  if ((CommentSchemaFile = parse_comments_schema(argc, argv))) {
1234    argc--;
1235    argv++;
1236  }
1237
1238  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
1239    clang_disposeIndex(Idx);
1240    return -1;
1241  }
1242
1243  TU = clang_parseTranslationUnit(Idx, 0,
1244                                  argv + num_unsaved_files,
1245                                  argc - num_unsaved_files,
1246                                  unsaved_files, num_unsaved_files,
1247                                  getDefaultParsingOptions());
1248  if (!TU) {
1249    fprintf(stderr, "Unable to load translation unit!\n");
1250    free_remapped_files(unsaved_files, num_unsaved_files);
1251    clang_disposeIndex(Idx);
1252    return 1;
1253  }
1254
1255  result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV,
1256                             CommentSchemaFile);
1257  free_remapped_files(unsaved_files, num_unsaved_files);
1258  clang_disposeIndex(Idx);
1259  return result;
1260}
1261
1262int perform_test_reparse_source(int argc, const char **argv, int trials,
1263                                const char *filter, CXCursorVisitor Visitor,
1264                                PostVisitTU PV) {
1265  CXIndex Idx;
1266  CXTranslationUnit TU;
1267  struct CXUnsavedFile *unsaved_files = 0;
1268  int num_unsaved_files = 0;
1269  int result;
1270  int trial;
1271  int remap_after_trial = 0;
1272  char *endptr = 0;
1273
1274  Idx = clang_createIndex(/* excludeDeclsFromPCH */
1275                          !strcmp(filter, "local") ? 1 : 0,
1276                          /* displayDiagnosics=*/0);
1277
1278  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
1279    clang_disposeIndex(Idx);
1280    return -1;
1281  }
1282
1283  /* Load the initial translation unit -- we do this without honoring remapped
1284   * files, so that we have a way to test results after changing the source. */
1285  TU = clang_parseTranslationUnit(Idx, 0,
1286                                  argv + num_unsaved_files,
1287                                  argc - num_unsaved_files,
1288                                  0, 0, getDefaultParsingOptions());
1289  if (!TU) {
1290    fprintf(stderr, "Unable to load translation unit!\n");
1291    free_remapped_files(unsaved_files, num_unsaved_files);
1292    clang_disposeIndex(Idx);
1293    return 1;
1294  }
1295
1296  if (checkForErrors(TU) != 0)
1297    return -1;
1298
1299  if (getenv("CINDEXTEST_REMAP_AFTER_TRIAL")) {
1300    remap_after_trial =
1301        strtol(getenv("CINDEXTEST_REMAP_AFTER_TRIAL"), &endptr, 10);
1302  }
1303
1304  for (trial = 0; trial < trials; ++trial) {
1305    if (clang_reparseTranslationUnit(TU,
1306                             trial >= remap_after_trial ? num_unsaved_files : 0,
1307                             trial >= remap_after_trial ? unsaved_files : 0,
1308                                     clang_defaultReparseOptions(TU))) {
1309      fprintf(stderr, "Unable to reparse translation unit!\n");
1310      clang_disposeTranslationUnit(TU);
1311      free_remapped_files(unsaved_files, num_unsaved_files);
1312      clang_disposeIndex(Idx);
1313      return -1;
1314    }
1315
1316    if (checkForErrors(TU) != 0)
1317      return -1;
1318  }
1319
1320  result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV, NULL);
1321
1322  free_remapped_files(unsaved_files, num_unsaved_files);
1323  clang_disposeIndex(Idx);
1324  return result;
1325}
1326
1327/******************************************************************************/
1328/* Logic for testing clang_getCursor().                                       */
1329/******************************************************************************/
1330
1331static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor,
1332                                   unsigned start_line, unsigned start_col,
1333                                   unsigned end_line, unsigned end_col,
1334                                   const char *prefix) {
1335  printf("// %s: ", FileCheckPrefix);
1336  if (prefix)
1337    printf("-%s", prefix);
1338  PrintExtent(stdout, start_line, start_col, end_line, end_col);
1339  printf(" ");
1340  PrintCursor(cursor, NULL);
1341  printf("\n");
1342}
1343
1344static int perform_file_scan(const char *ast_file, const char *source_file,
1345                             const char *prefix) {
1346  CXIndex Idx;
1347  CXTranslationUnit TU;
1348  FILE *fp;
1349  CXCursor prevCursor = clang_getNullCursor();
1350  CXFile file;
1351  unsigned line = 1, col = 1;
1352  unsigned start_line = 1, start_col = 1;
1353
1354  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
1355                                /* displayDiagnosics=*/1))) {
1356    fprintf(stderr, "Could not create Index\n");
1357    return 1;
1358  }
1359
1360  if (!CreateTranslationUnit(Idx, ast_file, &TU))
1361    return 1;
1362
1363  if ((fp = fopen(source_file, "r")) == NULL) {
1364    fprintf(stderr, "Could not open '%s'\n", source_file);
1365    return 1;
1366  }
1367
1368  file = clang_getFile(TU, source_file);
1369  for (;;) {
1370    CXCursor cursor;
1371    int c = fgetc(fp);
1372
1373    if (c == '\n') {
1374      ++line;
1375      col = 1;
1376    } else
1377      ++col;
1378
1379    /* Check the cursor at this position, and dump the previous one if we have
1380     * found something new.
1381     */
1382    cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col));
1383    if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) &&
1384        prevCursor.kind != CXCursor_InvalidFile) {
1385      print_cursor_file_scan(TU, prevCursor, start_line, start_col,
1386                             line, col, prefix);
1387      start_line = line;
1388      start_col = col;
1389    }
1390    if (c == EOF)
1391      break;
1392
1393    prevCursor = cursor;
1394  }
1395
1396  fclose(fp);
1397  clang_disposeTranslationUnit(TU);
1398  clang_disposeIndex(Idx);
1399  return 0;
1400}
1401
1402/******************************************************************************/
1403/* Logic for testing clang code completion.                                   */
1404/******************************************************************************/
1405
1406/* Parse file:line:column from the input string. Returns 0 on success, non-zero
1407   on failure. If successful, the pointer *filename will contain newly-allocated
1408   memory (that will be owned by the caller) to store the file name. */
1409int parse_file_line_column(const char *input, char **filename, unsigned *line,
1410                           unsigned *column, unsigned *second_line,
1411                           unsigned *second_column) {
1412  /* Find the second colon. */
1413  const char *last_colon = strrchr(input, ':');
1414  unsigned values[4], i;
1415  unsigned num_values = (second_line && second_column)? 4 : 2;
1416
1417  char *endptr = 0;
1418  if (!last_colon || last_colon == input) {
1419    if (num_values == 4)
1420      fprintf(stderr, "could not parse filename:line:column:line:column in "
1421              "'%s'\n", input);
1422    else
1423      fprintf(stderr, "could not parse filename:line:column in '%s'\n", input);
1424    return 1;
1425  }
1426
1427  for (i = 0; i != num_values; ++i) {
1428    const char *prev_colon;
1429
1430    /* Parse the next line or column. */
1431    values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10);
1432    if (*endptr != 0 && *endptr != ':') {
1433      fprintf(stderr, "could not parse %s in '%s'\n",
1434              (i % 2 ? "column" : "line"), input);
1435      return 1;
1436    }
1437
1438    if (i + 1 == num_values)
1439      break;
1440
1441    /* Find the previous colon. */
1442    prev_colon = last_colon - 1;
1443    while (prev_colon != input && *prev_colon != ':')
1444      --prev_colon;
1445    if (prev_colon == input) {
1446      fprintf(stderr, "could not parse %s in '%s'\n",
1447              (i % 2 == 0? "column" : "line"), input);
1448      return 1;
1449    }
1450
1451    last_colon = prev_colon;
1452  }
1453
1454  *line = values[0];
1455  *column = values[1];
1456
1457  if (second_line && second_column) {
1458    *second_line = values[2];
1459    *second_column = values[3];
1460  }
1461
1462  /* Copy the file name. */
1463  *filename = (char*)malloc(last_colon - input + 1);
1464  memcpy(*filename, input, last_colon - input);
1465  (*filename)[last_colon - input] = 0;
1466  return 0;
1467}
1468
1469const char *
1470clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) {
1471  switch (Kind) {
1472  case CXCompletionChunk_Optional: return "Optional";
1473  case CXCompletionChunk_TypedText: return "TypedText";
1474  case CXCompletionChunk_Text: return "Text";
1475  case CXCompletionChunk_Placeholder: return "Placeholder";
1476  case CXCompletionChunk_Informative: return "Informative";
1477  case CXCompletionChunk_CurrentParameter: return "CurrentParameter";
1478  case CXCompletionChunk_LeftParen: return "LeftParen";
1479  case CXCompletionChunk_RightParen: return "RightParen";
1480  case CXCompletionChunk_LeftBracket: return "LeftBracket";
1481  case CXCompletionChunk_RightBracket: return "RightBracket";
1482  case CXCompletionChunk_LeftBrace: return "LeftBrace";
1483  case CXCompletionChunk_RightBrace: return "RightBrace";
1484  case CXCompletionChunk_LeftAngle: return "LeftAngle";
1485  case CXCompletionChunk_RightAngle: return "RightAngle";
1486  case CXCompletionChunk_Comma: return "Comma";
1487  case CXCompletionChunk_ResultType: return "ResultType";
1488  case CXCompletionChunk_Colon: return "Colon";
1489  case CXCompletionChunk_SemiColon: return "SemiColon";
1490  case CXCompletionChunk_Equal: return "Equal";
1491  case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace";
1492  case CXCompletionChunk_VerticalSpace: return "VerticalSpace";
1493  }
1494
1495  return "Unknown";
1496}
1497
1498static int checkForErrors(CXTranslationUnit TU) {
1499  unsigned Num, i;
1500  CXDiagnostic Diag;
1501  CXString DiagStr;
1502
1503  if (!getenv("CINDEXTEST_FAILONERROR"))
1504    return 0;
1505
1506  Num = clang_getNumDiagnostics(TU);
1507  for (i = 0; i != Num; ++i) {
1508    Diag = clang_getDiagnostic(TU, i);
1509    if (clang_getDiagnosticSeverity(Diag) >= CXDiagnostic_Error) {
1510      DiagStr = clang_formatDiagnostic(Diag,
1511                                       clang_defaultDiagnosticDisplayOptions());
1512      fprintf(stderr, "%s\n", clang_getCString(DiagStr));
1513      clang_disposeString(DiagStr);
1514      clang_disposeDiagnostic(Diag);
1515      return -1;
1516    }
1517    clang_disposeDiagnostic(Diag);
1518  }
1519
1520  return 0;
1521}
1522
1523void print_completion_string(CXCompletionString completion_string, FILE *file) {
1524  int I, N;
1525
1526  N = clang_getNumCompletionChunks(completion_string);
1527  for (I = 0; I != N; ++I) {
1528    CXString text;
1529    const char *cstr;
1530    enum CXCompletionChunkKind Kind
1531      = clang_getCompletionChunkKind(completion_string, I);
1532
1533    if (Kind == CXCompletionChunk_Optional) {
1534      fprintf(file, "{Optional ");
1535      print_completion_string(
1536                clang_getCompletionChunkCompletionString(completion_string, I),
1537                              file);
1538      fprintf(file, "}");
1539      continue;
1540    }
1541
1542    if (Kind == CXCompletionChunk_VerticalSpace) {
1543      fprintf(file, "{VerticalSpace  }");
1544      continue;
1545    }
1546
1547    text = clang_getCompletionChunkText(completion_string, I);
1548    cstr = clang_getCString(text);
1549    fprintf(file, "{%s %s}",
1550            clang_getCompletionChunkKindSpelling(Kind),
1551            cstr ? cstr : "");
1552    clang_disposeString(text);
1553  }
1554
1555}
1556
1557void print_completion_result(CXCompletionResult *completion_result,
1558                             CXClientData client_data) {
1559  FILE *file = (FILE *)client_data;
1560  CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind);
1561  unsigned annotationCount;
1562  enum CXCursorKind ParentKind;
1563  CXString ParentName;
1564  CXString BriefComment;
1565  const char *BriefCommentCString;
1566
1567  fprintf(file, "%s:", clang_getCString(ks));
1568  clang_disposeString(ks);
1569
1570  print_completion_string(completion_result->CompletionString, file);
1571  fprintf(file, " (%u)",
1572          clang_getCompletionPriority(completion_result->CompletionString));
1573  switch (clang_getCompletionAvailability(completion_result->CompletionString)){
1574  case CXAvailability_Available:
1575    break;
1576
1577  case CXAvailability_Deprecated:
1578    fprintf(file, " (deprecated)");
1579    break;
1580
1581  case CXAvailability_NotAvailable:
1582    fprintf(file, " (unavailable)");
1583    break;
1584
1585  case CXAvailability_NotAccessible:
1586    fprintf(file, " (inaccessible)");
1587    break;
1588  }
1589
1590  annotationCount = clang_getCompletionNumAnnotations(
1591        completion_result->CompletionString);
1592  if (annotationCount) {
1593    unsigned i;
1594    fprintf(file, " (");
1595    for (i = 0; i < annotationCount; ++i) {
1596      if (i != 0)
1597        fprintf(file, ", ");
1598      fprintf(file, "\"%s\"",
1599              clang_getCString(clang_getCompletionAnnotation(
1600                                 completion_result->CompletionString, i)));
1601    }
1602    fprintf(file, ")");
1603  }
1604
1605  if (!getenv("CINDEXTEST_NO_COMPLETION_PARENTS")) {
1606    ParentName = clang_getCompletionParent(completion_result->CompletionString,
1607                                           &ParentKind);
1608    if (ParentKind != CXCursor_NotImplemented) {
1609      CXString KindSpelling = clang_getCursorKindSpelling(ParentKind);
1610      fprintf(file, " (parent: %s '%s')",
1611              clang_getCString(KindSpelling),
1612              clang_getCString(ParentName));
1613      clang_disposeString(KindSpelling);
1614    }
1615    clang_disposeString(ParentName);
1616  }
1617
1618  BriefComment = clang_getCompletionBriefComment(
1619                                        completion_result->CompletionString);
1620  BriefCommentCString = clang_getCString(BriefComment);
1621  if (BriefCommentCString && *BriefCommentCString != '\0') {
1622    fprintf(file, "(brief comment: %s)", BriefCommentCString);
1623  }
1624  clang_disposeString(BriefComment);
1625
1626  fprintf(file, "\n");
1627}
1628
1629void print_completion_contexts(unsigned long long contexts, FILE *file) {
1630  fprintf(file, "Completion contexts:\n");
1631  if (contexts == CXCompletionContext_Unknown) {
1632    fprintf(file, "Unknown\n");
1633  }
1634  if (contexts & CXCompletionContext_AnyType) {
1635    fprintf(file, "Any type\n");
1636  }
1637  if (contexts & CXCompletionContext_AnyValue) {
1638    fprintf(file, "Any value\n");
1639  }
1640  if (contexts & CXCompletionContext_ObjCObjectValue) {
1641    fprintf(file, "Objective-C object value\n");
1642  }
1643  if (contexts & CXCompletionContext_ObjCSelectorValue) {
1644    fprintf(file, "Objective-C selector value\n");
1645  }
1646  if (contexts & CXCompletionContext_CXXClassTypeValue) {
1647    fprintf(file, "C++ class type value\n");
1648  }
1649  if (contexts & CXCompletionContext_DotMemberAccess) {
1650    fprintf(file, "Dot member access\n");
1651  }
1652  if (contexts & CXCompletionContext_ArrowMemberAccess) {
1653    fprintf(file, "Arrow member access\n");
1654  }
1655  if (contexts & CXCompletionContext_ObjCPropertyAccess) {
1656    fprintf(file, "Objective-C property access\n");
1657  }
1658  if (contexts & CXCompletionContext_EnumTag) {
1659    fprintf(file, "Enum tag\n");
1660  }
1661  if (contexts & CXCompletionContext_UnionTag) {
1662    fprintf(file, "Union tag\n");
1663  }
1664  if (contexts & CXCompletionContext_StructTag) {
1665    fprintf(file, "Struct tag\n");
1666  }
1667  if (contexts & CXCompletionContext_ClassTag) {
1668    fprintf(file, "Class name\n");
1669  }
1670  if (contexts & CXCompletionContext_Namespace) {
1671    fprintf(file, "Namespace or namespace alias\n");
1672  }
1673  if (contexts & CXCompletionContext_NestedNameSpecifier) {
1674    fprintf(file, "Nested name specifier\n");
1675  }
1676  if (contexts & CXCompletionContext_ObjCInterface) {
1677    fprintf(file, "Objective-C interface\n");
1678  }
1679  if (contexts & CXCompletionContext_ObjCProtocol) {
1680    fprintf(file, "Objective-C protocol\n");
1681  }
1682  if (contexts & CXCompletionContext_ObjCCategory) {
1683    fprintf(file, "Objective-C category\n");
1684  }
1685  if (contexts & CXCompletionContext_ObjCInstanceMessage) {
1686    fprintf(file, "Objective-C instance method\n");
1687  }
1688  if (contexts & CXCompletionContext_ObjCClassMessage) {
1689    fprintf(file, "Objective-C class method\n");
1690  }
1691  if (contexts & CXCompletionContext_ObjCSelectorName) {
1692    fprintf(file, "Objective-C selector name\n");
1693  }
1694  if (contexts & CXCompletionContext_MacroName) {
1695    fprintf(file, "Macro name\n");
1696  }
1697  if (contexts & CXCompletionContext_NaturalLanguage) {
1698    fprintf(file, "Natural language\n");
1699  }
1700}
1701
1702int my_stricmp(const char *s1, const char *s2) {
1703  while (*s1 && *s2) {
1704    int c1 = tolower((unsigned char)*s1), c2 = tolower((unsigned char)*s2);
1705    if (c1 < c2)
1706      return -1;
1707    else if (c1 > c2)
1708      return 1;
1709
1710    ++s1;
1711    ++s2;
1712  }
1713
1714  if (*s1)
1715    return 1;
1716  else if (*s2)
1717    return -1;
1718  return 0;
1719}
1720
1721int perform_code_completion(int argc, const char **argv, int timing_only) {
1722  const char *input = argv[1];
1723  char *filename = 0;
1724  unsigned line;
1725  unsigned column;
1726  CXIndex CIdx;
1727  int errorCode;
1728  struct CXUnsavedFile *unsaved_files = 0;
1729  int num_unsaved_files = 0;
1730  CXCodeCompleteResults *results = 0;
1731  CXTranslationUnit TU = 0;
1732  unsigned I, Repeats = 1;
1733  unsigned completionOptions = clang_defaultCodeCompleteOptions();
1734
1735  if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS"))
1736    completionOptions |= CXCodeComplete_IncludeCodePatterns;
1737  if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS"))
1738    completionOptions |= CXCodeComplete_IncludeBriefComments;
1739
1740  if (timing_only)
1741    input += strlen("-code-completion-timing=");
1742  else
1743    input += strlen("-code-completion-at=");
1744
1745  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
1746                                          0, 0)))
1747    return errorCode;
1748
1749  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
1750    return -1;
1751
1752  CIdx = clang_createIndex(0, 0);
1753
1754  if (getenv("CINDEXTEST_EDITING"))
1755    Repeats = 5;
1756
1757  TU = clang_parseTranslationUnit(CIdx, 0,
1758                                  argv + num_unsaved_files + 2,
1759                                  argc - num_unsaved_files - 2,
1760                                  0, 0, getDefaultParsingOptions());
1761  if (!TU) {
1762    fprintf(stderr, "Unable to load translation unit!\n");
1763    return 1;
1764  }
1765
1766  if (clang_reparseTranslationUnit(TU, 0, 0, clang_defaultReparseOptions(TU))) {
1767    fprintf(stderr, "Unable to reparse translation init!\n");
1768    return 1;
1769  }
1770
1771  for (I = 0; I != Repeats; ++I) {
1772    results = clang_codeCompleteAt(TU, filename, line, column,
1773                                   unsaved_files, num_unsaved_files,
1774                                   completionOptions);
1775    if (!results) {
1776      fprintf(stderr, "Unable to perform code completion!\n");
1777      return 1;
1778    }
1779    if (I != Repeats-1)
1780      clang_disposeCodeCompleteResults(results);
1781  }
1782
1783  if (results) {
1784    unsigned i, n = results->NumResults, containerIsIncomplete = 0;
1785    unsigned long long contexts;
1786    enum CXCursorKind containerKind;
1787    CXString objCSelector;
1788    const char *selectorString;
1789    if (!timing_only) {
1790      /* Sort the code-completion results based on the typed text. */
1791      clang_sortCodeCompletionResults(results->Results, results->NumResults);
1792
1793      for (i = 0; i != n; ++i)
1794        print_completion_result(results->Results + i, stdout);
1795    }
1796    n = clang_codeCompleteGetNumDiagnostics(results);
1797    for (i = 0; i != n; ++i) {
1798      CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i);
1799      PrintDiagnostic(diag);
1800      clang_disposeDiagnostic(diag);
1801    }
1802
1803    contexts = clang_codeCompleteGetContexts(results);
1804    print_completion_contexts(contexts, stdout);
1805
1806    containerKind = clang_codeCompleteGetContainerKind(results,
1807                                                       &containerIsIncomplete);
1808
1809    if (containerKind != CXCursor_InvalidCode) {
1810      /* We have found a container */
1811      CXString containerUSR, containerKindSpelling;
1812      containerKindSpelling = clang_getCursorKindSpelling(containerKind);
1813      printf("Container Kind: %s\n", clang_getCString(containerKindSpelling));
1814      clang_disposeString(containerKindSpelling);
1815
1816      if (containerIsIncomplete) {
1817        printf("Container is incomplete\n");
1818      }
1819      else {
1820        printf("Container is complete\n");
1821      }
1822
1823      containerUSR = clang_codeCompleteGetContainerUSR(results);
1824      printf("Container USR: %s\n", clang_getCString(containerUSR));
1825      clang_disposeString(containerUSR);
1826    }
1827
1828    objCSelector = clang_codeCompleteGetObjCSelector(results);
1829    selectorString = clang_getCString(objCSelector);
1830    if (selectorString && strlen(selectorString) > 0) {
1831      printf("Objective-C selector: %s\n", selectorString);
1832    }
1833    clang_disposeString(objCSelector);
1834
1835    clang_disposeCodeCompleteResults(results);
1836  }
1837  clang_disposeTranslationUnit(TU);
1838  clang_disposeIndex(CIdx);
1839  free(filename);
1840
1841  free_remapped_files(unsaved_files, num_unsaved_files);
1842
1843  return 0;
1844}
1845
1846typedef struct {
1847  char *filename;
1848  unsigned line;
1849  unsigned column;
1850} CursorSourceLocation;
1851
1852static int inspect_cursor_at(int argc, const char **argv) {
1853  CXIndex CIdx;
1854  int errorCode;
1855  struct CXUnsavedFile *unsaved_files = 0;
1856  int num_unsaved_files = 0;
1857  CXTranslationUnit TU;
1858  CXCursor Cursor;
1859  CursorSourceLocation *Locations = 0;
1860  unsigned NumLocations = 0, Loc;
1861  unsigned Repeats = 1;
1862  unsigned I;
1863
1864  /* Count the number of locations. */
1865  while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1])
1866    ++NumLocations;
1867
1868  /* Parse the locations. */
1869  assert(NumLocations > 0 && "Unable to count locations?");
1870  Locations = (CursorSourceLocation *)malloc(
1871                                  NumLocations * sizeof(CursorSourceLocation));
1872  for (Loc = 0; Loc < NumLocations; ++Loc) {
1873    const char *input = argv[Loc + 1] + strlen("-cursor-at=");
1874    if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
1875                                            &Locations[Loc].line,
1876                                            &Locations[Loc].column, 0, 0)))
1877      return errorCode;
1878  }
1879
1880  if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
1881                           &num_unsaved_files))
1882    return -1;
1883
1884  if (getenv("CINDEXTEST_EDITING"))
1885    Repeats = 5;
1886
1887  /* Parse the translation unit. When we're testing clang_getCursor() after
1888     reparsing, don't remap unsaved files until the second parse. */
1889  CIdx = clang_createIndex(1, 1);
1890  TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
1891                                  argv + num_unsaved_files + 1 + NumLocations,
1892                                  argc - num_unsaved_files - 2 - NumLocations,
1893                                  unsaved_files,
1894                                  Repeats > 1? 0 : num_unsaved_files,
1895                                  getDefaultParsingOptions());
1896
1897  if (!TU) {
1898    fprintf(stderr, "unable to parse input\n");
1899    return -1;
1900  }
1901
1902  if (checkForErrors(TU) != 0)
1903    return -1;
1904
1905  for (I = 0; I != Repeats; ++I) {
1906    if (Repeats > 1 &&
1907        clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
1908                                     clang_defaultReparseOptions(TU))) {
1909      clang_disposeTranslationUnit(TU);
1910      return 1;
1911    }
1912
1913    if (checkForErrors(TU) != 0)
1914      return -1;
1915
1916    for (Loc = 0; Loc < NumLocations; ++Loc) {
1917      CXFile file = clang_getFile(TU, Locations[Loc].filename);
1918      if (!file)
1919        continue;
1920
1921      Cursor = clang_getCursor(TU,
1922                               clang_getLocation(TU, file, Locations[Loc].line,
1923                                                 Locations[Loc].column));
1924
1925      if (checkForErrors(TU) != 0)
1926        return -1;
1927
1928      if (I + 1 == Repeats) {
1929        CXCompletionString completionString = clang_getCursorCompletionString(
1930                                                                        Cursor);
1931        CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor);
1932        CXString Spelling;
1933        const char *cspell;
1934        unsigned line, column;
1935        clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0);
1936        printf("%d:%d ", line, column);
1937        PrintCursor(Cursor, NULL);
1938        PrintCursorExtent(Cursor);
1939        Spelling = clang_getCursorSpelling(Cursor);
1940        cspell = clang_getCString(Spelling);
1941        if (cspell && strlen(cspell) != 0) {
1942          unsigned pieceIndex;
1943          printf(" Spelling=%s (", cspell);
1944          for (pieceIndex = 0; ; ++pieceIndex) {
1945            CXSourceRange range =
1946              clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0);
1947            if (clang_Range_isNull(range))
1948              break;
1949            PrintRange(range, 0);
1950          }
1951          printf(")");
1952        }
1953        clang_disposeString(Spelling);
1954        if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1)
1955          printf(" Selector index=%d",clang_Cursor_getObjCSelectorIndex(Cursor));
1956        if (clang_Cursor_isDynamicCall(Cursor))
1957          printf(" Dynamic-call");
1958
1959        if (completionString != NULL) {
1960          printf("\nCompletion string: ");
1961          print_completion_string(completionString, stdout);
1962        }
1963        printf("\n");
1964        free(Locations[Loc].filename);
1965      }
1966    }
1967  }
1968
1969  PrintDiagnostics(TU);
1970  clang_disposeTranslationUnit(TU);
1971  clang_disposeIndex(CIdx);
1972  free(Locations);
1973  free_remapped_files(unsaved_files, num_unsaved_files);
1974  return 0;
1975}
1976
1977static enum CXVisitorResult findFileRefsVisit(void *context,
1978                                         CXCursor cursor, CXSourceRange range) {
1979  if (clang_Range_isNull(range))
1980    return CXVisit_Continue;
1981
1982  PrintCursor(cursor, NULL);
1983  PrintRange(range, "");
1984  printf("\n");
1985  return CXVisit_Continue;
1986}
1987
1988static int find_file_refs_at(int argc, const char **argv) {
1989  CXIndex CIdx;
1990  int errorCode;
1991  struct CXUnsavedFile *unsaved_files = 0;
1992  int num_unsaved_files = 0;
1993  CXTranslationUnit TU;
1994  CXCursor Cursor;
1995  CursorSourceLocation *Locations = 0;
1996  unsigned NumLocations = 0, Loc;
1997  unsigned Repeats = 1;
1998  unsigned I;
1999
2000  /* Count the number of locations. */
2001  while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1])
2002    ++NumLocations;
2003
2004  /* Parse the locations. */
2005  assert(NumLocations > 0 && "Unable to count locations?");
2006  Locations = (CursorSourceLocation *)malloc(
2007                                  NumLocations * sizeof(CursorSourceLocation));
2008  for (Loc = 0; Loc < NumLocations; ++Loc) {
2009    const char *input = argv[Loc + 1] + strlen("-file-refs-at=");
2010    if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
2011                                            &Locations[Loc].line,
2012                                            &Locations[Loc].column, 0, 0)))
2013      return errorCode;
2014  }
2015
2016  if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
2017                           &num_unsaved_files))
2018    return -1;
2019
2020  if (getenv("CINDEXTEST_EDITING"))
2021    Repeats = 5;
2022
2023  /* Parse the translation unit. When we're testing clang_getCursor() after
2024     reparsing, don't remap unsaved files until the second parse. */
2025  CIdx = clang_createIndex(1, 1);
2026  TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
2027                                  argv + num_unsaved_files + 1 + NumLocations,
2028                                  argc - num_unsaved_files - 2 - NumLocations,
2029                                  unsaved_files,
2030                                  Repeats > 1? 0 : num_unsaved_files,
2031                                  getDefaultParsingOptions());
2032
2033  if (!TU) {
2034    fprintf(stderr, "unable to parse input\n");
2035    return -1;
2036  }
2037
2038  if (checkForErrors(TU) != 0)
2039    return -1;
2040
2041  for (I = 0; I != Repeats; ++I) {
2042    if (Repeats > 1 &&
2043        clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
2044                                     clang_defaultReparseOptions(TU))) {
2045      clang_disposeTranslationUnit(TU);
2046      return 1;
2047    }
2048
2049    if (checkForErrors(TU) != 0)
2050      return -1;
2051
2052    for (Loc = 0; Loc < NumLocations; ++Loc) {
2053      CXFile file = clang_getFile(TU, Locations[Loc].filename);
2054      if (!file)
2055        continue;
2056
2057      Cursor = clang_getCursor(TU,
2058                               clang_getLocation(TU, file, Locations[Loc].line,
2059                                                 Locations[Loc].column));
2060
2061      if (checkForErrors(TU) != 0)
2062        return -1;
2063
2064      if (I + 1 == Repeats) {
2065        CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit };
2066        PrintCursor(Cursor, NULL);
2067        printf("\n");
2068        clang_findReferencesInFile(Cursor, file, visitor);
2069        free(Locations[Loc].filename);
2070
2071        if (checkForErrors(TU) != 0)
2072          return -1;
2073      }
2074    }
2075  }
2076
2077  PrintDiagnostics(TU);
2078  clang_disposeTranslationUnit(TU);
2079  clang_disposeIndex(CIdx);
2080  free(Locations);
2081  free_remapped_files(unsaved_files, num_unsaved_files);
2082  return 0;
2083}
2084
2085typedef struct {
2086  const char *check_prefix;
2087  int first_check_printed;
2088  int fail_for_error;
2089  int abort;
2090  const char *main_filename;
2091} IndexData;
2092
2093static void printCheck(IndexData *data) {
2094  if (data->check_prefix) {
2095    if (data->first_check_printed) {
2096      printf("// %s-NEXT: ", data->check_prefix);
2097    } else {
2098      printf("// %s     : ", data->check_prefix);
2099      data->first_check_printed = 1;
2100    }
2101  }
2102}
2103
2104static void printCXIndexFile(CXIdxClientFile file) {
2105  CXString filename = clang_getFileName((CXFile)file);
2106  printf("%s", clang_getCString(filename));
2107  clang_disposeString(filename);
2108}
2109
2110static void printCXIndexLoc(CXIdxLoc loc, CXClientData client_data) {
2111  IndexData *index_data;
2112  CXString filename;
2113  const char *cname;
2114  CXIdxClientFile file;
2115  unsigned line, column;
2116  int isMainFile;
2117
2118  index_data = (IndexData *)client_data;
2119  clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0);
2120  if (line == 0) {
2121    printf("<null loc>");
2122    return;
2123  }
2124  if (!file) {
2125    printf("<no idxfile>");
2126    return;
2127  }
2128  filename = clang_getFileName((CXFile)file);
2129  cname = clang_getCString(filename);
2130  if (strcmp(cname, index_data->main_filename) == 0)
2131    isMainFile = 1;
2132  else
2133    isMainFile = 0;
2134  clang_disposeString(filename);
2135
2136  if (!isMainFile) {
2137    printCXIndexFile(file);
2138    printf(":");
2139  }
2140  printf("%d:%d", line, column);
2141}
2142
2143static unsigned digitCount(unsigned val) {
2144  unsigned c = 1;
2145  while (1) {
2146    if (val < 10)
2147      return c;
2148    ++c;
2149    val /= 10;
2150  }
2151}
2152
2153static CXIdxClientContainer makeClientContainer(const CXIdxEntityInfo *info,
2154                                                CXIdxLoc loc) {
2155  const char *name;
2156  char *newStr;
2157  CXIdxClientFile file;
2158  unsigned line, column;
2159
2160  name = info->name;
2161  if (!name)
2162    name = "<anon-tag>";
2163
2164  clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0);
2165  /* FIXME: free these.*/
2166  newStr = (char *)malloc(strlen(name) +
2167                          digitCount(line) + digitCount(column) + 3);
2168  sprintf(newStr, "%s:%d:%d", name, line, column);
2169  return (CXIdxClientContainer)newStr;
2170}
2171
2172static void printCXIndexContainer(const CXIdxContainerInfo *info) {
2173  CXIdxClientContainer container;
2174  container = clang_index_getClientContainer(info);
2175  if (!container)
2176    printf("[<<NULL>>]");
2177  else
2178    printf("[%s]", (const char *)container);
2179}
2180
2181static const char *getEntityKindString(CXIdxEntityKind kind) {
2182  switch (kind) {
2183  case CXIdxEntity_Unexposed: return "<<UNEXPOSED>>";
2184  case CXIdxEntity_Typedef: return "typedef";
2185  case CXIdxEntity_Function: return "function";
2186  case CXIdxEntity_Variable: return "variable";
2187  case CXIdxEntity_Field: return "field";
2188  case CXIdxEntity_EnumConstant: return "enumerator";
2189  case CXIdxEntity_ObjCClass: return "objc-class";
2190  case CXIdxEntity_ObjCProtocol: return "objc-protocol";
2191  case CXIdxEntity_ObjCCategory: return "objc-category";
2192  case CXIdxEntity_ObjCInstanceMethod: return "objc-instance-method";
2193  case CXIdxEntity_ObjCClassMethod: return "objc-class-method";
2194  case CXIdxEntity_ObjCProperty: return "objc-property";
2195  case CXIdxEntity_ObjCIvar: return "objc-ivar";
2196  case CXIdxEntity_Enum: return "enum";
2197  case CXIdxEntity_Struct: return "struct";
2198  case CXIdxEntity_Union: return "union";
2199  case CXIdxEntity_CXXClass: return "c++-class";
2200  case CXIdxEntity_CXXNamespace: return "namespace";
2201  case CXIdxEntity_CXXNamespaceAlias: return "namespace-alias";
2202  case CXIdxEntity_CXXStaticVariable: return "c++-static-var";
2203  case CXIdxEntity_CXXStaticMethod: return "c++-static-method";
2204  case CXIdxEntity_CXXInstanceMethod: return "c++-instance-method";
2205  case CXIdxEntity_CXXConstructor: return "constructor";
2206  case CXIdxEntity_CXXDestructor: return "destructor";
2207  case CXIdxEntity_CXXConversionFunction: return "conversion-func";
2208  case CXIdxEntity_CXXTypeAlias: return "type-alias";
2209  case CXIdxEntity_CXXInterface: return "c++-__interface";
2210  }
2211  assert(0 && "Garbage entity kind");
2212  return 0;
2213}
2214
2215static const char *getEntityTemplateKindString(CXIdxEntityCXXTemplateKind kind) {
2216  switch (kind) {
2217  case CXIdxEntity_NonTemplate: return "";
2218  case CXIdxEntity_Template: return "-template";
2219  case CXIdxEntity_TemplatePartialSpecialization:
2220    return "-template-partial-spec";
2221  case CXIdxEntity_TemplateSpecialization: return "-template-spec";
2222  }
2223  assert(0 && "Garbage entity kind");
2224  return 0;
2225}
2226
2227static const char *getEntityLanguageString(CXIdxEntityLanguage kind) {
2228  switch (kind) {
2229  case CXIdxEntityLang_None: return "<none>";
2230  case CXIdxEntityLang_C: return "C";
2231  case CXIdxEntityLang_ObjC: return "ObjC";
2232  case CXIdxEntityLang_CXX: return "C++";
2233  }
2234  assert(0 && "Garbage language kind");
2235  return 0;
2236}
2237
2238static void printEntityInfo(const char *cb,
2239                            CXClientData client_data,
2240                            const CXIdxEntityInfo *info) {
2241  const char *name;
2242  IndexData *index_data;
2243  unsigned i;
2244  index_data = (IndexData *)client_data;
2245  printCheck(index_data);
2246
2247  if (!info) {
2248    printf("%s: <<NULL>>", cb);
2249    return;
2250  }
2251
2252  name = info->name;
2253  if (!name)
2254    name = "<anon-tag>";
2255
2256  printf("%s: kind: %s%s", cb, getEntityKindString(info->kind),
2257         getEntityTemplateKindString(info->templateKind));
2258  printf(" | name: %s", name);
2259  printf(" | USR: %s", info->USR);
2260  printf(" | lang: %s", getEntityLanguageString(info->lang));
2261
2262  for (i = 0; i != info->numAttributes; ++i) {
2263    const CXIdxAttrInfo *Attr = info->attributes[i];
2264    printf("     <attribute>: ");
2265    PrintCursor(Attr->cursor, NULL);
2266  }
2267}
2268
2269static void printBaseClassInfo(CXClientData client_data,
2270                               const CXIdxBaseClassInfo *info) {
2271  printEntityInfo("     <base>", client_data, info->base);
2272  printf(" | cursor: ");
2273  PrintCursor(info->cursor, NULL);
2274  printf(" | loc: ");
2275  printCXIndexLoc(info->loc, client_data);
2276}
2277
2278static void printProtocolList(const CXIdxObjCProtocolRefListInfo *ProtoInfo,
2279                              CXClientData client_data) {
2280  unsigned i;
2281  for (i = 0; i < ProtoInfo->numProtocols; ++i) {
2282    printEntityInfo("     <protocol>", client_data,
2283                    ProtoInfo->protocols[i]->protocol);
2284    printf(" | cursor: ");
2285    PrintCursor(ProtoInfo->protocols[i]->cursor, NULL);
2286    printf(" | loc: ");
2287    printCXIndexLoc(ProtoInfo->protocols[i]->loc, client_data);
2288    printf("\n");
2289  }
2290}
2291
2292static void index_diagnostic(CXClientData client_data,
2293                             CXDiagnosticSet diagSet, void *reserved) {
2294  CXString str;
2295  const char *cstr;
2296  unsigned numDiags, i;
2297  CXDiagnostic diag;
2298  IndexData *index_data;
2299  index_data = (IndexData *)client_data;
2300  printCheck(index_data);
2301
2302  numDiags = clang_getNumDiagnosticsInSet(diagSet);
2303  for (i = 0; i != numDiags; ++i) {
2304    diag = clang_getDiagnosticInSet(diagSet, i);
2305    str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions());
2306    cstr = clang_getCString(str);
2307    printf("[diagnostic]: %s\n", cstr);
2308    clang_disposeString(str);
2309
2310    if (getenv("CINDEXTEST_FAILONERROR") &&
2311        clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) {
2312      index_data->fail_for_error = 1;
2313    }
2314  }
2315}
2316
2317static CXIdxClientFile index_enteredMainFile(CXClientData client_data,
2318                                       CXFile file, void *reserved) {
2319  IndexData *index_data;
2320  CXString filename;
2321
2322  index_data = (IndexData *)client_data;
2323  printCheck(index_data);
2324
2325  filename = clang_getFileName(file);
2326  index_data->main_filename = clang_getCString(filename);
2327  clang_disposeString(filename);
2328
2329  printf("[enteredMainFile]: ");
2330  printCXIndexFile((CXIdxClientFile)file);
2331  printf("\n");
2332
2333  return (CXIdxClientFile)file;
2334}
2335
2336static CXIdxClientFile index_ppIncludedFile(CXClientData client_data,
2337                                            const CXIdxIncludedFileInfo *info) {
2338  IndexData *index_data;
2339  index_data = (IndexData *)client_data;
2340  printCheck(index_data);
2341
2342  printf("[ppIncludedFile]: ");
2343  printCXIndexFile((CXIdxClientFile)info->file);
2344  printf(" | name: \"%s\"", info->filename);
2345  printf(" | hash loc: ");
2346  printCXIndexLoc(info->hashLoc, client_data);
2347  printf(" | isImport: %d | isAngled: %d\n", info->isImport, info->isAngled);
2348
2349  return (CXIdxClientFile)info->file;
2350}
2351
2352static CXIdxClientContainer index_startedTranslationUnit(CXClientData client_data,
2353                                                   void *reserved) {
2354  IndexData *index_data;
2355  index_data = (IndexData *)client_data;
2356  printCheck(index_data);
2357
2358  printf("[startedTranslationUnit]\n");
2359  return (CXIdxClientContainer)"TU";
2360}
2361
2362static void index_indexDeclaration(CXClientData client_data,
2363                                   const CXIdxDeclInfo *info) {
2364  IndexData *index_data;
2365  const CXIdxObjCCategoryDeclInfo *CatInfo;
2366  const CXIdxObjCInterfaceDeclInfo *InterInfo;
2367  const CXIdxObjCProtocolRefListInfo *ProtoInfo;
2368  const CXIdxObjCPropertyDeclInfo *PropInfo;
2369  const CXIdxCXXClassDeclInfo *CXXClassInfo;
2370  unsigned i;
2371  index_data = (IndexData *)client_data;
2372
2373  printEntityInfo("[indexDeclaration]", client_data, info->entityInfo);
2374  printf(" | cursor: ");
2375  PrintCursor(info->cursor, NULL);
2376  printf(" | loc: ");
2377  printCXIndexLoc(info->loc, client_data);
2378  printf(" | semantic-container: ");
2379  printCXIndexContainer(info->semanticContainer);
2380  printf(" | lexical-container: ");
2381  printCXIndexContainer(info->lexicalContainer);
2382  printf(" | isRedecl: %d", info->isRedeclaration);
2383  printf(" | isDef: %d", info->isDefinition);
2384  printf(" | isContainer: %d", info->isContainer);
2385  printf(" | isImplicit: %d\n", info->isImplicit);
2386
2387  for (i = 0; i != info->numAttributes; ++i) {
2388    const CXIdxAttrInfo *Attr = info->attributes[i];
2389    printf("     <attribute>: ");
2390    PrintCursor(Attr->cursor, NULL);
2391    printf("\n");
2392  }
2393
2394  if (clang_index_isEntityObjCContainerKind(info->entityInfo->kind)) {
2395    const char *kindName = 0;
2396    CXIdxObjCContainerKind K = clang_index_getObjCContainerDeclInfo(info)->kind;
2397    switch (K) {
2398    case CXIdxObjCContainer_ForwardRef:
2399      kindName = "forward-ref"; break;
2400    case CXIdxObjCContainer_Interface:
2401      kindName = "interface"; break;
2402    case CXIdxObjCContainer_Implementation:
2403      kindName = "implementation"; break;
2404    }
2405    printCheck(index_data);
2406    printf("     <ObjCContainerInfo>: kind: %s\n", kindName);
2407  }
2408
2409  if ((CatInfo = clang_index_getObjCCategoryDeclInfo(info))) {
2410    printEntityInfo("     <ObjCCategoryInfo>: class", client_data,
2411                    CatInfo->objcClass);
2412    printf(" | cursor: ");
2413    PrintCursor(CatInfo->classCursor, NULL);
2414    printf(" | loc: ");
2415    printCXIndexLoc(CatInfo->classLoc, client_data);
2416    printf("\n");
2417  }
2418
2419  if ((InterInfo = clang_index_getObjCInterfaceDeclInfo(info))) {
2420    if (InterInfo->superInfo) {
2421      printBaseClassInfo(client_data, InterInfo->superInfo);
2422      printf("\n");
2423    }
2424  }
2425
2426  if ((ProtoInfo = clang_index_getObjCProtocolRefListInfo(info))) {
2427    printProtocolList(ProtoInfo, client_data);
2428  }
2429
2430  if ((PropInfo = clang_index_getObjCPropertyDeclInfo(info))) {
2431    if (PropInfo->getter) {
2432      printEntityInfo("     <getter>", client_data, PropInfo->getter);
2433      printf("\n");
2434    }
2435    if (PropInfo->setter) {
2436      printEntityInfo("     <setter>", client_data, PropInfo->setter);
2437      printf("\n");
2438    }
2439  }
2440
2441  if ((CXXClassInfo = clang_index_getCXXClassDeclInfo(info))) {
2442    for (i = 0; i != CXXClassInfo->numBases; ++i) {
2443      printBaseClassInfo(client_data, CXXClassInfo->bases[i]);
2444      printf("\n");
2445    }
2446  }
2447
2448  if (info->declAsContainer)
2449    clang_index_setClientContainer(info->declAsContainer,
2450                              makeClientContainer(info->entityInfo, info->loc));
2451}
2452
2453static void index_indexEntityReference(CXClientData client_data,
2454                                       const CXIdxEntityRefInfo *info) {
2455  printEntityInfo("[indexEntityReference]", client_data, info->referencedEntity);
2456  printf(" | cursor: ");
2457  PrintCursor(info->cursor, NULL);
2458  printf(" | loc: ");
2459  printCXIndexLoc(info->loc, client_data);
2460  printEntityInfo(" | <parent>:", client_data, info->parentEntity);
2461  printf(" | container: ");
2462  printCXIndexContainer(info->container);
2463  printf(" | refkind: ");
2464  switch (info->kind) {
2465  case CXIdxEntityRef_Direct: printf("direct"); break;
2466  case CXIdxEntityRef_Implicit: printf("implicit"); break;
2467  }
2468  printf("\n");
2469}
2470
2471static int index_abortQuery(CXClientData client_data, void *reserved) {
2472  IndexData *index_data;
2473  index_data = (IndexData *)client_data;
2474  return index_data->abort;
2475}
2476
2477static IndexerCallbacks IndexCB = {
2478  index_abortQuery,
2479  index_diagnostic,
2480  index_enteredMainFile,
2481  index_ppIncludedFile,
2482  0, /*importedASTFile*/
2483  index_startedTranslationUnit,
2484  index_indexDeclaration,
2485  index_indexEntityReference
2486};
2487
2488static unsigned getIndexOptions(void) {
2489  unsigned index_opts;
2490  index_opts = 0;
2491  if (getenv("CINDEXTEST_SUPPRESSREFS"))
2492    index_opts |= CXIndexOpt_SuppressRedundantRefs;
2493  if (getenv("CINDEXTEST_INDEXLOCALSYMBOLS"))
2494    index_opts |= CXIndexOpt_IndexFunctionLocalSymbols;
2495
2496  return index_opts;
2497}
2498
2499static int index_file(int argc, const char **argv) {
2500  const char *check_prefix;
2501  CXIndex Idx;
2502  CXIndexAction idxAction;
2503  IndexData index_data;
2504  unsigned index_opts;
2505  int result;
2506
2507  check_prefix = 0;
2508  if (argc > 0) {
2509    if (strstr(argv[0], "-check-prefix=") == argv[0]) {
2510      check_prefix = argv[0] + strlen("-check-prefix=");
2511      ++argv;
2512      --argc;
2513    }
2514  }
2515
2516  if (argc == 0) {
2517    fprintf(stderr, "no compiler arguments\n");
2518    return -1;
2519  }
2520
2521  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
2522                                /* displayDiagnosics=*/1))) {
2523    fprintf(stderr, "Could not create Index\n");
2524    return 1;
2525  }
2526  idxAction = 0;
2527
2528  index_data.check_prefix = check_prefix;
2529  index_data.first_check_printed = 0;
2530  index_data.fail_for_error = 0;
2531  index_data.abort = 0;
2532
2533  index_opts = getIndexOptions();
2534  idxAction = clang_IndexAction_create(Idx);
2535  result = clang_indexSourceFile(idxAction, &index_data,
2536                                 &IndexCB,sizeof(IndexCB), index_opts,
2537                                 0, argv, argc, 0, 0, 0, 0);
2538  if (index_data.fail_for_error)
2539    result = -1;
2540
2541  clang_IndexAction_dispose(idxAction);
2542  clang_disposeIndex(Idx);
2543  return result;
2544}
2545
2546static int index_tu(int argc, const char **argv) {
2547  CXIndex Idx;
2548  CXIndexAction idxAction;
2549  CXTranslationUnit TU;
2550  const char *check_prefix;
2551  IndexData index_data;
2552  unsigned index_opts;
2553  int result;
2554
2555  check_prefix = 0;
2556  if (argc > 0) {
2557    if (strstr(argv[0], "-check-prefix=") == argv[0]) {
2558      check_prefix = argv[0] + strlen("-check-prefix=");
2559      ++argv;
2560      --argc;
2561    }
2562  }
2563
2564  if (argc == 0) {
2565    fprintf(stderr, "no ast file\n");
2566    return -1;
2567  }
2568
2569  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
2570                                /* displayDiagnosics=*/1))) {
2571    fprintf(stderr, "Could not create Index\n");
2572    return 1;
2573  }
2574  idxAction = 0;
2575  result = 1;
2576
2577  if (!CreateTranslationUnit(Idx, argv[0], &TU))
2578    goto finished;
2579
2580  index_data.check_prefix = check_prefix;
2581  index_data.first_check_printed = 0;
2582  index_data.fail_for_error = 0;
2583  index_data.abort = 0;
2584
2585  index_opts = getIndexOptions();
2586  idxAction = clang_IndexAction_create(Idx);
2587  result = clang_indexTranslationUnit(idxAction, &index_data,
2588                                      &IndexCB,sizeof(IndexCB),
2589                                      index_opts, TU);
2590  if (index_data.fail_for_error)
2591    goto finished;
2592
2593  finished:
2594  clang_IndexAction_dispose(idxAction);
2595  clang_disposeIndex(Idx);
2596
2597  return result;
2598}
2599
2600int perform_token_annotation(int argc, const char **argv) {
2601  const char *input = argv[1];
2602  char *filename = 0;
2603  unsigned line, second_line;
2604  unsigned column, second_column;
2605  CXIndex CIdx;
2606  CXTranslationUnit TU = 0;
2607  int errorCode;
2608  struct CXUnsavedFile *unsaved_files = 0;
2609  int num_unsaved_files = 0;
2610  CXToken *tokens;
2611  unsigned num_tokens;
2612  CXSourceRange range;
2613  CXSourceLocation startLoc, endLoc;
2614  CXFile file = 0;
2615  CXCursor *cursors = 0;
2616  unsigned i;
2617
2618  input += strlen("-test-annotate-tokens=");
2619  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
2620                                          &second_line, &second_column)))
2621    return errorCode;
2622
2623  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) {
2624    free(filename);
2625    return -1;
2626  }
2627
2628  CIdx = clang_createIndex(0, 1);
2629  TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
2630                                  argv + num_unsaved_files + 2,
2631                                  argc - num_unsaved_files - 3,
2632                                  unsaved_files,
2633                                  num_unsaved_files,
2634                                  getDefaultParsingOptions());
2635  if (!TU) {
2636    fprintf(stderr, "unable to parse input\n");
2637    clang_disposeIndex(CIdx);
2638    free(filename);
2639    free_remapped_files(unsaved_files, num_unsaved_files);
2640    return -1;
2641  }
2642  errorCode = 0;
2643
2644  if (checkForErrors(TU) != 0) {
2645    errorCode = -1;
2646    goto teardown;
2647  }
2648
2649  if (getenv("CINDEXTEST_EDITING")) {
2650    for (i = 0; i < 5; ++i) {
2651      if (clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
2652                                       clang_defaultReparseOptions(TU))) {
2653        fprintf(stderr, "Unable to reparse translation unit!\n");
2654        errorCode = -1;
2655        goto teardown;
2656      }
2657    }
2658  }
2659
2660  if (checkForErrors(TU) != 0) {
2661    errorCode = -1;
2662    goto teardown;
2663  }
2664
2665  file = clang_getFile(TU, filename);
2666  if (!file) {
2667    fprintf(stderr, "file %s is not in this translation unit\n", filename);
2668    errorCode = -1;
2669    goto teardown;
2670  }
2671
2672  startLoc = clang_getLocation(TU, file, line, column);
2673  if (clang_equalLocations(clang_getNullLocation(), startLoc)) {
2674    fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line,
2675            column);
2676    errorCode = -1;
2677    goto teardown;
2678  }
2679
2680  endLoc = clang_getLocation(TU, file, second_line, second_column);
2681  if (clang_equalLocations(clang_getNullLocation(), endLoc)) {
2682    fprintf(stderr, "invalid source location %s:%d:%d\n", filename,
2683            second_line, second_column);
2684    errorCode = -1;
2685    goto teardown;
2686  }
2687
2688  range = clang_getRange(startLoc, endLoc);
2689  clang_tokenize(TU, range, &tokens, &num_tokens);
2690
2691  if (checkForErrors(TU) != 0) {
2692    errorCode = -1;
2693    goto teardown;
2694  }
2695
2696  cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor));
2697  clang_annotateTokens(TU, tokens, num_tokens, cursors);
2698
2699  if (checkForErrors(TU) != 0) {
2700    errorCode = -1;
2701    goto teardown;
2702  }
2703
2704  for (i = 0; i != num_tokens; ++i) {
2705    const char *kind = "<unknown>";
2706    CXString spelling = clang_getTokenSpelling(TU, tokens[i]);
2707    CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]);
2708    unsigned start_line, start_column, end_line, end_column;
2709
2710    switch (clang_getTokenKind(tokens[i])) {
2711    case CXToken_Punctuation: kind = "Punctuation"; break;
2712    case CXToken_Keyword: kind = "Keyword"; break;
2713    case CXToken_Identifier: kind = "Identifier"; break;
2714    case CXToken_Literal: kind = "Literal"; break;
2715    case CXToken_Comment: kind = "Comment"; break;
2716    }
2717    clang_getSpellingLocation(clang_getRangeStart(extent),
2718                              0, &start_line, &start_column, 0);
2719    clang_getSpellingLocation(clang_getRangeEnd(extent),
2720                              0, &end_line, &end_column, 0);
2721    printf("%s: \"%s\" ", kind, clang_getCString(spelling));
2722    clang_disposeString(spelling);
2723    PrintExtent(stdout, start_line, start_column, end_line, end_column);
2724    if (!clang_isInvalid(cursors[i].kind)) {
2725      printf(" ");
2726      PrintCursor(cursors[i], NULL);
2727    }
2728    printf("\n");
2729  }
2730  free(cursors);
2731  clang_disposeTokens(TU, tokens, num_tokens);
2732
2733 teardown:
2734  PrintDiagnostics(TU);
2735  clang_disposeTranslationUnit(TU);
2736  clang_disposeIndex(CIdx);
2737  free(filename);
2738  free_remapped_files(unsaved_files, num_unsaved_files);
2739  return errorCode;
2740}
2741
2742static int
2743perform_test_compilation_db(const char *database, int argc, const char **argv) {
2744  CXCompilationDatabase db;
2745  CXCompileCommands CCmds;
2746  CXCompileCommand CCmd;
2747  CXCompilationDatabase_Error ec;
2748  CXString wd;
2749  CXString arg;
2750  int errorCode = 0;
2751  char *tmp;
2752  unsigned len;
2753  char *buildDir;
2754  int i, j, a, numCmds, numArgs;
2755
2756  len = strlen(database);
2757  tmp = (char *) malloc(len+1);
2758  memcpy(tmp, database, len+1);
2759  buildDir = dirname(tmp);
2760
2761  db = clang_CompilationDatabase_fromDirectory(buildDir, &ec);
2762
2763  if (db) {
2764
2765    if (ec!=CXCompilationDatabase_NoError) {
2766      printf("unexpected error %d code while loading compilation database\n", ec);
2767      errorCode = -1;
2768      goto cdb_end;
2769    }
2770
2771    for (i=0; i<argc && errorCode==0; ) {
2772      if (strcmp(argv[i],"lookup")==0){
2773        CCmds = clang_CompilationDatabase_getCompileCommands(db, argv[i+1]);
2774
2775        if (!CCmds) {
2776          printf("file %s not found in compilation db\n", argv[i+1]);
2777          errorCode = -1;
2778          break;
2779        }
2780
2781        numCmds = clang_CompileCommands_getSize(CCmds);
2782
2783        if (numCmds==0) {
2784          fprintf(stderr, "should not get an empty compileCommand set for file"
2785                          " '%s'\n", argv[i+1]);
2786          errorCode = -1;
2787          break;
2788        }
2789
2790        for (j=0; j<numCmds; ++j) {
2791          CCmd = clang_CompileCommands_getCommand(CCmds, j);
2792
2793          wd = clang_CompileCommand_getDirectory(CCmd);
2794          printf("workdir:'%s'", clang_getCString(wd));
2795          clang_disposeString(wd);
2796
2797          printf(" cmdline:'");
2798          numArgs = clang_CompileCommand_getNumArgs(CCmd);
2799          for (a=0; a<numArgs; ++a) {
2800            if (a) printf(" ");
2801            arg = clang_CompileCommand_getArg(CCmd, a);
2802            printf("%s", clang_getCString(arg));
2803            clang_disposeString(arg);
2804          }
2805          printf("'\n");
2806        }
2807
2808        clang_CompileCommands_dispose(CCmds);
2809
2810        i += 2;
2811      }
2812    }
2813    clang_CompilationDatabase_dispose(db);
2814  } else {
2815    printf("database loading failed with error code %d.\n", ec);
2816    errorCode = -1;
2817  }
2818
2819cdb_end:
2820  free(tmp);
2821
2822  return errorCode;
2823}
2824
2825/******************************************************************************/
2826/* USR printing.                                                              */
2827/******************************************************************************/
2828
2829static int insufficient_usr(const char *kind, const char *usage) {
2830  fprintf(stderr, "USR for '%s' requires: %s\n", kind, usage);
2831  return 1;
2832}
2833
2834static unsigned isUSR(const char *s) {
2835  return s[0] == 'c' && s[1] == ':';
2836}
2837
2838static int not_usr(const char *s, const char *arg) {
2839  fprintf(stderr, "'%s' argument ('%s') is not a USR\n", s, arg);
2840  return 1;
2841}
2842
2843static void print_usr(CXString usr) {
2844  const char *s = clang_getCString(usr);
2845  printf("%s\n", s);
2846  clang_disposeString(usr);
2847}
2848
2849static void display_usrs() {
2850  fprintf(stderr, "-print-usrs options:\n"
2851        " ObjCCategory <class name> <category name>\n"
2852        " ObjCClass <class name>\n"
2853        " ObjCIvar <ivar name> <class USR>\n"
2854        " ObjCMethod <selector> [0=class method|1=instance method] "
2855            "<class USR>\n"
2856          " ObjCProperty <property name> <class USR>\n"
2857          " ObjCProtocol <protocol name>\n");
2858}
2859
2860int print_usrs(const char **I, const char **E) {
2861  while (I != E) {
2862    const char *kind = *I;
2863    unsigned len = strlen(kind);
2864    switch (len) {
2865      case 8:
2866        if (memcmp(kind, "ObjCIvar", 8) == 0) {
2867          if (I + 2 >= E)
2868            return insufficient_usr(kind, "<ivar name> <class USR>");
2869          if (!isUSR(I[2]))
2870            return not_usr("<class USR>", I[2]);
2871          else {
2872            CXString x;
2873            x.data = (void*) I[2];
2874            x.private_flags = 0;
2875            print_usr(clang_constructUSR_ObjCIvar(I[1], x));
2876          }
2877
2878          I += 3;
2879          continue;
2880        }
2881        break;
2882      case 9:
2883        if (memcmp(kind, "ObjCClass", 9) == 0) {
2884          if (I + 1 >= E)
2885            return insufficient_usr(kind, "<class name>");
2886          print_usr(clang_constructUSR_ObjCClass(I[1]));
2887          I += 2;
2888          continue;
2889        }
2890        break;
2891      case 10:
2892        if (memcmp(kind, "ObjCMethod", 10) == 0) {
2893          if (I + 3 >= E)
2894            return insufficient_usr(kind, "<method selector> "
2895                "[0=class method|1=instance method] <class USR>");
2896          if (!isUSR(I[3]))
2897            return not_usr("<class USR>", I[3]);
2898          else {
2899            CXString x;
2900            x.data = (void*) I[3];
2901            x.private_flags = 0;
2902            print_usr(clang_constructUSR_ObjCMethod(I[1], atoi(I[2]), x));
2903          }
2904          I += 4;
2905          continue;
2906        }
2907        break;
2908      case 12:
2909        if (memcmp(kind, "ObjCCategory", 12) == 0) {
2910          if (I + 2 >= E)
2911            return insufficient_usr(kind, "<class name> <category name>");
2912          print_usr(clang_constructUSR_ObjCCategory(I[1], I[2]));
2913          I += 3;
2914          continue;
2915        }
2916        if (memcmp(kind, "ObjCProtocol", 12) == 0) {
2917          if (I + 1 >= E)
2918            return insufficient_usr(kind, "<protocol name>");
2919          print_usr(clang_constructUSR_ObjCProtocol(I[1]));
2920          I += 2;
2921          continue;
2922        }
2923        if (memcmp(kind, "ObjCProperty", 12) == 0) {
2924          if (I + 2 >= E)
2925            return insufficient_usr(kind, "<property name> <class USR>");
2926          if (!isUSR(I[2]))
2927            return not_usr("<class USR>", I[2]);
2928          else {
2929            CXString x;
2930            x.data = (void*) I[2];
2931            x.private_flags = 0;
2932            print_usr(clang_constructUSR_ObjCProperty(I[1], x));
2933          }
2934          I += 3;
2935          continue;
2936        }
2937        break;
2938      default:
2939        break;
2940    }
2941    break;
2942  }
2943
2944  if (I != E) {
2945    fprintf(stderr, "Invalid USR kind: %s\n", *I);
2946    display_usrs();
2947    return 1;
2948  }
2949  return 0;
2950}
2951
2952int print_usrs_file(const char *file_name) {
2953  char line[2048];
2954  const char *args[128];
2955  unsigned numChars = 0;
2956
2957  FILE *fp = fopen(file_name, "r");
2958  if (!fp) {
2959    fprintf(stderr, "error: cannot open '%s'\n", file_name);
2960    return 1;
2961  }
2962
2963  /* This code is not really all that safe, but it works fine for testing. */
2964  while (!feof(fp)) {
2965    char c = fgetc(fp);
2966    if (c == '\n') {
2967      unsigned i = 0;
2968      const char *s = 0;
2969
2970      if (numChars == 0)
2971        continue;
2972
2973      line[numChars] = '\0';
2974      numChars = 0;
2975
2976      if (line[0] == '/' && line[1] == '/')
2977        continue;
2978
2979      s = strtok(line, " ");
2980      while (s) {
2981        args[i] = s;
2982        ++i;
2983        s = strtok(0, " ");
2984      }
2985      if (print_usrs(&args[0], &args[i]))
2986        return 1;
2987    }
2988    else
2989      line[numChars++] = c;
2990  }
2991
2992  fclose(fp);
2993  return 0;
2994}
2995
2996/******************************************************************************/
2997/* Command line processing.                                                   */
2998/******************************************************************************/
2999int write_pch_file(const char *filename, int argc, const char *argv[]) {
3000  CXIndex Idx;
3001  CXTranslationUnit TU;
3002  struct CXUnsavedFile *unsaved_files = 0;
3003  int num_unsaved_files = 0;
3004  int result = 0;
3005
3006  Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnosics=*/1);
3007
3008  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
3009    clang_disposeIndex(Idx);
3010    return -1;
3011  }
3012
3013  TU = clang_parseTranslationUnit(Idx, 0,
3014                                  argv + num_unsaved_files,
3015                                  argc - num_unsaved_files,
3016                                  unsaved_files,
3017                                  num_unsaved_files,
3018                                  CXTranslationUnit_Incomplete);
3019  if (!TU) {
3020    fprintf(stderr, "Unable to load translation unit!\n");
3021    free_remapped_files(unsaved_files, num_unsaved_files);
3022    clang_disposeIndex(Idx);
3023    return 1;
3024  }
3025
3026  switch (clang_saveTranslationUnit(TU, filename,
3027                                    clang_defaultSaveOptions(TU))) {
3028  case CXSaveError_None:
3029    break;
3030
3031  case CXSaveError_TranslationErrors:
3032    fprintf(stderr, "Unable to write PCH file %s: translation errors\n",
3033            filename);
3034    result = 2;
3035    break;
3036
3037  case CXSaveError_InvalidTU:
3038    fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n",
3039            filename);
3040    result = 3;
3041    break;
3042
3043  case CXSaveError_Unknown:
3044  default:
3045    fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename);
3046    result = 1;
3047    break;
3048  }
3049
3050  clang_disposeTranslationUnit(TU);
3051  free_remapped_files(unsaved_files, num_unsaved_files);
3052  clang_disposeIndex(Idx);
3053  return result;
3054}
3055
3056/******************************************************************************/
3057/* Serialized diagnostics.                                                    */
3058/******************************************************************************/
3059
3060static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) {
3061  switch (error) {
3062    case CXLoadDiag_CannotLoad: return "Cannot Load File";
3063    case CXLoadDiag_None: break;
3064    case CXLoadDiag_Unknown: return "Unknown";
3065    case CXLoadDiag_InvalidFile: return "Invalid File";
3066  }
3067  return "None";
3068}
3069
3070static const char *getSeverityString(enum CXDiagnosticSeverity severity) {
3071  switch (severity) {
3072    case CXDiagnostic_Note: return "note";
3073    case CXDiagnostic_Error: return "error";
3074    case CXDiagnostic_Fatal: return "fatal";
3075    case CXDiagnostic_Ignored: return "ignored";
3076    case CXDiagnostic_Warning: return "warning";
3077  }
3078  return "unknown";
3079}
3080
3081static void printIndent(unsigned indent) {
3082  if (indent == 0)
3083    return;
3084  fprintf(stderr, "+");
3085  --indent;
3086  while (indent > 0) {
3087    fprintf(stderr, "-");
3088    --indent;
3089  }
3090}
3091
3092static void printLocation(CXSourceLocation L) {
3093  CXFile File;
3094  CXString FileName;
3095  unsigned line, column, offset;
3096
3097  clang_getExpansionLocation(L, &File, &line, &column, &offset);
3098  FileName = clang_getFileName(File);
3099
3100  fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column);
3101  clang_disposeString(FileName);
3102}
3103
3104static void printRanges(CXDiagnostic D, unsigned indent) {
3105  unsigned i, n = clang_getDiagnosticNumRanges(D);
3106
3107  for (i = 0; i < n; ++i) {
3108    CXSourceLocation Start, End;
3109    CXSourceRange SR = clang_getDiagnosticRange(D, i);
3110    Start = clang_getRangeStart(SR);
3111    End = clang_getRangeEnd(SR);
3112
3113    printIndent(indent);
3114    fprintf(stderr, "Range: ");
3115    printLocation(Start);
3116    fprintf(stderr, " ");
3117    printLocation(End);
3118    fprintf(stderr, "\n");
3119  }
3120}
3121
3122static void printFixIts(CXDiagnostic D, unsigned indent) {
3123  unsigned i, n = clang_getDiagnosticNumFixIts(D);
3124  fprintf(stderr, "Number FIXITs = %d\n", n);
3125  for (i = 0 ; i < n; ++i) {
3126    CXSourceRange ReplacementRange;
3127    CXString text;
3128    text = clang_getDiagnosticFixIt(D, i, &ReplacementRange);
3129
3130    printIndent(indent);
3131    fprintf(stderr, "FIXIT: (");
3132    printLocation(clang_getRangeStart(ReplacementRange));
3133    fprintf(stderr, " - ");
3134    printLocation(clang_getRangeEnd(ReplacementRange));
3135    fprintf(stderr, "): \"%s\"\n", clang_getCString(text));
3136    clang_disposeString(text);
3137  }
3138}
3139
3140static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) {
3141  unsigned i, n;
3142
3143  if (!Diags)
3144    return;
3145
3146  n = clang_getNumDiagnosticsInSet(Diags);
3147  for (i = 0; i < n; ++i) {
3148    CXSourceLocation DiagLoc;
3149    CXDiagnostic D;
3150    CXFile File;
3151    CXString FileName, DiagSpelling, DiagOption, DiagCat;
3152    unsigned line, column, offset;
3153    const char *DiagOptionStr = 0, *DiagCatStr = 0;
3154
3155    D = clang_getDiagnosticInSet(Diags, i);
3156    DiagLoc = clang_getDiagnosticLocation(D);
3157    clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset);
3158    FileName = clang_getFileName(File);
3159    DiagSpelling = clang_getDiagnosticSpelling(D);
3160
3161    printIndent(indent);
3162
3163    fprintf(stderr, "%s:%d:%d: %s: %s",
3164            clang_getCString(FileName),
3165            line,
3166            column,
3167            getSeverityString(clang_getDiagnosticSeverity(D)),
3168            clang_getCString(DiagSpelling));
3169
3170    DiagOption = clang_getDiagnosticOption(D, 0);
3171    DiagOptionStr = clang_getCString(DiagOption);
3172    if (DiagOptionStr) {
3173      fprintf(stderr, " [%s]", DiagOptionStr);
3174    }
3175
3176    DiagCat = clang_getDiagnosticCategoryText(D);
3177    DiagCatStr = clang_getCString(DiagCat);
3178    if (DiagCatStr) {
3179      fprintf(stderr, " [%s]", DiagCatStr);
3180    }
3181
3182    fprintf(stderr, "\n");
3183
3184    printRanges(D, indent);
3185    printFixIts(D, indent);
3186
3187    /* Print subdiagnostics. */
3188    printDiagnosticSet(clang_getChildDiagnostics(D), indent+2);
3189
3190    clang_disposeString(FileName);
3191    clang_disposeString(DiagSpelling);
3192    clang_disposeString(DiagOption);
3193  }
3194}
3195
3196static int read_diagnostics(const char *filename) {
3197  enum CXLoadDiag_Error error;
3198  CXString errorString;
3199  CXDiagnosticSet Diags = 0;
3200
3201  Diags = clang_loadDiagnostics(filename, &error, &errorString);
3202  if (!Diags) {
3203    fprintf(stderr, "Trouble deserializing file (%s): %s\n",
3204            getDiagnosticCodeStr(error),
3205            clang_getCString(errorString));
3206    clang_disposeString(errorString);
3207    return 1;
3208  }
3209
3210  printDiagnosticSet(Diags, 0);
3211  fprintf(stderr, "Number of diagnostics: %d\n",
3212          clang_getNumDiagnosticsInSet(Diags));
3213  clang_disposeDiagnosticSet(Diags);
3214  return 0;
3215}
3216
3217/******************************************************************************/
3218/* Command line processing.                                                   */
3219/******************************************************************************/
3220
3221static CXCursorVisitor GetVisitor(const char *s) {
3222  if (s[0] == '\0')
3223    return FilteredPrintingVisitor;
3224  if (strcmp(s, "-usrs") == 0)
3225    return USRVisitor;
3226  if (strncmp(s, "-memory-usage", 13) == 0)
3227    return GetVisitor(s + 13);
3228  return NULL;
3229}
3230
3231static void print_usage(void) {
3232  fprintf(stderr,
3233    "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n"
3234    "       c-index-test -code-completion-timing=<site> <compiler arguments>\n"
3235    "       c-index-test -cursor-at=<site> <compiler arguments>\n"
3236    "       c-index-test -file-refs-at=<site> <compiler arguments>\n"
3237    "       c-index-test -index-file [-check-prefix=<FileCheck prefix>] <compiler arguments>\n"
3238    "       c-index-test -index-tu [-check-prefix=<FileCheck prefix>] <AST file>\n"
3239    "       c-index-test -test-file-scan <AST file> <source file> "
3240          "[FileCheck prefix]\n");
3241  fprintf(stderr,
3242    "       c-index-test -test-load-tu <AST file> <symbol filter> "
3243          "[FileCheck prefix]\n"
3244    "       c-index-test -test-load-tu-usrs <AST file> <symbol filter> "
3245           "[FileCheck prefix]\n"
3246    "       c-index-test -test-load-source <symbol filter> {<args>}*\n");
3247  fprintf(stderr,
3248    "       c-index-test -test-load-source-memory-usage "
3249    "<symbol filter> {<args>}*\n"
3250    "       c-index-test -test-load-source-reparse <trials> <symbol filter> "
3251    "          {<args>}*\n"
3252    "       c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n"
3253    "       c-index-test -test-load-source-usrs-memory-usage "
3254          "<symbol filter> {<args>}*\n"
3255    "       c-index-test -test-annotate-tokens=<range> {<args>}*\n"
3256    "       c-index-test -test-inclusion-stack-source {<args>}*\n"
3257    "       c-index-test -test-inclusion-stack-tu <AST file>\n");
3258  fprintf(stderr,
3259    "       c-index-test -test-print-linkage-source {<args>}*\n"
3260    "       c-index-test -test-print-typekind {<args>}*\n"
3261    "       c-index-test -print-usr [<CursorKind> {<args>}]*\n"
3262    "       c-index-test -print-usr-file <file>\n"
3263    "       c-index-test -write-pch <file> <compiler arguments>\n");
3264  fprintf(stderr,
3265    "       c-index-test -compilation-db [lookup <filename>] database\n");
3266  fprintf(stderr,
3267    "       c-index-test -read-diagnostics <file>\n\n");
3268  fprintf(stderr,
3269    " <symbol filter> values:\n%s",
3270    "   all - load all symbols, including those from PCH\n"
3271    "   local - load all symbols except those in PCH\n"
3272    "   category - only load ObjC categories (non-PCH)\n"
3273    "   interface - only load ObjC interfaces (non-PCH)\n"
3274    "   protocol - only load ObjC protocols (non-PCH)\n"
3275    "   function - only load functions (non-PCH)\n"
3276    "   typedef - only load typdefs (non-PCH)\n"
3277    "   scan-function - scan function bodies (non-PCH)\n\n");
3278}
3279
3280/***/
3281
3282int cindextest_main(int argc, const char **argv) {
3283  clang_enableStackTraces();
3284  if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0)
3285      return read_diagnostics(argv[2]);
3286  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1])
3287    return perform_code_completion(argc, argv, 0);
3288  if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1])
3289    return perform_code_completion(argc, argv, 1);
3290  if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1])
3291    return inspect_cursor_at(argc, argv);
3292  if (argc > 2 && strstr(argv[1], "-file-refs-at=") == argv[1])
3293    return find_file_refs_at(argc, argv);
3294  if (argc > 2 && strcmp(argv[1], "-index-file") == 0)
3295    return index_file(argc - 2, argv + 2);
3296  if (argc > 2 && strcmp(argv[1], "-index-tu") == 0)
3297    return index_tu(argc - 2, argv + 2);
3298  else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) {
3299    CXCursorVisitor I = GetVisitor(argv[1] + 13);
3300    if (I)
3301      return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I,
3302                                  NULL);
3303  }
3304  else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){
3305    CXCursorVisitor I = GetVisitor(argv[1] + 25);
3306    if (I) {
3307      int trials = atoi(argv[2]);
3308      return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I,
3309                                         NULL);
3310    }
3311  }
3312  else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
3313    CXCursorVisitor I = GetVisitor(argv[1] + 17);
3314
3315    PostVisitTU postVisit = 0;
3316    if (strstr(argv[1], "-memory-usage"))
3317      postVisit = PrintMemoryUsage;
3318
3319    if (I)
3320      return perform_test_load_source(argc - 3, argv + 3, argv[2], I,
3321                                      postVisit);
3322  }
3323  else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
3324    return perform_file_scan(argv[2], argv[3],
3325                             argc >= 5 ? argv[4] : 0);
3326  else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1])
3327    return perform_token_annotation(argc, argv);
3328  else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0)
3329    return perform_test_load_source(argc - 2, argv + 2, "all", NULL,
3330                                    PrintInclusionStack);
3331  else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0)
3332    return perform_test_load_tu(argv[2], "all", NULL, NULL,
3333                                PrintInclusionStack);
3334  else if (argc > 2 && strcmp(argv[1], "-test-print-linkage-source") == 0)
3335    return perform_test_load_source(argc - 2, argv + 2, "all", PrintLinkage,
3336                                    NULL);
3337  else if (argc > 2 && strcmp(argv[1], "-test-print-typekind") == 0)
3338    return perform_test_load_source(argc - 2, argv + 2, "all",
3339                                    PrintTypeKind, 0);
3340  else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) {
3341    if (argc > 2)
3342      return print_usrs(argv + 2, argv + argc);
3343    else {
3344      display_usrs();
3345      return 1;
3346    }
3347  }
3348  else if (argc > 2 && strcmp(argv[1], "-print-usr-file") == 0)
3349    return print_usrs_file(argv[2]);
3350  else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0)
3351    return write_pch_file(argv[2], argc - 3, argv + 3);
3352  else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0)
3353    return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2);
3354
3355  print_usage();
3356  return 1;
3357}
3358
3359/***/
3360
3361/* We intentionally run in a separate thread to ensure we at least minimal
3362 * testing of a multithreaded environment (for example, having a reduced stack
3363 * size). */
3364
3365typedef struct thread_info {
3366  int argc;
3367  const char **argv;
3368  int result;
3369} thread_info;
3370void thread_runner(void *client_data_v) {
3371  thread_info *client_data = client_data_v;
3372  client_data->result = cindextest_main(client_data->argc, client_data->argv);
3373#ifdef __CYGWIN__
3374  fflush(stdout);  /* stdout is not flushed on Cygwin. */
3375#endif
3376}
3377
3378int main(int argc, const char **argv) {
3379  thread_info client_data;
3380
3381#ifdef CLANG_HAVE_LIBXML
3382  LIBXML_TEST_VERSION
3383#endif
3384
3385  if (getenv("CINDEXTEST_NOTHREADS"))
3386    return cindextest_main(argc, argv);
3387
3388  client_data.argc = argc;
3389  client_data.argv = argv;
3390  clang_executeOnThread(thread_runner, &client_data, 0);
3391  return client_data.result;
3392}
3393