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