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