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