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