c-index-test.c revision e4a990f34904eb572c8d6aa1deef19465214359c
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        if (Cursor.kind == CXCursor_ObjCMessageExpr) {
1959          CXType T = clang_Cursor_getReceiverType(Cursor);
1960          CXString S = clang_getTypeKindSpelling(T.kind);
1961          printf(" Receiver-type=%s", clang_getCString(S));
1962          clang_disposeString(S);
1963        }
1964
1965        {
1966          CXModule mod = clang_Cursor_getModule(Cursor);
1967          CXString name;
1968          unsigned i, numHeaders;
1969          if (mod) {
1970            name = clang_Module_getFullName(mod);
1971            numHeaders = clang_Module_getNumTopLevelHeaders(mod);
1972            printf(" ModuleName=%s Headers(%d):",
1973                   clang_getCString(name), numHeaders);
1974            clang_disposeString(name);
1975            for (i = 0; i < numHeaders; ++i) {
1976              CXFile file = clang_Module_getTopLevelHeader(mod, i);
1977              CXString filename = clang_getFileName(file);
1978              printf("\n%s", clang_getCString(filename));
1979              clang_disposeString(filename);
1980            }
1981          }
1982        }
1983
1984        if (completionString != NULL) {
1985          printf("\nCompletion string: ");
1986          print_completion_string(completionString, stdout);
1987        }
1988        printf("\n");
1989        free(Locations[Loc].filename);
1990      }
1991    }
1992  }
1993
1994  PrintDiagnostics(TU);
1995  clang_disposeTranslationUnit(TU);
1996  clang_disposeIndex(CIdx);
1997  free(Locations);
1998  free_remapped_files(unsaved_files, num_unsaved_files);
1999  return 0;
2000}
2001
2002static enum CXVisitorResult findFileRefsVisit(void *context,
2003                                         CXCursor cursor, CXSourceRange range) {
2004  if (clang_Range_isNull(range))
2005    return CXVisit_Continue;
2006
2007  PrintCursor(cursor, NULL);
2008  PrintRange(range, "");
2009  printf("\n");
2010  return CXVisit_Continue;
2011}
2012
2013static int find_file_refs_at(int argc, const char **argv) {
2014  CXIndex CIdx;
2015  int errorCode;
2016  struct CXUnsavedFile *unsaved_files = 0;
2017  int num_unsaved_files = 0;
2018  CXTranslationUnit TU;
2019  CXCursor Cursor;
2020  CursorSourceLocation *Locations = 0;
2021  unsigned NumLocations = 0, Loc;
2022  unsigned Repeats = 1;
2023  unsigned I;
2024
2025  /* Count the number of locations. */
2026  while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1])
2027    ++NumLocations;
2028
2029  /* Parse the locations. */
2030  assert(NumLocations > 0 && "Unable to count locations?");
2031  Locations = (CursorSourceLocation *)malloc(
2032                                  NumLocations * sizeof(CursorSourceLocation));
2033  for (Loc = 0; Loc < NumLocations; ++Loc) {
2034    const char *input = argv[Loc + 1] + strlen("-file-refs-at=");
2035    if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename,
2036                                            &Locations[Loc].line,
2037                                            &Locations[Loc].column, 0, 0)))
2038      return errorCode;
2039  }
2040
2041  if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files,
2042                           &num_unsaved_files))
2043    return -1;
2044
2045  if (getenv("CINDEXTEST_EDITING"))
2046    Repeats = 5;
2047
2048  /* Parse the translation unit. When we're testing clang_getCursor() after
2049     reparsing, don't remap unsaved files until the second parse. */
2050  CIdx = clang_createIndex(1, 1);
2051  TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
2052                                  argv + num_unsaved_files + 1 + NumLocations,
2053                                  argc - num_unsaved_files - 2 - NumLocations,
2054                                  unsaved_files,
2055                                  Repeats > 1? 0 : num_unsaved_files,
2056                                  getDefaultParsingOptions());
2057
2058  if (!TU) {
2059    fprintf(stderr, "unable to parse input\n");
2060    return -1;
2061  }
2062
2063  if (checkForErrors(TU) != 0)
2064    return -1;
2065
2066  for (I = 0; I != Repeats; ++I) {
2067    if (Repeats > 1 &&
2068        clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
2069                                     clang_defaultReparseOptions(TU))) {
2070      clang_disposeTranslationUnit(TU);
2071      return 1;
2072    }
2073
2074    if (checkForErrors(TU) != 0)
2075      return -1;
2076
2077    for (Loc = 0; Loc < NumLocations; ++Loc) {
2078      CXFile file = clang_getFile(TU, Locations[Loc].filename);
2079      if (!file)
2080        continue;
2081
2082      Cursor = clang_getCursor(TU,
2083                               clang_getLocation(TU, file, Locations[Loc].line,
2084                                                 Locations[Loc].column));
2085
2086      if (checkForErrors(TU) != 0)
2087        return -1;
2088
2089      if (I + 1 == Repeats) {
2090        CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit };
2091        PrintCursor(Cursor, NULL);
2092        printf("\n");
2093        clang_findReferencesInFile(Cursor, file, visitor);
2094        free(Locations[Loc].filename);
2095
2096        if (checkForErrors(TU) != 0)
2097          return -1;
2098      }
2099    }
2100  }
2101
2102  PrintDiagnostics(TU);
2103  clang_disposeTranslationUnit(TU);
2104  clang_disposeIndex(CIdx);
2105  free(Locations);
2106  free_remapped_files(unsaved_files, num_unsaved_files);
2107  return 0;
2108}
2109
2110#define MAX_IMPORTED_ASTFILES 200
2111
2112typedef struct {
2113  char **filenames;
2114  unsigned num_files;
2115} ImportedASTFilesData;
2116
2117static ImportedASTFilesData *importedASTs_create() {
2118  ImportedASTFilesData *p;
2119  p = malloc(sizeof(ImportedASTFilesData));
2120  p->filenames = malloc(MAX_IMPORTED_ASTFILES * sizeof(const char *));
2121  p->num_files = 0;
2122  return p;
2123}
2124
2125static void importedASTs_dispose(ImportedASTFilesData *p) {
2126  unsigned i;
2127  if (!p)
2128    return;
2129
2130  for (i = 0; i < p->num_files; ++i)
2131    free(p->filenames[i]);
2132  free(p->filenames);
2133  free(p);
2134}
2135
2136static void importedASTS_insert(ImportedASTFilesData *p, const char *file) {
2137  unsigned i;
2138  assert(p && file);
2139  for (i = 0; i < p->num_files; ++i)
2140    if (strcmp(file, p->filenames[i]) == 0)
2141      return;
2142  assert(p->num_files + 1 < MAX_IMPORTED_ASTFILES);
2143  p->filenames[p->num_files++] = strdup(file);
2144}
2145
2146typedef struct {
2147  const char *check_prefix;
2148  int first_check_printed;
2149  int fail_for_error;
2150  int abort;
2151  const char *main_filename;
2152  ImportedASTFilesData *importedASTs;
2153} IndexData;
2154
2155static void printCheck(IndexData *data) {
2156  if (data->check_prefix) {
2157    if (data->first_check_printed) {
2158      printf("// %s-NEXT: ", data->check_prefix);
2159    } else {
2160      printf("// %s     : ", data->check_prefix);
2161      data->first_check_printed = 1;
2162    }
2163  }
2164}
2165
2166static void printCXIndexFile(CXIdxClientFile file) {
2167  CXString filename = clang_getFileName((CXFile)file);
2168  printf("%s", clang_getCString(filename));
2169  clang_disposeString(filename);
2170}
2171
2172static void printCXIndexLoc(CXIdxLoc loc, CXClientData client_data) {
2173  IndexData *index_data;
2174  CXString filename;
2175  const char *cname;
2176  CXIdxClientFile file;
2177  unsigned line, column;
2178  int isMainFile;
2179
2180  index_data = (IndexData *)client_data;
2181  clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0);
2182  if (line == 0) {
2183    printf("<invalid>");
2184    return;
2185  }
2186  if (!file) {
2187    printf("<no idxfile>");
2188    return;
2189  }
2190  filename = clang_getFileName((CXFile)file);
2191  cname = clang_getCString(filename);
2192  if (strcmp(cname, index_data->main_filename) == 0)
2193    isMainFile = 1;
2194  else
2195    isMainFile = 0;
2196  clang_disposeString(filename);
2197
2198  if (!isMainFile) {
2199    printCXIndexFile(file);
2200    printf(":");
2201  }
2202  printf("%d:%d", line, column);
2203}
2204
2205static unsigned digitCount(unsigned val) {
2206  unsigned c = 1;
2207  while (1) {
2208    if (val < 10)
2209      return c;
2210    ++c;
2211    val /= 10;
2212  }
2213}
2214
2215static CXIdxClientContainer makeClientContainer(const CXIdxEntityInfo *info,
2216                                                CXIdxLoc loc) {
2217  const char *name;
2218  char *newStr;
2219  CXIdxClientFile file;
2220  unsigned line, column;
2221
2222  name = info->name;
2223  if (!name)
2224    name = "<anon-tag>";
2225
2226  clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0);
2227  /* FIXME: free these.*/
2228  newStr = (char *)malloc(strlen(name) +
2229                          digitCount(line) + digitCount(column) + 3);
2230  sprintf(newStr, "%s:%d:%d", name, line, column);
2231  return (CXIdxClientContainer)newStr;
2232}
2233
2234static void printCXIndexContainer(const CXIdxContainerInfo *info) {
2235  CXIdxClientContainer container;
2236  container = clang_index_getClientContainer(info);
2237  if (!container)
2238    printf("[<<NULL>>]");
2239  else
2240    printf("[%s]", (const char *)container);
2241}
2242
2243static const char *getEntityKindString(CXIdxEntityKind kind) {
2244  switch (kind) {
2245  case CXIdxEntity_Unexposed: return "<<UNEXPOSED>>";
2246  case CXIdxEntity_Typedef: return "typedef";
2247  case CXIdxEntity_Function: return "function";
2248  case CXIdxEntity_Variable: return "variable";
2249  case CXIdxEntity_Field: return "field";
2250  case CXIdxEntity_EnumConstant: return "enumerator";
2251  case CXIdxEntity_ObjCClass: return "objc-class";
2252  case CXIdxEntity_ObjCProtocol: return "objc-protocol";
2253  case CXIdxEntity_ObjCCategory: return "objc-category";
2254  case CXIdxEntity_ObjCInstanceMethod: return "objc-instance-method";
2255  case CXIdxEntity_ObjCClassMethod: return "objc-class-method";
2256  case CXIdxEntity_ObjCProperty: return "objc-property";
2257  case CXIdxEntity_ObjCIvar: return "objc-ivar";
2258  case CXIdxEntity_Enum: return "enum";
2259  case CXIdxEntity_Struct: return "struct";
2260  case CXIdxEntity_Union: return "union";
2261  case CXIdxEntity_CXXClass: return "c++-class";
2262  case CXIdxEntity_CXXNamespace: return "namespace";
2263  case CXIdxEntity_CXXNamespaceAlias: return "namespace-alias";
2264  case CXIdxEntity_CXXStaticVariable: return "c++-static-var";
2265  case CXIdxEntity_CXXStaticMethod: return "c++-static-method";
2266  case CXIdxEntity_CXXInstanceMethod: return "c++-instance-method";
2267  case CXIdxEntity_CXXConstructor: return "constructor";
2268  case CXIdxEntity_CXXDestructor: return "destructor";
2269  case CXIdxEntity_CXXConversionFunction: return "conversion-func";
2270  case CXIdxEntity_CXXTypeAlias: return "type-alias";
2271  case CXIdxEntity_CXXInterface: return "c++-__interface";
2272  }
2273  assert(0 && "Garbage entity kind");
2274  return 0;
2275}
2276
2277static const char *getEntityTemplateKindString(CXIdxEntityCXXTemplateKind kind) {
2278  switch (kind) {
2279  case CXIdxEntity_NonTemplate: return "";
2280  case CXIdxEntity_Template: return "-template";
2281  case CXIdxEntity_TemplatePartialSpecialization:
2282    return "-template-partial-spec";
2283  case CXIdxEntity_TemplateSpecialization: return "-template-spec";
2284  }
2285  assert(0 && "Garbage entity kind");
2286  return 0;
2287}
2288
2289static const char *getEntityLanguageString(CXIdxEntityLanguage kind) {
2290  switch (kind) {
2291  case CXIdxEntityLang_None: return "<none>";
2292  case CXIdxEntityLang_C: return "C";
2293  case CXIdxEntityLang_ObjC: return "ObjC";
2294  case CXIdxEntityLang_CXX: return "C++";
2295  }
2296  assert(0 && "Garbage language kind");
2297  return 0;
2298}
2299
2300static void printEntityInfo(const char *cb,
2301                            CXClientData client_data,
2302                            const CXIdxEntityInfo *info) {
2303  const char *name;
2304  IndexData *index_data;
2305  unsigned i;
2306  index_data = (IndexData *)client_data;
2307  printCheck(index_data);
2308
2309  if (!info) {
2310    printf("%s: <<NULL>>", cb);
2311    return;
2312  }
2313
2314  name = info->name;
2315  if (!name)
2316    name = "<anon-tag>";
2317
2318  printf("%s: kind: %s%s", cb, getEntityKindString(info->kind),
2319         getEntityTemplateKindString(info->templateKind));
2320  printf(" | name: %s", name);
2321  printf(" | USR: %s", info->USR);
2322  printf(" | lang: %s", getEntityLanguageString(info->lang));
2323
2324  for (i = 0; i != info->numAttributes; ++i) {
2325    const CXIdxAttrInfo *Attr = info->attributes[i];
2326    printf("     <attribute>: ");
2327    PrintCursor(Attr->cursor, NULL);
2328  }
2329}
2330
2331static void printBaseClassInfo(CXClientData client_data,
2332                               const CXIdxBaseClassInfo *info) {
2333  printEntityInfo("     <base>", client_data, info->base);
2334  printf(" | cursor: ");
2335  PrintCursor(info->cursor, NULL);
2336  printf(" | loc: ");
2337  printCXIndexLoc(info->loc, client_data);
2338}
2339
2340static void printProtocolList(const CXIdxObjCProtocolRefListInfo *ProtoInfo,
2341                              CXClientData client_data) {
2342  unsigned i;
2343  for (i = 0; i < ProtoInfo->numProtocols; ++i) {
2344    printEntityInfo("     <protocol>", client_data,
2345                    ProtoInfo->protocols[i]->protocol);
2346    printf(" | cursor: ");
2347    PrintCursor(ProtoInfo->protocols[i]->cursor, NULL);
2348    printf(" | loc: ");
2349    printCXIndexLoc(ProtoInfo->protocols[i]->loc, client_data);
2350    printf("\n");
2351  }
2352}
2353
2354static void index_diagnostic(CXClientData client_data,
2355                             CXDiagnosticSet diagSet, void *reserved) {
2356  CXString str;
2357  const char *cstr;
2358  unsigned numDiags, i;
2359  CXDiagnostic diag;
2360  IndexData *index_data;
2361  index_data = (IndexData *)client_data;
2362  printCheck(index_data);
2363
2364  numDiags = clang_getNumDiagnosticsInSet(diagSet);
2365  for (i = 0; i != numDiags; ++i) {
2366    diag = clang_getDiagnosticInSet(diagSet, i);
2367    str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions());
2368    cstr = clang_getCString(str);
2369    printf("[diagnostic]: %s\n", cstr);
2370    clang_disposeString(str);
2371
2372    if (getenv("CINDEXTEST_FAILONERROR") &&
2373        clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) {
2374      index_data->fail_for_error = 1;
2375    }
2376  }
2377}
2378
2379static CXIdxClientFile index_enteredMainFile(CXClientData client_data,
2380                                       CXFile file, void *reserved) {
2381  IndexData *index_data;
2382  CXString filename;
2383
2384  index_data = (IndexData *)client_data;
2385  printCheck(index_data);
2386
2387  filename = clang_getFileName(file);
2388  index_data->main_filename = clang_getCString(filename);
2389  clang_disposeString(filename);
2390
2391  printf("[enteredMainFile]: ");
2392  printCXIndexFile((CXIdxClientFile)file);
2393  printf("\n");
2394
2395  return (CXIdxClientFile)file;
2396}
2397
2398static CXIdxClientFile index_ppIncludedFile(CXClientData client_data,
2399                                            const CXIdxIncludedFileInfo *info) {
2400  IndexData *index_data;
2401  index_data = (IndexData *)client_data;
2402  printCheck(index_data);
2403
2404  printf("[ppIncludedFile]: ");
2405  printCXIndexFile((CXIdxClientFile)info->file);
2406  printf(" | name: \"%s\"", info->filename);
2407  printf(" | hash loc: ");
2408  printCXIndexLoc(info->hashLoc, client_data);
2409  printf(" | isImport: %d | isAngled: %d | isModule: %d\n",
2410         info->isImport, info->isAngled, info->isModuleImport);
2411
2412  return (CXIdxClientFile)info->file;
2413}
2414
2415static CXIdxClientFile index_importedASTFile(CXClientData client_data,
2416                                         const CXIdxImportedASTFileInfo *info) {
2417  IndexData *index_data;
2418  index_data = (IndexData *)client_data;
2419  printCheck(index_data);
2420
2421  if (index_data->importedASTs) {
2422    CXString filename = clang_getFileName(info->file);
2423    importedASTS_insert(index_data->importedASTs, clang_getCString(filename));
2424    clang_disposeString(filename);
2425  }
2426
2427  printf("[importedASTFile]: ");
2428  printCXIndexFile((CXIdxClientFile)info->file);
2429  if (info->module) {
2430    CXString name = clang_Module_getFullName(info->module);
2431    printf(" | loc: ");
2432    printCXIndexLoc(info->loc, client_data);
2433    printf(" | name: \"%s\"", clang_getCString(name));
2434    printf(" | isImplicit: %d\n", info->isImplicit);
2435    clang_disposeString(name);
2436  } else {
2437    /* PCH file, the rest are not relevant. */
2438    printf("\n");
2439  }
2440
2441  return (CXIdxClientFile)info->file;
2442}
2443
2444static CXIdxClientContainer index_startedTranslationUnit(CXClientData client_data,
2445                                                   void *reserved) {
2446  IndexData *index_data;
2447  index_data = (IndexData *)client_data;
2448  printCheck(index_data);
2449
2450  printf("[startedTranslationUnit]\n");
2451  return (CXIdxClientContainer)"TU";
2452}
2453
2454static void index_indexDeclaration(CXClientData client_data,
2455                                   const CXIdxDeclInfo *info) {
2456  IndexData *index_data;
2457  const CXIdxObjCCategoryDeclInfo *CatInfo;
2458  const CXIdxObjCInterfaceDeclInfo *InterInfo;
2459  const CXIdxObjCProtocolRefListInfo *ProtoInfo;
2460  const CXIdxObjCPropertyDeclInfo *PropInfo;
2461  const CXIdxCXXClassDeclInfo *CXXClassInfo;
2462  unsigned i;
2463  index_data = (IndexData *)client_data;
2464
2465  printEntityInfo("[indexDeclaration]", client_data, info->entityInfo);
2466  printf(" | cursor: ");
2467  PrintCursor(info->cursor, NULL);
2468  printf(" | loc: ");
2469  printCXIndexLoc(info->loc, client_data);
2470  printf(" | semantic-container: ");
2471  printCXIndexContainer(info->semanticContainer);
2472  printf(" | lexical-container: ");
2473  printCXIndexContainer(info->lexicalContainer);
2474  printf(" | isRedecl: %d", info->isRedeclaration);
2475  printf(" | isDef: %d", info->isDefinition);
2476  printf(" | isContainer: %d", info->isContainer);
2477  printf(" | isImplicit: %d\n", info->isImplicit);
2478
2479  for (i = 0; i != info->numAttributes; ++i) {
2480    const CXIdxAttrInfo *Attr = info->attributes[i];
2481    printf("     <attribute>: ");
2482    PrintCursor(Attr->cursor, NULL);
2483    printf("\n");
2484  }
2485
2486  if (clang_index_isEntityObjCContainerKind(info->entityInfo->kind)) {
2487    const char *kindName = 0;
2488    CXIdxObjCContainerKind K = clang_index_getObjCContainerDeclInfo(info)->kind;
2489    switch (K) {
2490    case CXIdxObjCContainer_ForwardRef:
2491      kindName = "forward-ref"; break;
2492    case CXIdxObjCContainer_Interface:
2493      kindName = "interface"; break;
2494    case CXIdxObjCContainer_Implementation:
2495      kindName = "implementation"; break;
2496    }
2497    printCheck(index_data);
2498    printf("     <ObjCContainerInfo>: kind: %s\n", kindName);
2499  }
2500
2501  if ((CatInfo = clang_index_getObjCCategoryDeclInfo(info))) {
2502    printEntityInfo("     <ObjCCategoryInfo>: class", client_data,
2503                    CatInfo->objcClass);
2504    printf(" | cursor: ");
2505    PrintCursor(CatInfo->classCursor, NULL);
2506    printf(" | loc: ");
2507    printCXIndexLoc(CatInfo->classLoc, client_data);
2508    printf("\n");
2509  }
2510
2511  if ((InterInfo = clang_index_getObjCInterfaceDeclInfo(info))) {
2512    if (InterInfo->superInfo) {
2513      printBaseClassInfo(client_data, InterInfo->superInfo);
2514      printf("\n");
2515    }
2516  }
2517
2518  if ((ProtoInfo = clang_index_getObjCProtocolRefListInfo(info))) {
2519    printProtocolList(ProtoInfo, client_data);
2520  }
2521
2522  if ((PropInfo = clang_index_getObjCPropertyDeclInfo(info))) {
2523    if (PropInfo->getter) {
2524      printEntityInfo("     <getter>", client_data, PropInfo->getter);
2525      printf("\n");
2526    }
2527    if (PropInfo->setter) {
2528      printEntityInfo("     <setter>", client_data, PropInfo->setter);
2529      printf("\n");
2530    }
2531  }
2532
2533  if ((CXXClassInfo = clang_index_getCXXClassDeclInfo(info))) {
2534    for (i = 0; i != CXXClassInfo->numBases; ++i) {
2535      printBaseClassInfo(client_data, CXXClassInfo->bases[i]);
2536      printf("\n");
2537    }
2538  }
2539
2540  if (info->declAsContainer)
2541    clang_index_setClientContainer(info->declAsContainer,
2542                              makeClientContainer(info->entityInfo, info->loc));
2543}
2544
2545static void index_indexEntityReference(CXClientData client_data,
2546                                       const CXIdxEntityRefInfo *info) {
2547  printEntityInfo("[indexEntityReference]", client_data, info->referencedEntity);
2548  printf(" | cursor: ");
2549  PrintCursor(info->cursor, NULL);
2550  printf(" | loc: ");
2551  printCXIndexLoc(info->loc, client_data);
2552  printEntityInfo(" | <parent>:", client_data, info->parentEntity);
2553  printf(" | container: ");
2554  printCXIndexContainer(info->container);
2555  printf(" | refkind: ");
2556  switch (info->kind) {
2557  case CXIdxEntityRef_Direct: printf("direct"); break;
2558  case CXIdxEntityRef_Implicit: printf("implicit"); break;
2559  }
2560  printf("\n");
2561}
2562
2563static int index_abortQuery(CXClientData client_data, void *reserved) {
2564  IndexData *index_data;
2565  index_data = (IndexData *)client_data;
2566  return index_data->abort;
2567}
2568
2569static IndexerCallbacks IndexCB = {
2570  index_abortQuery,
2571  index_diagnostic,
2572  index_enteredMainFile,
2573  index_ppIncludedFile,
2574  index_importedASTFile,
2575  index_startedTranslationUnit,
2576  index_indexDeclaration,
2577  index_indexEntityReference
2578};
2579
2580static unsigned getIndexOptions(void) {
2581  unsigned index_opts;
2582  index_opts = 0;
2583  if (getenv("CINDEXTEST_SUPPRESSREFS"))
2584    index_opts |= CXIndexOpt_SuppressRedundantRefs;
2585  if (getenv("CINDEXTEST_INDEXLOCALSYMBOLS"))
2586    index_opts |= CXIndexOpt_IndexFunctionLocalSymbols;
2587
2588  return index_opts;
2589}
2590
2591static int index_file(int argc, const char **argv, int full) {
2592  const char *check_prefix;
2593  CXIndex Idx;
2594  CXIndexAction idxAction;
2595  IndexData index_data;
2596  unsigned index_opts;
2597  int result;
2598
2599  check_prefix = 0;
2600  if (argc > 0) {
2601    if (strstr(argv[0], "-check-prefix=") == argv[0]) {
2602      check_prefix = argv[0] + strlen("-check-prefix=");
2603      ++argv;
2604      --argc;
2605    }
2606  }
2607
2608  if (argc == 0) {
2609    fprintf(stderr, "no compiler arguments\n");
2610    return -1;
2611  }
2612
2613  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
2614                                /* displayDiagnosics=*/1))) {
2615    fprintf(stderr, "Could not create Index\n");
2616    return 1;
2617  }
2618  idxAction = 0;
2619
2620  index_data.check_prefix = check_prefix;
2621  index_data.first_check_printed = 0;
2622  index_data.fail_for_error = 0;
2623  index_data.abort = 0;
2624  index_data.main_filename = "";
2625  index_data.importedASTs = 0;
2626
2627  if (full)
2628    index_data.importedASTs = importedASTs_create();
2629
2630  index_opts = getIndexOptions();
2631  idxAction = clang_IndexAction_create(Idx);
2632  result = clang_indexSourceFile(idxAction, &index_data,
2633                                 &IndexCB,sizeof(IndexCB), index_opts,
2634                                 0, argv, argc, 0, 0, 0,
2635                                 getDefaultParsingOptions());
2636  if (index_data.fail_for_error)
2637    result = -1;
2638
2639  if (full) {
2640    CXTranslationUnit TU;
2641    unsigned i;
2642
2643    for (i = 0; i < index_data.importedASTs->num_files; ++i) {
2644      if (!CreateTranslationUnit(Idx, index_data.importedASTs->filenames[i],
2645                                 &TU)) {
2646        result = -1;
2647        goto finished;
2648      }
2649      result = clang_indexTranslationUnit(idxAction, &index_data,
2650                                          &IndexCB,sizeof(IndexCB),
2651                                          index_opts, TU);
2652      clang_disposeTranslationUnit(TU);
2653    }
2654  }
2655
2656finished:
2657  importedASTs_dispose(index_data.importedASTs);
2658  clang_IndexAction_dispose(idxAction);
2659  clang_disposeIndex(Idx);
2660  return result;
2661}
2662
2663static int index_tu(int argc, const char **argv) {
2664  CXIndex Idx;
2665  CXIndexAction idxAction;
2666  CXTranslationUnit TU;
2667  const char *check_prefix;
2668  IndexData index_data;
2669  unsigned index_opts;
2670  int result;
2671
2672  check_prefix = 0;
2673  if (argc > 0) {
2674    if (strstr(argv[0], "-check-prefix=") == argv[0]) {
2675      check_prefix = argv[0] + strlen("-check-prefix=");
2676      ++argv;
2677      --argc;
2678    }
2679  }
2680
2681  if (argc == 0) {
2682    fprintf(stderr, "no ast file\n");
2683    return -1;
2684  }
2685
2686  if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1,
2687                                /* displayDiagnosics=*/1))) {
2688    fprintf(stderr, "Could not create Index\n");
2689    return 1;
2690  }
2691  idxAction = 0;
2692  TU = 0;
2693  result = 1;
2694
2695  if (!CreateTranslationUnit(Idx, argv[0], &TU))
2696    goto finished;
2697
2698  index_data.check_prefix = check_prefix;
2699  index_data.first_check_printed = 0;
2700  index_data.fail_for_error = 0;
2701  index_data.abort = 0;
2702  index_data.main_filename = "";
2703  index_data.importedASTs = 0;
2704
2705  index_opts = getIndexOptions();
2706  idxAction = clang_IndexAction_create(Idx);
2707  result = clang_indexTranslationUnit(idxAction, &index_data,
2708                                      &IndexCB,sizeof(IndexCB),
2709                                      index_opts, TU);
2710  if (index_data.fail_for_error)
2711    goto finished;
2712
2713  finished:
2714  clang_IndexAction_dispose(idxAction);
2715  clang_disposeTranslationUnit(TU);
2716  clang_disposeIndex(Idx);
2717
2718  return result;
2719}
2720
2721int perform_token_annotation(int argc, const char **argv) {
2722  const char *input = argv[1];
2723  char *filename = 0;
2724  unsigned line, second_line;
2725  unsigned column, second_column;
2726  CXIndex CIdx;
2727  CXTranslationUnit TU = 0;
2728  int errorCode;
2729  struct CXUnsavedFile *unsaved_files = 0;
2730  int num_unsaved_files = 0;
2731  CXToken *tokens;
2732  unsigned num_tokens;
2733  CXSourceRange range;
2734  CXSourceLocation startLoc, endLoc;
2735  CXFile file = 0;
2736  CXCursor *cursors = 0;
2737  unsigned i;
2738
2739  input += strlen("-test-annotate-tokens=");
2740  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
2741                                          &second_line, &second_column)))
2742    return errorCode;
2743
2744  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) {
2745    free(filename);
2746    return -1;
2747  }
2748
2749  CIdx = clang_createIndex(0, 1);
2750  TU = clang_parseTranslationUnit(CIdx, argv[argc - 1],
2751                                  argv + num_unsaved_files + 2,
2752                                  argc - num_unsaved_files - 3,
2753                                  unsaved_files,
2754                                  num_unsaved_files,
2755                                  getDefaultParsingOptions());
2756  if (!TU) {
2757    fprintf(stderr, "unable to parse input\n");
2758    clang_disposeIndex(CIdx);
2759    free(filename);
2760    free_remapped_files(unsaved_files, num_unsaved_files);
2761    return -1;
2762  }
2763  errorCode = 0;
2764
2765  if (checkForErrors(TU) != 0) {
2766    errorCode = -1;
2767    goto teardown;
2768  }
2769
2770  if (getenv("CINDEXTEST_EDITING")) {
2771    for (i = 0; i < 5; ++i) {
2772      if (clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
2773                                       clang_defaultReparseOptions(TU))) {
2774        fprintf(stderr, "Unable to reparse translation unit!\n");
2775        errorCode = -1;
2776        goto teardown;
2777      }
2778    }
2779  }
2780
2781  if (checkForErrors(TU) != 0) {
2782    errorCode = -1;
2783    goto teardown;
2784  }
2785
2786  file = clang_getFile(TU, filename);
2787  if (!file) {
2788    fprintf(stderr, "file %s is not in this translation unit\n", filename);
2789    errorCode = -1;
2790    goto teardown;
2791  }
2792
2793  startLoc = clang_getLocation(TU, file, line, column);
2794  if (clang_equalLocations(clang_getNullLocation(), startLoc)) {
2795    fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line,
2796            column);
2797    errorCode = -1;
2798    goto teardown;
2799  }
2800
2801  endLoc = clang_getLocation(TU, file, second_line, second_column);
2802  if (clang_equalLocations(clang_getNullLocation(), endLoc)) {
2803    fprintf(stderr, "invalid source location %s:%d:%d\n", filename,
2804            second_line, second_column);
2805    errorCode = -1;
2806    goto teardown;
2807  }
2808
2809  range = clang_getRange(startLoc, endLoc);
2810  clang_tokenize(TU, range, &tokens, &num_tokens);
2811
2812  if (checkForErrors(TU) != 0) {
2813    errorCode = -1;
2814    goto teardown;
2815  }
2816
2817  cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor));
2818  clang_annotateTokens(TU, tokens, num_tokens, cursors);
2819
2820  if (checkForErrors(TU) != 0) {
2821    errorCode = -1;
2822    goto teardown;
2823  }
2824
2825  for (i = 0; i != num_tokens; ++i) {
2826    const char *kind = "<unknown>";
2827    CXString spelling = clang_getTokenSpelling(TU, tokens[i]);
2828    CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]);
2829    unsigned start_line, start_column, end_line, end_column;
2830
2831    switch (clang_getTokenKind(tokens[i])) {
2832    case CXToken_Punctuation: kind = "Punctuation"; break;
2833    case CXToken_Keyword: kind = "Keyword"; break;
2834    case CXToken_Identifier: kind = "Identifier"; break;
2835    case CXToken_Literal: kind = "Literal"; break;
2836    case CXToken_Comment: kind = "Comment"; break;
2837    }
2838    clang_getSpellingLocation(clang_getRangeStart(extent),
2839                              0, &start_line, &start_column, 0);
2840    clang_getSpellingLocation(clang_getRangeEnd(extent),
2841                              0, &end_line, &end_column, 0);
2842    printf("%s: \"%s\" ", kind, clang_getCString(spelling));
2843    clang_disposeString(spelling);
2844    PrintExtent(stdout, start_line, start_column, end_line, end_column);
2845    if (!clang_isInvalid(cursors[i].kind)) {
2846      printf(" ");
2847      PrintCursor(cursors[i], NULL);
2848    }
2849    printf("\n");
2850  }
2851  free(cursors);
2852  clang_disposeTokens(TU, tokens, num_tokens);
2853
2854 teardown:
2855  PrintDiagnostics(TU);
2856  clang_disposeTranslationUnit(TU);
2857  clang_disposeIndex(CIdx);
2858  free(filename);
2859  free_remapped_files(unsaved_files, num_unsaved_files);
2860  return errorCode;
2861}
2862
2863static int
2864perform_test_compilation_db(const char *database, int argc, const char **argv) {
2865  CXCompilationDatabase db;
2866  CXCompileCommands CCmds;
2867  CXCompileCommand CCmd;
2868  CXCompilationDatabase_Error ec;
2869  CXString wd;
2870  CXString arg;
2871  int errorCode = 0;
2872  char *tmp;
2873  unsigned len;
2874  char *buildDir;
2875  int i, j, a, numCmds, numArgs;
2876
2877  len = strlen(database);
2878  tmp = (char *) malloc(len+1);
2879  memcpy(tmp, database, len+1);
2880  buildDir = dirname(tmp);
2881
2882  db = clang_CompilationDatabase_fromDirectory(buildDir, &ec);
2883
2884  if (db) {
2885
2886    if (ec!=CXCompilationDatabase_NoError) {
2887      printf("unexpected error %d code while loading compilation database\n", ec);
2888      errorCode = -1;
2889      goto cdb_end;
2890    }
2891
2892    for (i=0; i<argc && errorCode==0; ) {
2893      if (strcmp(argv[i],"lookup")==0){
2894        CCmds = clang_CompilationDatabase_getCompileCommands(db, argv[i+1]);
2895
2896        if (!CCmds) {
2897          printf("file %s not found in compilation db\n", argv[i+1]);
2898          errorCode = -1;
2899          break;
2900        }
2901
2902        numCmds = clang_CompileCommands_getSize(CCmds);
2903
2904        if (numCmds==0) {
2905          fprintf(stderr, "should not get an empty compileCommand set for file"
2906                          " '%s'\n", argv[i+1]);
2907          errorCode = -1;
2908          break;
2909        }
2910
2911        for (j=0; j<numCmds; ++j) {
2912          CCmd = clang_CompileCommands_getCommand(CCmds, j);
2913
2914          wd = clang_CompileCommand_getDirectory(CCmd);
2915          printf("workdir:'%s'", clang_getCString(wd));
2916          clang_disposeString(wd);
2917
2918          printf(" cmdline:'");
2919          numArgs = clang_CompileCommand_getNumArgs(CCmd);
2920          for (a=0; a<numArgs; ++a) {
2921            if (a) printf(" ");
2922            arg = clang_CompileCommand_getArg(CCmd, a);
2923            printf("%s", clang_getCString(arg));
2924            clang_disposeString(arg);
2925          }
2926          printf("'\n");
2927        }
2928
2929        clang_CompileCommands_dispose(CCmds);
2930
2931        i += 2;
2932      }
2933    }
2934    clang_CompilationDatabase_dispose(db);
2935  } else {
2936    printf("database loading failed with error code %d.\n", ec);
2937    errorCode = -1;
2938  }
2939
2940cdb_end:
2941  free(tmp);
2942
2943  return errorCode;
2944}
2945
2946/******************************************************************************/
2947/* USR printing.                                                              */
2948/******************************************************************************/
2949
2950static int insufficient_usr(const char *kind, const char *usage) {
2951  fprintf(stderr, "USR for '%s' requires: %s\n", kind, usage);
2952  return 1;
2953}
2954
2955static unsigned isUSR(const char *s) {
2956  return s[0] == 'c' && s[1] == ':';
2957}
2958
2959static int not_usr(const char *s, const char *arg) {
2960  fprintf(stderr, "'%s' argument ('%s') is not a USR\n", s, arg);
2961  return 1;
2962}
2963
2964static void print_usr(CXString usr) {
2965  const char *s = clang_getCString(usr);
2966  printf("%s\n", s);
2967  clang_disposeString(usr);
2968}
2969
2970static void display_usrs() {
2971  fprintf(stderr, "-print-usrs options:\n"
2972        " ObjCCategory <class name> <category name>\n"
2973        " ObjCClass <class name>\n"
2974        " ObjCIvar <ivar name> <class USR>\n"
2975        " ObjCMethod <selector> [0=class method|1=instance method] "
2976            "<class USR>\n"
2977          " ObjCProperty <property name> <class USR>\n"
2978          " ObjCProtocol <protocol name>\n");
2979}
2980
2981int print_usrs(const char **I, const char **E) {
2982  while (I != E) {
2983    const char *kind = *I;
2984    unsigned len = strlen(kind);
2985    switch (len) {
2986      case 8:
2987        if (memcmp(kind, "ObjCIvar", 8) == 0) {
2988          if (I + 2 >= E)
2989            return insufficient_usr(kind, "<ivar name> <class USR>");
2990          if (!isUSR(I[2]))
2991            return not_usr("<class USR>", I[2]);
2992          else {
2993            CXString x;
2994            x.data = (void*) I[2];
2995            x.private_flags = 0;
2996            print_usr(clang_constructUSR_ObjCIvar(I[1], x));
2997          }
2998
2999          I += 3;
3000          continue;
3001        }
3002        break;
3003      case 9:
3004        if (memcmp(kind, "ObjCClass", 9) == 0) {
3005          if (I + 1 >= E)
3006            return insufficient_usr(kind, "<class name>");
3007          print_usr(clang_constructUSR_ObjCClass(I[1]));
3008          I += 2;
3009          continue;
3010        }
3011        break;
3012      case 10:
3013        if (memcmp(kind, "ObjCMethod", 10) == 0) {
3014          if (I + 3 >= E)
3015            return insufficient_usr(kind, "<method selector> "
3016                "[0=class method|1=instance method] <class USR>");
3017          if (!isUSR(I[3]))
3018            return not_usr("<class USR>", I[3]);
3019          else {
3020            CXString x;
3021            x.data = (void*) I[3];
3022            x.private_flags = 0;
3023            print_usr(clang_constructUSR_ObjCMethod(I[1], atoi(I[2]), x));
3024          }
3025          I += 4;
3026          continue;
3027        }
3028        break;
3029      case 12:
3030        if (memcmp(kind, "ObjCCategory", 12) == 0) {
3031          if (I + 2 >= E)
3032            return insufficient_usr(kind, "<class name> <category name>");
3033          print_usr(clang_constructUSR_ObjCCategory(I[1], I[2]));
3034          I += 3;
3035          continue;
3036        }
3037        if (memcmp(kind, "ObjCProtocol", 12) == 0) {
3038          if (I + 1 >= E)
3039            return insufficient_usr(kind, "<protocol name>");
3040          print_usr(clang_constructUSR_ObjCProtocol(I[1]));
3041          I += 2;
3042          continue;
3043        }
3044        if (memcmp(kind, "ObjCProperty", 12) == 0) {
3045          if (I + 2 >= E)
3046            return insufficient_usr(kind, "<property name> <class USR>");
3047          if (!isUSR(I[2]))
3048            return not_usr("<class USR>", I[2]);
3049          else {
3050            CXString x;
3051            x.data = (void*) I[2];
3052            x.private_flags = 0;
3053            print_usr(clang_constructUSR_ObjCProperty(I[1], x));
3054          }
3055          I += 3;
3056          continue;
3057        }
3058        break;
3059      default:
3060        break;
3061    }
3062    break;
3063  }
3064
3065  if (I != E) {
3066    fprintf(stderr, "Invalid USR kind: %s\n", *I);
3067    display_usrs();
3068    return 1;
3069  }
3070  return 0;
3071}
3072
3073int print_usrs_file(const char *file_name) {
3074  char line[2048];
3075  const char *args[128];
3076  unsigned numChars = 0;
3077
3078  FILE *fp = fopen(file_name, "r");
3079  if (!fp) {
3080    fprintf(stderr, "error: cannot open '%s'\n", file_name);
3081    return 1;
3082  }
3083
3084  /* This code is not really all that safe, but it works fine for testing. */
3085  while (!feof(fp)) {
3086    char c = fgetc(fp);
3087    if (c == '\n') {
3088      unsigned i = 0;
3089      const char *s = 0;
3090
3091      if (numChars == 0)
3092        continue;
3093
3094      line[numChars] = '\0';
3095      numChars = 0;
3096
3097      if (line[0] == '/' && line[1] == '/')
3098        continue;
3099
3100      s = strtok(line, " ");
3101      while (s) {
3102        args[i] = s;
3103        ++i;
3104        s = strtok(0, " ");
3105      }
3106      if (print_usrs(&args[0], &args[i]))
3107        return 1;
3108    }
3109    else
3110      line[numChars++] = c;
3111  }
3112
3113  fclose(fp);
3114  return 0;
3115}
3116
3117/******************************************************************************/
3118/* Command line processing.                                                   */
3119/******************************************************************************/
3120int write_pch_file(const char *filename, int argc, const char *argv[]) {
3121  CXIndex Idx;
3122  CXTranslationUnit TU;
3123  struct CXUnsavedFile *unsaved_files = 0;
3124  int num_unsaved_files = 0;
3125  int result = 0;
3126
3127  Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnosics=*/1);
3128
3129  if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) {
3130    clang_disposeIndex(Idx);
3131    return -1;
3132  }
3133
3134  TU = clang_parseTranslationUnit(Idx, 0,
3135                                  argv + num_unsaved_files,
3136                                  argc - num_unsaved_files,
3137                                  unsaved_files,
3138                                  num_unsaved_files,
3139                                  CXTranslationUnit_Incomplete |
3140                                    CXTranslationUnit_ForSerialization);
3141  if (!TU) {
3142    fprintf(stderr, "Unable to load translation unit!\n");
3143    free_remapped_files(unsaved_files, num_unsaved_files);
3144    clang_disposeIndex(Idx);
3145    return 1;
3146  }
3147
3148  switch (clang_saveTranslationUnit(TU, filename,
3149                                    clang_defaultSaveOptions(TU))) {
3150  case CXSaveError_None:
3151    break;
3152
3153  case CXSaveError_TranslationErrors:
3154    fprintf(stderr, "Unable to write PCH file %s: translation errors\n",
3155            filename);
3156    result = 2;
3157    break;
3158
3159  case CXSaveError_InvalidTU:
3160    fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n",
3161            filename);
3162    result = 3;
3163    break;
3164
3165  case CXSaveError_Unknown:
3166  default:
3167    fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename);
3168    result = 1;
3169    break;
3170  }
3171
3172  clang_disposeTranslationUnit(TU);
3173  free_remapped_files(unsaved_files, num_unsaved_files);
3174  clang_disposeIndex(Idx);
3175  return result;
3176}
3177
3178/******************************************************************************/
3179/* Serialized diagnostics.                                                    */
3180/******************************************************************************/
3181
3182static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) {
3183  switch (error) {
3184    case CXLoadDiag_CannotLoad: return "Cannot Load File";
3185    case CXLoadDiag_None: break;
3186    case CXLoadDiag_Unknown: return "Unknown";
3187    case CXLoadDiag_InvalidFile: return "Invalid File";
3188  }
3189  return "None";
3190}
3191
3192static const char *getSeverityString(enum CXDiagnosticSeverity severity) {
3193  switch (severity) {
3194    case CXDiagnostic_Note: return "note";
3195    case CXDiagnostic_Error: return "error";
3196    case CXDiagnostic_Fatal: return "fatal";
3197    case CXDiagnostic_Ignored: return "ignored";
3198    case CXDiagnostic_Warning: return "warning";
3199  }
3200  return "unknown";
3201}
3202
3203static void printIndent(unsigned indent) {
3204  if (indent == 0)
3205    return;
3206  fprintf(stderr, "+");
3207  --indent;
3208  while (indent > 0) {
3209    fprintf(stderr, "-");
3210    --indent;
3211  }
3212}
3213
3214static void printLocation(CXSourceLocation L) {
3215  CXFile File;
3216  CXString FileName;
3217  unsigned line, column, offset;
3218
3219  clang_getExpansionLocation(L, &File, &line, &column, &offset);
3220  FileName = clang_getFileName(File);
3221
3222  fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column);
3223  clang_disposeString(FileName);
3224}
3225
3226static void printRanges(CXDiagnostic D, unsigned indent) {
3227  unsigned i, n = clang_getDiagnosticNumRanges(D);
3228
3229  for (i = 0; i < n; ++i) {
3230    CXSourceLocation Start, End;
3231    CXSourceRange SR = clang_getDiagnosticRange(D, i);
3232    Start = clang_getRangeStart(SR);
3233    End = clang_getRangeEnd(SR);
3234
3235    printIndent(indent);
3236    fprintf(stderr, "Range: ");
3237    printLocation(Start);
3238    fprintf(stderr, " ");
3239    printLocation(End);
3240    fprintf(stderr, "\n");
3241  }
3242}
3243
3244static void printFixIts(CXDiagnostic D, unsigned indent) {
3245  unsigned i, n = clang_getDiagnosticNumFixIts(D);
3246  fprintf(stderr, "Number FIXITs = %d\n", n);
3247  for (i = 0 ; i < n; ++i) {
3248    CXSourceRange ReplacementRange;
3249    CXString text;
3250    text = clang_getDiagnosticFixIt(D, i, &ReplacementRange);
3251
3252    printIndent(indent);
3253    fprintf(stderr, "FIXIT: (");
3254    printLocation(clang_getRangeStart(ReplacementRange));
3255    fprintf(stderr, " - ");
3256    printLocation(clang_getRangeEnd(ReplacementRange));
3257    fprintf(stderr, "): \"%s\"\n", clang_getCString(text));
3258    clang_disposeString(text);
3259  }
3260}
3261
3262static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) {
3263  unsigned i, n;
3264
3265  if (!Diags)
3266    return;
3267
3268  n = clang_getNumDiagnosticsInSet(Diags);
3269  for (i = 0; i < n; ++i) {
3270    CXSourceLocation DiagLoc;
3271    CXDiagnostic D;
3272    CXFile File;
3273    CXString FileName, DiagSpelling, DiagOption, DiagCat;
3274    unsigned line, column, offset;
3275    const char *DiagOptionStr = 0, *DiagCatStr = 0;
3276
3277    D = clang_getDiagnosticInSet(Diags, i);
3278    DiagLoc = clang_getDiagnosticLocation(D);
3279    clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset);
3280    FileName = clang_getFileName(File);
3281    DiagSpelling = clang_getDiagnosticSpelling(D);
3282
3283    printIndent(indent);
3284
3285    fprintf(stderr, "%s:%d:%d: %s: %s",
3286            clang_getCString(FileName),
3287            line,
3288            column,
3289            getSeverityString(clang_getDiagnosticSeverity(D)),
3290            clang_getCString(DiagSpelling));
3291
3292    DiagOption = clang_getDiagnosticOption(D, 0);
3293    DiagOptionStr = clang_getCString(DiagOption);
3294    if (DiagOptionStr) {
3295      fprintf(stderr, " [%s]", DiagOptionStr);
3296    }
3297
3298    DiagCat = clang_getDiagnosticCategoryText(D);
3299    DiagCatStr = clang_getCString(DiagCat);
3300    if (DiagCatStr) {
3301      fprintf(stderr, " [%s]", DiagCatStr);
3302    }
3303
3304    fprintf(stderr, "\n");
3305
3306    printRanges(D, indent);
3307    printFixIts(D, indent);
3308
3309    /* Print subdiagnostics. */
3310    printDiagnosticSet(clang_getChildDiagnostics(D), indent+2);
3311
3312    clang_disposeString(FileName);
3313    clang_disposeString(DiagSpelling);
3314    clang_disposeString(DiagOption);
3315  }
3316}
3317
3318static int read_diagnostics(const char *filename) {
3319  enum CXLoadDiag_Error error;
3320  CXString errorString;
3321  CXDiagnosticSet Diags = 0;
3322
3323  Diags = clang_loadDiagnostics(filename, &error, &errorString);
3324  if (!Diags) {
3325    fprintf(stderr, "Trouble deserializing file (%s): %s\n",
3326            getDiagnosticCodeStr(error),
3327            clang_getCString(errorString));
3328    clang_disposeString(errorString);
3329    return 1;
3330  }
3331
3332  printDiagnosticSet(Diags, 0);
3333  fprintf(stderr, "Number of diagnostics: %d\n",
3334          clang_getNumDiagnosticsInSet(Diags));
3335  clang_disposeDiagnosticSet(Diags);
3336  return 0;
3337}
3338
3339/******************************************************************************/
3340/* Command line processing.                                                   */
3341/******************************************************************************/
3342
3343static CXCursorVisitor GetVisitor(const char *s) {
3344  if (s[0] == '\0')
3345    return FilteredPrintingVisitor;
3346  if (strcmp(s, "-usrs") == 0)
3347    return USRVisitor;
3348  if (strncmp(s, "-memory-usage", 13) == 0)
3349    return GetVisitor(s + 13);
3350  return NULL;
3351}
3352
3353static void print_usage(void) {
3354  fprintf(stderr,
3355    "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n"
3356    "       c-index-test -code-completion-timing=<site> <compiler arguments>\n"
3357    "       c-index-test -cursor-at=<site> <compiler arguments>\n"
3358    "       c-index-test -file-refs-at=<site> <compiler arguments>\n");
3359  fprintf(stderr,
3360    "       c-index-test -index-file [-check-prefix=<FileCheck prefix>] <compiler arguments>\n"
3361    "       c-index-test -index-file-full [-check-prefix=<FileCheck prefix>] <compiler arguments>\n"
3362    "       c-index-test -index-tu [-check-prefix=<FileCheck prefix>] <AST file>\n"
3363    "       c-index-test -test-file-scan <AST file> <source file> "
3364          "[FileCheck prefix]\n");
3365  fprintf(stderr,
3366    "       c-index-test -test-load-tu <AST file> <symbol filter> "
3367          "[FileCheck prefix]\n"
3368    "       c-index-test -test-load-tu-usrs <AST file> <symbol filter> "
3369           "[FileCheck prefix]\n"
3370    "       c-index-test -test-load-source <symbol filter> {<args>}*\n");
3371  fprintf(stderr,
3372    "       c-index-test -test-load-source-memory-usage "
3373    "<symbol filter> {<args>}*\n"
3374    "       c-index-test -test-load-source-reparse <trials> <symbol filter> "
3375    "          {<args>}*\n"
3376    "       c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n"
3377    "       c-index-test -test-load-source-usrs-memory-usage "
3378          "<symbol filter> {<args>}*\n"
3379    "       c-index-test -test-annotate-tokens=<range> {<args>}*\n"
3380    "       c-index-test -test-inclusion-stack-source {<args>}*\n"
3381    "       c-index-test -test-inclusion-stack-tu <AST file>\n");
3382  fprintf(stderr,
3383    "       c-index-test -test-print-linkage-source {<args>}*\n"
3384    "       c-index-test -test-print-typekind {<args>}*\n"
3385    "       c-index-test -print-usr [<CursorKind> {<args>}]*\n"
3386    "       c-index-test -print-usr-file <file>\n"
3387    "       c-index-test -write-pch <file> <compiler arguments>\n");
3388  fprintf(stderr,
3389    "       c-index-test -compilation-db [lookup <filename>] database\n");
3390  fprintf(stderr,
3391    "       c-index-test -read-diagnostics <file>\n\n");
3392  fprintf(stderr,
3393    " <symbol filter> values:\n%s",
3394    "   all - load all symbols, including those from PCH\n"
3395    "   local - load all symbols except those in PCH\n"
3396    "   category - only load ObjC categories (non-PCH)\n"
3397    "   interface - only load ObjC interfaces (non-PCH)\n"
3398    "   protocol - only load ObjC protocols (non-PCH)\n"
3399    "   function - only load functions (non-PCH)\n"
3400    "   typedef - only load typdefs (non-PCH)\n"
3401    "   scan-function - scan function bodies (non-PCH)\n\n");
3402}
3403
3404/***/
3405
3406int cindextest_main(int argc, const char **argv) {
3407  clang_enableStackTraces();
3408  if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0)
3409      return read_diagnostics(argv[2]);
3410  if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1])
3411    return perform_code_completion(argc, argv, 0);
3412  if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1])
3413    return perform_code_completion(argc, argv, 1);
3414  if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1])
3415    return inspect_cursor_at(argc, argv);
3416  if (argc > 2 && strstr(argv[1], "-file-refs-at=") == argv[1])
3417    return find_file_refs_at(argc, argv);
3418  if (argc > 2 && strcmp(argv[1], "-index-file") == 0)
3419    return index_file(argc - 2, argv + 2, /*full=*/0);
3420  if (argc > 2 && strcmp(argv[1], "-index-file-full") == 0)
3421    return index_file(argc - 2, argv + 2, /*full=*/1);
3422  if (argc > 2 && strcmp(argv[1], "-index-tu") == 0)
3423    return index_tu(argc - 2, argv + 2);
3424  else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) {
3425    CXCursorVisitor I = GetVisitor(argv[1] + 13);
3426    if (I)
3427      return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I,
3428                                  NULL);
3429  }
3430  else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){
3431    CXCursorVisitor I = GetVisitor(argv[1] + 25);
3432    if (I) {
3433      int trials = atoi(argv[2]);
3434      return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I,
3435                                         NULL);
3436    }
3437  }
3438  else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) {
3439    CXCursorVisitor I = GetVisitor(argv[1] + 17);
3440
3441    PostVisitTU postVisit = 0;
3442    if (strstr(argv[1], "-memory-usage"))
3443      postVisit = PrintMemoryUsage;
3444
3445    if (I)
3446      return perform_test_load_source(argc - 3, argv + 3, argv[2], I,
3447                                      postVisit);
3448  }
3449  else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
3450    return perform_file_scan(argv[2], argv[3],
3451                             argc >= 5 ? argv[4] : 0);
3452  else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1])
3453    return perform_token_annotation(argc, argv);
3454  else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0)
3455    return perform_test_load_source(argc - 2, argv + 2, "all", NULL,
3456                                    PrintInclusionStack);
3457  else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0)
3458    return perform_test_load_tu(argv[2], "all", NULL, NULL,
3459                                PrintInclusionStack);
3460  else if (argc > 2 && strcmp(argv[1], "-test-print-linkage-source") == 0)
3461    return perform_test_load_source(argc - 2, argv + 2, "all", PrintLinkage,
3462                                    NULL);
3463  else if (argc > 2 && strcmp(argv[1], "-test-print-typekind") == 0)
3464    return perform_test_load_source(argc - 2, argv + 2, "all",
3465                                    PrintTypeKind, 0);
3466  else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) {
3467    if (argc > 2)
3468      return print_usrs(argv + 2, argv + argc);
3469    else {
3470      display_usrs();
3471      return 1;
3472    }
3473  }
3474  else if (argc > 2 && strcmp(argv[1], "-print-usr-file") == 0)
3475    return print_usrs_file(argv[2]);
3476  else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0)
3477    return write_pch_file(argv[2], argc - 3, argv + 3);
3478  else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0)
3479    return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2);
3480
3481  print_usage();
3482  return 1;
3483}
3484
3485/***/
3486
3487/* We intentionally run in a separate thread to ensure we at least minimal
3488 * testing of a multithreaded environment (for example, having a reduced stack
3489 * size). */
3490
3491typedef struct thread_info {
3492  int argc;
3493  const char **argv;
3494  int result;
3495} thread_info;
3496void thread_runner(void *client_data_v) {
3497  thread_info *client_data = client_data_v;
3498  client_data->result = cindextest_main(client_data->argc, client_data->argv);
3499#ifdef __CYGWIN__
3500  fflush(stdout);  /* stdout is not flushed on Cygwin. */
3501#endif
3502}
3503
3504int main(int argc, const char **argv) {
3505  thread_info client_data;
3506
3507#ifdef CLANG_HAVE_LIBXML
3508  LIBXML_TEST_VERSION
3509#endif
3510
3511  if (getenv("CINDEXTEST_NOTHREADS"))
3512    return cindextest_main(argc, argv);
3513
3514  client_data.argc = argc;
3515  client_data.argv = argv;
3516  clang_executeOnThread(thread_runner, &client_data, 0);
3517  return client_data.result;
3518}
3519