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