c-index-test.c revision 6ec43adc39006a7fce94188956d0239bd54c0363
1/* c-index-test.c */ 2 3#include "clang-c/Index.h" 4#include <ctype.h> 5#include <stdlib.h> 6#include <stdio.h> 7#include <string.h> 8#include <assert.h> 9 10/******************************************************************************/ 11/* Utility functions. */ 12/******************************************************************************/ 13 14#ifdef _MSC_VER 15char *basename(const char* path) 16{ 17 char* base1 = (char*)strrchr(path, '/'); 18 char* base2 = (char*)strrchr(path, '\\'); 19 if (base1 && base2) 20 return((base1 > base2) ? base1 + 1 : base2 + 1); 21 else if (base1) 22 return(base1 + 1); 23 else if (base2) 24 return(base2 + 1); 25 26 return((char*)path); 27} 28#else 29extern char *basename(const char *); 30#endif 31 32/** \brief Return the default parsing options. */ 33static unsigned getDefaultParsingOptions() { 34 unsigned options = CXTranslationUnit_DetailedPreprocessingRecord; 35 36 if (getenv("CINDEXTEST_EDITING")) 37 options |= clang_defaultEditingTranslationUnitOptions(); 38 if (getenv("CINDEXTEST_COMPLETION_CACHING")) 39 options |= CXTranslationUnit_CacheCompletionResults; 40 if (getenv("CINDEXTEST_NESTED_MACROS")) 41 options |= CXTranslationUnit_NestedMacroExpansions; 42 if (getenv("CINDEXTEST_COMPLETION_NO_CACHING")) 43 options &= ~CXTranslationUnit_CacheCompletionResults; 44 45 return options; 46} 47 48static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column, 49 unsigned end_line, unsigned end_column) { 50 fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column, 51 end_line, end_column); 52} 53 54static unsigned CreateTranslationUnit(CXIndex Idx, const char *file, 55 CXTranslationUnit *TU) { 56 57 *TU = clang_createTranslationUnit(Idx, file); 58 if (!*TU) { 59 fprintf(stderr, "Unable to load translation unit from '%s'!\n", file); 60 return 0; 61 } 62 return 1; 63} 64 65void free_remapped_files(struct CXUnsavedFile *unsaved_files, 66 int num_unsaved_files) { 67 int i; 68 for (i = 0; i != num_unsaved_files; ++i) { 69 free((char *)unsaved_files[i].Filename); 70 free((char *)unsaved_files[i].Contents); 71 } 72 free(unsaved_files); 73} 74 75int parse_remapped_files(int argc, const char **argv, int start_arg, 76 struct CXUnsavedFile **unsaved_files, 77 int *num_unsaved_files) { 78 int i; 79 int arg; 80 int prefix_len = strlen("-remap-file="); 81 *unsaved_files = 0; 82 *num_unsaved_files = 0; 83 84 /* Count the number of remapped files. */ 85 for (arg = start_arg; arg < argc; ++arg) { 86 if (strncmp(argv[arg], "-remap-file=", prefix_len)) 87 break; 88 89 ++*num_unsaved_files; 90 } 91 92 if (*num_unsaved_files == 0) 93 return 0; 94 95 *unsaved_files 96 = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) * 97 *num_unsaved_files); 98 for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) { 99 struct CXUnsavedFile *unsaved = *unsaved_files + i; 100 const char *arg_string = argv[arg] + prefix_len; 101 int filename_len; 102 char *filename; 103 char *contents; 104 FILE *to_file; 105 const char *semi = strchr(arg_string, ';'); 106 if (!semi) { 107 fprintf(stderr, 108 "error: -remap-file=from;to argument is missing semicolon\n"); 109 free_remapped_files(*unsaved_files, i); 110 *unsaved_files = 0; 111 *num_unsaved_files = 0; 112 return -1; 113 } 114 115 /* Open the file that we're remapping to. */ 116 to_file = fopen(semi + 1, "rb"); 117 if (!to_file) { 118 fprintf(stderr, "error: cannot open file %s that we are remapping to\n", 119 semi + 1); 120 free_remapped_files(*unsaved_files, i); 121 *unsaved_files = 0; 122 *num_unsaved_files = 0; 123 return -1; 124 } 125 126 /* Determine the length of the file we're remapping to. */ 127 fseek(to_file, 0, SEEK_END); 128 unsaved->Length = ftell(to_file); 129 fseek(to_file, 0, SEEK_SET); 130 131 /* Read the contents of the file we're remapping to. */ 132 contents = (char *)malloc(unsaved->Length + 1); 133 if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) { 134 fprintf(stderr, "error: unexpected %s reading 'to' file %s\n", 135 (feof(to_file) ? "EOF" : "error"), semi + 1); 136 fclose(to_file); 137 free_remapped_files(*unsaved_files, i); 138 *unsaved_files = 0; 139 *num_unsaved_files = 0; 140 return -1; 141 } 142 contents[unsaved->Length] = 0; 143 unsaved->Contents = contents; 144 145 /* Close the file. */ 146 fclose(to_file); 147 148 /* Copy the file name that we're remapping from. */ 149 filename_len = semi - arg_string; 150 filename = (char *)malloc(filename_len + 1); 151 memcpy(filename, arg_string, filename_len); 152 filename[filename_len] = 0; 153 unsaved->Filename = filename; 154 } 155 156 return 0; 157} 158 159/******************************************************************************/ 160/* Pretty-printing. */ 161/******************************************************************************/ 162 163static void PrintRange(CXSourceRange R, const char *str) { 164 CXFile begin_file, end_file; 165 unsigned begin_line, begin_column, end_line, end_column; 166 167 clang_getSpellingLocation(clang_getRangeStart(R), 168 &begin_file, &begin_line, &begin_column, 0); 169 clang_getSpellingLocation(clang_getRangeEnd(R), 170 &end_file, &end_line, &end_column, 0); 171 if (!begin_file || !end_file) 172 return; 173 174 printf(" %s=", str); 175 PrintExtent(stdout, begin_line, begin_column, end_line, end_column); 176} 177 178int want_display_name = 0; 179 180static void PrintCursor(CXCursor Cursor) { 181 CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); 182 if (clang_isInvalid(Cursor.kind)) { 183 CXString ks = clang_getCursorKindSpelling(Cursor.kind); 184 printf("Invalid Cursor => %s", clang_getCString(ks)); 185 clang_disposeString(ks); 186 } 187 else { 188 CXString string, ks; 189 CXCursor Referenced; 190 unsigned line, column; 191 CXCursor SpecializationOf; 192 CXCursor *overridden; 193 unsigned num_overridden; 194 unsigned RefNameRangeNr; 195 CXSourceRange CursorExtent; 196 CXSourceRange RefNameRange; 197 198 ks = clang_getCursorKindSpelling(Cursor.kind); 199 string = want_display_name? clang_getCursorDisplayName(Cursor) 200 : clang_getCursorSpelling(Cursor); 201 printf("%s=%s", clang_getCString(ks), 202 clang_getCString(string)); 203 clang_disposeString(ks); 204 clang_disposeString(string); 205 206 Referenced = clang_getCursorReferenced(Cursor); 207 if (!clang_equalCursors(Referenced, clang_getNullCursor())) { 208 if (clang_getCursorKind(Referenced) == CXCursor_OverloadedDeclRef) { 209 unsigned I, N = clang_getNumOverloadedDecls(Referenced); 210 printf("["); 211 for (I = 0; I != N; ++I) { 212 CXCursor Ovl = clang_getOverloadedDecl(Referenced, I); 213 CXSourceLocation Loc; 214 if (I) 215 printf(", "); 216 217 Loc = clang_getCursorLocation(Ovl); 218 clang_getSpellingLocation(Loc, 0, &line, &column, 0); 219 printf("%d:%d", line, column); 220 } 221 printf("]"); 222 } else { 223 CXSourceLocation Loc = clang_getCursorLocation(Referenced); 224 clang_getSpellingLocation(Loc, 0, &line, &column, 0); 225 printf(":%d:%d", line, column); 226 } 227 } 228 229 if (clang_isCursorDefinition(Cursor)) 230 printf(" (Definition)"); 231 232 switch (clang_getCursorAvailability(Cursor)) { 233 case CXAvailability_Available: 234 break; 235 236 case CXAvailability_Deprecated: 237 printf(" (deprecated)"); 238 break; 239 240 case CXAvailability_NotAvailable: 241 printf(" (unavailable)"); 242 break; 243 244 case CXAvailability_NotAccessible: 245 printf(" (inaccessible)"); 246 break; 247 } 248 249 if (clang_CXXMethod_isStatic(Cursor)) 250 printf(" (static)"); 251 if (clang_CXXMethod_isVirtual(Cursor)) 252 printf(" (virtual)"); 253 254 if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { 255 CXType T = 256 clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor)); 257 CXString S = clang_getTypeKindSpelling(T.kind); 258 printf(" [IBOutletCollection=%s]", clang_getCString(S)); 259 clang_disposeString(S); 260 } 261 262 if (Cursor.kind == CXCursor_CXXBaseSpecifier) { 263 enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); 264 unsigned isVirtual = clang_isVirtualBase(Cursor); 265 const char *accessStr = 0; 266 267 switch (access) { 268 case CX_CXXInvalidAccessSpecifier: 269 accessStr = "invalid"; break; 270 case CX_CXXPublic: 271 accessStr = "public"; break; 272 case CX_CXXProtected: 273 accessStr = "protected"; break; 274 case CX_CXXPrivate: 275 accessStr = "private"; break; 276 } 277 278 printf(" [access=%s isVirtual=%s]", accessStr, 279 isVirtual ? "true" : "false"); 280 } 281 282 SpecializationOf = clang_getSpecializedCursorTemplate(Cursor); 283 if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) { 284 CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf); 285 CXString Name = clang_getCursorSpelling(SpecializationOf); 286 clang_getSpellingLocation(Loc, 0, &line, &column, 0); 287 printf(" [Specialization of %s:%d:%d]", 288 clang_getCString(Name), line, column); 289 clang_disposeString(Name); 290 } 291 292 clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); 293 if (num_overridden) { 294 unsigned I; 295 printf(" [Overrides "); 296 for (I = 0; I != num_overridden; ++I) { 297 CXSourceLocation Loc = clang_getCursorLocation(overridden[I]); 298 clang_getSpellingLocation(Loc, 0, &line, &column, 0); 299 if (I) 300 printf(", "); 301 printf("@%d:%d", line, column); 302 } 303 printf("]"); 304 clang_disposeOverriddenCursors(overridden); 305 } 306 307 if (Cursor.kind == CXCursor_InclusionDirective) { 308 CXFile File = clang_getIncludedFile(Cursor); 309 CXString Included = clang_getFileName(File); 310 printf(" (%s)", clang_getCString(Included)); 311 clang_disposeString(Included); 312 313 if (clang_isFileMultipleIncludeGuarded(TU, File)) 314 printf(" [multi-include guarded]"); 315 } 316 317 CursorExtent = clang_getCursorExtent(Cursor); 318 RefNameRange = clang_getCursorReferenceNameRange(Cursor, 319 CXNameRange_WantQualifier 320 | CXNameRange_WantSinglePiece 321 | CXNameRange_WantTemplateArgs, 322 0); 323 if (!clang_equalRanges(CursorExtent, RefNameRange)) 324 PrintRange(RefNameRange, "SingleRefName"); 325 326 for (RefNameRangeNr = 0; 1; RefNameRangeNr++) { 327 RefNameRange = clang_getCursorReferenceNameRange(Cursor, 328 CXNameRange_WantQualifier 329 | CXNameRange_WantTemplateArgs, 330 RefNameRangeNr); 331 if (clang_equalRanges(clang_getNullRange(), RefNameRange)) 332 break; 333 if (!clang_equalRanges(CursorExtent, RefNameRange)) 334 PrintRange(RefNameRange, "RefName"); 335 } 336 } 337} 338 339static const char* GetCursorSource(CXCursor Cursor) { 340 CXSourceLocation Loc = clang_getCursorLocation(Cursor); 341 CXString source; 342 CXFile file; 343 clang_getExpansionLocation(Loc, &file, 0, 0, 0); 344 source = clang_getFileName(file); 345 if (!clang_getCString(source)) { 346 clang_disposeString(source); 347 return "<invalid loc>"; 348 } 349 else { 350 const char *b = basename(clang_getCString(source)); 351 clang_disposeString(source); 352 return b; 353 } 354} 355 356/******************************************************************************/ 357/* Callbacks. */ 358/******************************************************************************/ 359 360typedef void (*PostVisitTU)(CXTranslationUnit); 361 362void PrintDiagnostic(CXDiagnostic Diagnostic) { 363 FILE *out = stderr; 364 CXFile file; 365 CXString Msg; 366 unsigned display_opts = CXDiagnostic_DisplaySourceLocation 367 | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges 368 | CXDiagnostic_DisplayOption; 369 unsigned i, num_fixits; 370 371 if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored) 372 return; 373 374 Msg = clang_formatDiagnostic(Diagnostic, display_opts); 375 fprintf(stderr, "%s\n", clang_getCString(Msg)); 376 clang_disposeString(Msg); 377 378 clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), 379 &file, 0, 0, 0); 380 if (!file) 381 return; 382 383 num_fixits = clang_getDiagnosticNumFixIts(Diagnostic); 384 for (i = 0; i != num_fixits; ++i) { 385 CXSourceRange range; 386 CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range); 387 CXSourceLocation start = clang_getRangeStart(range); 388 CXSourceLocation end = clang_getRangeEnd(range); 389 unsigned start_line, start_column, end_line, end_column; 390 CXFile start_file, end_file; 391 clang_getSpellingLocation(start, &start_file, &start_line, 392 &start_column, 0); 393 clang_getSpellingLocation(end, &end_file, &end_line, &end_column, 0); 394 if (clang_equalLocations(start, end)) { 395 /* Insertion. */ 396 if (start_file == file) 397 fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n", 398 clang_getCString(insertion_text), start_line, start_column); 399 } else if (strcmp(clang_getCString(insertion_text), "") == 0) { 400 /* Removal. */ 401 if (start_file == file && end_file == file) { 402 fprintf(out, "FIX-IT: Remove "); 403 PrintExtent(out, start_line, start_column, end_line, end_column); 404 fprintf(out, "\n"); 405 } 406 } else { 407 /* Replacement. */ 408 if (start_file == end_file) { 409 fprintf(out, "FIX-IT: Replace "); 410 PrintExtent(out, start_line, start_column, end_line, end_column); 411 fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text)); 412 } 413 break; 414 } 415 clang_disposeString(insertion_text); 416 } 417} 418 419void PrintDiagnostics(CXTranslationUnit TU) { 420 int i, n = clang_getNumDiagnostics(TU); 421 for (i = 0; i != n; ++i) { 422 CXDiagnostic Diag = clang_getDiagnostic(TU, i); 423 PrintDiagnostic(Diag); 424 clang_disposeDiagnostic(Diag); 425 } 426} 427 428void PrintMemoryUsage(CXTranslationUnit TU) { 429 unsigned long total = 0; 430 unsigned i = 0; 431 CXTUResourceUsage usage = clang_getCXTUResourceUsage(TU); 432 fprintf(stderr, "Memory usage:\n"); 433 for (i = 0 ; i != usage.numEntries; ++i) { 434 const char *name = clang_getTUResourceUsageName(usage.entries[i].kind); 435 unsigned long amount = usage.entries[i].amount; 436 total += amount; 437 fprintf(stderr, " %s : %ld bytes (%f MBytes)\n", name, amount, 438 ((double) amount)/(1024*1024)); 439 } 440 fprintf(stderr, " TOTAL = %ld bytes (%f MBytes)\n", total, 441 ((double) total)/(1024*1024)); 442 clang_disposeCXTUResourceUsage(usage); 443} 444 445/******************************************************************************/ 446/* Logic for testing traversal. */ 447/******************************************************************************/ 448 449static const char *FileCheckPrefix = "CHECK"; 450 451static void PrintCursorExtent(CXCursor C) { 452 CXSourceRange extent = clang_getCursorExtent(C); 453 PrintRange(extent, "Extent"); 454} 455 456/* Data used by all of the visitors. */ 457typedef struct { 458 CXTranslationUnit TU; 459 enum CXCursorKind *Filter; 460} VisitorData; 461 462 463enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, 464 CXCursor Parent, 465 CXClientData ClientData) { 466 VisitorData *Data = (VisitorData *)ClientData; 467 if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) { 468 CXSourceLocation Loc = clang_getCursorLocation(Cursor); 469 unsigned line, column; 470 clang_getSpellingLocation(Loc, 0, &line, &column, 0); 471 printf("// %s: %s:%d:%d: ", FileCheckPrefix, 472 GetCursorSource(Cursor), line, column); 473 PrintCursor(Cursor); 474 PrintCursorExtent(Cursor); 475 printf("\n"); 476 return CXChildVisit_Recurse; 477 } 478 479 return CXChildVisit_Continue; 480} 481 482static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor, 483 CXCursor Parent, 484 CXClientData ClientData) { 485 const char *startBuf, *endBuf; 486 unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn; 487 CXCursor Ref; 488 VisitorData *Data = (VisitorData *)ClientData; 489 490 if (Cursor.kind != CXCursor_FunctionDecl || 491 !clang_isCursorDefinition(Cursor)) 492 return CXChildVisit_Continue; 493 494 clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, 495 &startLine, &startColumn, 496 &endLine, &endColumn); 497 /* Probe the entire body, looking for both decls and refs. */ 498 curLine = startLine; 499 curColumn = startColumn; 500 501 while (startBuf < endBuf) { 502 CXSourceLocation Loc; 503 CXFile file; 504 CXString source; 505 506 if (*startBuf == '\n') { 507 startBuf++; 508 curLine++; 509 curColumn = 1; 510 } else if (*startBuf != '\t') 511 curColumn++; 512 513 Loc = clang_getCursorLocation(Cursor); 514 clang_getSpellingLocation(Loc, &file, 0, 0, 0); 515 516 source = clang_getFileName(file); 517 if (clang_getCString(source)) { 518 CXSourceLocation RefLoc 519 = clang_getLocation(Data->TU, file, curLine, curColumn); 520 Ref = clang_getCursor(Data->TU, RefLoc); 521 if (Ref.kind == CXCursor_NoDeclFound) { 522 /* Nothing found here; that's fine. */ 523 } else if (Ref.kind != CXCursor_FunctionDecl) { 524 printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), 525 curLine, curColumn); 526 PrintCursor(Ref); 527 printf("\n"); 528 } 529 } 530 clang_disposeString(source); 531 startBuf++; 532 } 533 534 return CXChildVisit_Continue; 535} 536 537/******************************************************************************/ 538/* USR testing. */ 539/******************************************************************************/ 540 541enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, 542 CXClientData ClientData) { 543 VisitorData *Data = (VisitorData *)ClientData; 544 if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { 545 CXString USR = clang_getCursorUSR(C); 546 const char *cstr = clang_getCString(USR); 547 if (!cstr || cstr[0] == '\0') { 548 clang_disposeString(USR); 549 return CXChildVisit_Recurse; 550 } 551 printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr); 552 553 PrintCursorExtent(C); 554 printf("\n"); 555 clang_disposeString(USR); 556 557 return CXChildVisit_Recurse; 558 } 559 560 return CXChildVisit_Continue; 561} 562 563/******************************************************************************/ 564/* Inclusion stack testing. */ 565/******************************************************************************/ 566 567void InclusionVisitor(CXFile includedFile, CXSourceLocation *includeStack, 568 unsigned includeStackLen, CXClientData data) { 569 570 unsigned i; 571 CXString fname; 572 573 fname = clang_getFileName(includedFile); 574 printf("file: %s\nincluded by:\n", clang_getCString(fname)); 575 clang_disposeString(fname); 576 577 for (i = 0; i < includeStackLen; ++i) { 578 CXFile includingFile; 579 unsigned line, column; 580 clang_getSpellingLocation(includeStack[i], &includingFile, &line, 581 &column, 0); 582 fname = clang_getFileName(includingFile); 583 printf(" %s:%d:%d\n", clang_getCString(fname), line, column); 584 clang_disposeString(fname); 585 } 586 printf("\n"); 587} 588 589void PrintInclusionStack(CXTranslationUnit TU) { 590 clang_getInclusions(TU, InclusionVisitor, NULL); 591} 592 593/******************************************************************************/ 594/* Linkage testing. */ 595/******************************************************************************/ 596 597static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, 598 CXClientData d) { 599 const char *linkage = 0; 600 601 if (clang_isInvalid(clang_getCursorKind(cursor))) 602 return CXChildVisit_Recurse; 603 604 switch (clang_getCursorLinkage(cursor)) { 605 case CXLinkage_Invalid: break; 606 case CXLinkage_NoLinkage: linkage = "NoLinkage"; break; 607 case CXLinkage_Internal: linkage = "Internal"; break; 608 case CXLinkage_UniqueExternal: linkage = "UniqueExternal"; break; 609 case CXLinkage_External: linkage = "External"; break; 610 } 611 612 if (linkage) { 613 PrintCursor(cursor); 614 printf("linkage=%s\n", linkage); 615 } 616 617 return CXChildVisit_Recurse; 618} 619 620/******************************************************************************/ 621/* Typekind testing. */ 622/******************************************************************************/ 623 624static enum CXChildVisitResult PrintTypeKind(CXCursor cursor, CXCursor p, 625 CXClientData d) { 626 if (!clang_isInvalid(clang_getCursorKind(cursor))) { 627 CXType T = clang_getCursorType(cursor); 628 CXString S = clang_getTypeKindSpelling(T.kind); 629 PrintCursor(cursor); 630 printf(" typekind=%s", clang_getCString(S)); 631 if (clang_isConstQualifiedType(T)) 632 printf(" const"); 633 if (clang_isVolatileQualifiedType(T)) 634 printf(" volatile"); 635 if (clang_isRestrictQualifiedType(T)) 636 printf(" restrict"); 637 clang_disposeString(S); 638 /* Print the canonical type if it is different. */ 639 { 640 CXType CT = clang_getCanonicalType(T); 641 if (!clang_equalTypes(T, CT)) { 642 CXString CS = clang_getTypeKindSpelling(CT.kind); 643 printf(" [canonical=%s]", clang_getCString(CS)); 644 clang_disposeString(CS); 645 } 646 } 647 /* Print the return type if it exists. */ 648 { 649 CXType RT = clang_getCursorResultType(cursor); 650 if (RT.kind != CXType_Invalid) { 651 CXString RS = clang_getTypeKindSpelling(RT.kind); 652 printf(" [result=%s]", clang_getCString(RS)); 653 clang_disposeString(RS); 654 } 655 } 656 /* Print if this is a non-POD type. */ 657 printf(" [isPOD=%d]", clang_isPODType(T)); 658 659 printf("\n"); 660 } 661 return CXChildVisit_Recurse; 662} 663 664 665/******************************************************************************/ 666/* Loading ASTs/source. */ 667/******************************************************************************/ 668 669static int perform_test_load(CXIndex Idx, CXTranslationUnit TU, 670 const char *filter, const char *prefix, 671 CXCursorVisitor Visitor, 672 PostVisitTU PV) { 673 674 if (prefix) 675 FileCheckPrefix = prefix; 676 677 if (Visitor) { 678 enum CXCursorKind K = CXCursor_NotImplemented; 679 enum CXCursorKind *ck = &K; 680 VisitorData Data; 681 682 /* Perform some simple filtering. */ 683 if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; 684 else if (!strcmp(filter, "all-display") || 685 !strcmp(filter, "local-display")) { 686 ck = NULL; 687 want_display_name = 1; 688 } 689 else if (!strcmp(filter, "none")) K = (enum CXCursorKind) ~0; 690 else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl; 691 else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl; 692 else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl; 693 else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl; 694 else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl; 695 else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor; 696 else { 697 fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter); 698 return 1; 699 } 700 701 Data.TU = TU; 702 Data.Filter = ck; 703 clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data); 704 } 705 706 if (PV) 707 PV(TU); 708 709 PrintDiagnostics(TU); 710 clang_disposeTranslationUnit(TU); 711 return 0; 712} 713 714int perform_test_load_tu(const char *file, const char *filter, 715 const char *prefix, CXCursorVisitor Visitor, 716 PostVisitTU PV) { 717 CXIndex Idx; 718 CXTranslationUnit TU; 719 int result; 720 Idx = clang_createIndex(/* excludeDeclsFromPCH */ 721 !strcmp(filter, "local") ? 1 : 0, 722 /* displayDiagnosics=*/1); 723 724 if (!CreateTranslationUnit(Idx, file, &TU)) { 725 clang_disposeIndex(Idx); 726 return 1; 727 } 728 729 result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV); 730 clang_disposeIndex(Idx); 731 return result; 732} 733 734int perform_test_load_source(int argc, const char **argv, 735 const char *filter, CXCursorVisitor Visitor, 736 PostVisitTU PV) { 737 CXIndex Idx; 738 CXTranslationUnit TU; 739 struct CXUnsavedFile *unsaved_files = 0; 740 int num_unsaved_files = 0; 741 int result; 742 743 Idx = clang_createIndex(/* excludeDeclsFromPCH */ 744 (!strcmp(filter, "local") || 745 !strcmp(filter, "local-display"))? 1 : 0, 746 /* displayDiagnosics=*/0); 747 748 if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { 749 clang_disposeIndex(Idx); 750 return -1; 751 } 752 753 TU = clang_parseTranslationUnit(Idx, 0, 754 argv + num_unsaved_files, 755 argc - num_unsaved_files, 756 unsaved_files, num_unsaved_files, 757 getDefaultParsingOptions()); 758 if (!TU) { 759 fprintf(stderr, "Unable to load translation unit!\n"); 760 free_remapped_files(unsaved_files, num_unsaved_files); 761 clang_disposeIndex(Idx); 762 return 1; 763 } 764 765 result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV); 766 free_remapped_files(unsaved_files, num_unsaved_files); 767 clang_disposeIndex(Idx); 768 return result; 769} 770 771int perform_test_reparse_source(int argc, const char **argv, int trials, 772 const char *filter, CXCursorVisitor Visitor, 773 PostVisitTU PV) { 774 CXIndex Idx; 775 CXTranslationUnit TU; 776 struct CXUnsavedFile *unsaved_files = 0; 777 int num_unsaved_files = 0; 778 int result; 779 int trial; 780 int remap_after_trial = 0; 781 char *endptr = 0; 782 783 Idx = clang_createIndex(/* excludeDeclsFromPCH */ 784 !strcmp(filter, "local") ? 1 : 0, 785 /* displayDiagnosics=*/0); 786 787 if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { 788 clang_disposeIndex(Idx); 789 return -1; 790 } 791 792 /* Load the initial translation unit -- we do this without honoring remapped 793 * files, so that we have a way to test results after changing the source. */ 794 TU = clang_parseTranslationUnit(Idx, 0, 795 argv + num_unsaved_files, 796 argc - num_unsaved_files, 797 0, 0, getDefaultParsingOptions()); 798 if (!TU) { 799 fprintf(stderr, "Unable to load translation unit!\n"); 800 free_remapped_files(unsaved_files, num_unsaved_files); 801 clang_disposeIndex(Idx); 802 return 1; 803 } 804 805 if (getenv("CINDEXTEST_REMAP_AFTER_TRIAL")) { 806 remap_after_trial = 807 strtol(getenv("CINDEXTEST_REMAP_AFTER_TRIAL"), &endptr, 10); 808 } 809 810 for (trial = 0; trial < trials; ++trial) { 811 if (clang_reparseTranslationUnit(TU, 812 trial >= remap_after_trial ? num_unsaved_files : 0, 813 trial >= remap_after_trial ? unsaved_files : 0, 814 clang_defaultReparseOptions(TU))) { 815 fprintf(stderr, "Unable to reparse translation unit!\n"); 816 clang_disposeTranslationUnit(TU); 817 free_remapped_files(unsaved_files, num_unsaved_files); 818 clang_disposeIndex(Idx); 819 return -1; 820 } 821 } 822 823 result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV); 824 free_remapped_files(unsaved_files, num_unsaved_files); 825 clang_disposeIndex(Idx); 826 return result; 827} 828 829/******************************************************************************/ 830/* Logic for testing clang_getCursor(). */ 831/******************************************************************************/ 832 833static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor, 834 unsigned start_line, unsigned start_col, 835 unsigned end_line, unsigned end_col, 836 const char *prefix) { 837 printf("// %s: ", FileCheckPrefix); 838 if (prefix) 839 printf("-%s", prefix); 840 PrintExtent(stdout, start_line, start_col, end_line, end_col); 841 printf(" "); 842 PrintCursor(cursor); 843 printf("\n"); 844} 845 846static int perform_file_scan(const char *ast_file, const char *source_file, 847 const char *prefix) { 848 CXIndex Idx; 849 CXTranslationUnit TU; 850 FILE *fp; 851 CXCursor prevCursor = clang_getNullCursor(); 852 CXFile file; 853 unsigned line = 1, col = 1; 854 unsigned start_line = 1, start_col = 1; 855 856 if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, 857 /* displayDiagnosics=*/1))) { 858 fprintf(stderr, "Could not create Index\n"); 859 return 1; 860 } 861 862 if (!CreateTranslationUnit(Idx, ast_file, &TU)) 863 return 1; 864 865 if ((fp = fopen(source_file, "r")) == NULL) { 866 fprintf(stderr, "Could not open '%s'\n", source_file); 867 return 1; 868 } 869 870 file = clang_getFile(TU, source_file); 871 for (;;) { 872 CXCursor cursor; 873 int c = fgetc(fp); 874 875 if (c == '\n') { 876 ++line; 877 col = 1; 878 } else 879 ++col; 880 881 /* Check the cursor at this position, and dump the previous one if we have 882 * found something new. 883 */ 884 cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col)); 885 if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) && 886 prevCursor.kind != CXCursor_InvalidFile) { 887 print_cursor_file_scan(TU, prevCursor, start_line, start_col, 888 line, col, prefix); 889 start_line = line; 890 start_col = col; 891 } 892 if (c == EOF) 893 break; 894 895 prevCursor = cursor; 896 } 897 898 fclose(fp); 899 clang_disposeTranslationUnit(TU); 900 clang_disposeIndex(Idx); 901 return 0; 902} 903 904/******************************************************************************/ 905/* Logic for testing clang code completion. */ 906/******************************************************************************/ 907 908/* Parse file:line:column from the input string. Returns 0 on success, non-zero 909 on failure. If successful, the pointer *filename will contain newly-allocated 910 memory (that will be owned by the caller) to store the file name. */ 911int parse_file_line_column(const char *input, char **filename, unsigned *line, 912 unsigned *column, unsigned *second_line, 913 unsigned *second_column) { 914 /* Find the second colon. */ 915 const char *last_colon = strrchr(input, ':'); 916 unsigned values[4], i; 917 unsigned num_values = (second_line && second_column)? 4 : 2; 918 919 char *endptr = 0; 920 if (!last_colon || last_colon == input) { 921 if (num_values == 4) 922 fprintf(stderr, "could not parse filename:line:column:line:column in " 923 "'%s'\n", input); 924 else 925 fprintf(stderr, "could not parse filename:line:column in '%s'\n", input); 926 return 1; 927 } 928 929 for (i = 0; i != num_values; ++i) { 930 const char *prev_colon; 931 932 /* Parse the next line or column. */ 933 values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10); 934 if (*endptr != 0 && *endptr != ':') { 935 fprintf(stderr, "could not parse %s in '%s'\n", 936 (i % 2 ? "column" : "line"), input); 937 return 1; 938 } 939 940 if (i + 1 == num_values) 941 break; 942 943 /* Find the previous colon. */ 944 prev_colon = last_colon - 1; 945 while (prev_colon != input && *prev_colon != ':') 946 --prev_colon; 947 if (prev_colon == input) { 948 fprintf(stderr, "could not parse %s in '%s'\n", 949 (i % 2 == 0? "column" : "line"), input); 950 return 1; 951 } 952 953 last_colon = prev_colon; 954 } 955 956 *line = values[0]; 957 *column = values[1]; 958 959 if (second_line && second_column) { 960 *second_line = values[2]; 961 *second_column = values[3]; 962 } 963 964 /* Copy the file name. */ 965 *filename = (char*)malloc(last_colon - input + 1); 966 memcpy(*filename, input, last_colon - input); 967 (*filename)[last_colon - input] = 0; 968 return 0; 969} 970 971const char * 972clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) { 973 switch (Kind) { 974 case CXCompletionChunk_Optional: return "Optional"; 975 case CXCompletionChunk_TypedText: return "TypedText"; 976 case CXCompletionChunk_Text: return "Text"; 977 case CXCompletionChunk_Placeholder: return "Placeholder"; 978 case CXCompletionChunk_Informative: return "Informative"; 979 case CXCompletionChunk_CurrentParameter: return "CurrentParameter"; 980 case CXCompletionChunk_LeftParen: return "LeftParen"; 981 case CXCompletionChunk_RightParen: return "RightParen"; 982 case CXCompletionChunk_LeftBracket: return "LeftBracket"; 983 case CXCompletionChunk_RightBracket: return "RightBracket"; 984 case CXCompletionChunk_LeftBrace: return "LeftBrace"; 985 case CXCompletionChunk_RightBrace: return "RightBrace"; 986 case CXCompletionChunk_LeftAngle: return "LeftAngle"; 987 case CXCompletionChunk_RightAngle: return "RightAngle"; 988 case CXCompletionChunk_Comma: return "Comma"; 989 case CXCompletionChunk_ResultType: return "ResultType"; 990 case CXCompletionChunk_Colon: return "Colon"; 991 case CXCompletionChunk_SemiColon: return "SemiColon"; 992 case CXCompletionChunk_Equal: return "Equal"; 993 case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace"; 994 case CXCompletionChunk_VerticalSpace: return "VerticalSpace"; 995 } 996 997 return "Unknown"; 998} 999 1000static int checkForErrors(CXTranslationUnit TU) { 1001 unsigned Num, i; 1002 CXDiagnostic Diag; 1003 CXString DiagStr; 1004 1005 if (!getenv("CINDEXTEST_FAILONERROR")) 1006 return 0; 1007 1008 Num = clang_getNumDiagnostics(TU); 1009 for (i = 0; i != Num; ++i) { 1010 Diag = clang_getDiagnostic(TU, i); 1011 if (clang_getDiagnosticSeverity(Diag) >= CXDiagnostic_Error) { 1012 DiagStr = clang_formatDiagnostic(Diag, 1013 clang_defaultDiagnosticDisplayOptions()); 1014 fprintf(stderr, "%s\n", clang_getCString(DiagStr)); 1015 clang_disposeString(DiagStr); 1016 clang_disposeDiagnostic(Diag); 1017 return -1; 1018 } 1019 clang_disposeDiagnostic(Diag); 1020 } 1021 1022 return 0; 1023} 1024 1025void print_completion_string(CXCompletionString completion_string, FILE *file) { 1026 int I, N; 1027 1028 N = clang_getNumCompletionChunks(completion_string); 1029 for (I = 0; I != N; ++I) { 1030 CXString text; 1031 const char *cstr; 1032 enum CXCompletionChunkKind Kind 1033 = clang_getCompletionChunkKind(completion_string, I); 1034 1035 if (Kind == CXCompletionChunk_Optional) { 1036 fprintf(file, "{Optional "); 1037 print_completion_string( 1038 clang_getCompletionChunkCompletionString(completion_string, I), 1039 file); 1040 fprintf(file, "}"); 1041 continue; 1042 } 1043 1044 if (Kind == CXCompletionChunk_VerticalSpace) { 1045 fprintf(file, "{VerticalSpace }"); 1046 continue; 1047 } 1048 1049 text = clang_getCompletionChunkText(completion_string, I); 1050 cstr = clang_getCString(text); 1051 fprintf(file, "{%s %s}", 1052 clang_getCompletionChunkKindSpelling(Kind), 1053 cstr ? cstr : ""); 1054 clang_disposeString(text); 1055 } 1056 1057} 1058 1059void print_completion_result(CXCompletionResult *completion_result, 1060 CXClientData client_data) { 1061 FILE *file = (FILE *)client_data; 1062 CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind); 1063 unsigned annotationCount; 1064 1065 fprintf(file, "%s:", clang_getCString(ks)); 1066 clang_disposeString(ks); 1067 1068 print_completion_string(completion_result->CompletionString, file); 1069 fprintf(file, " (%u)", 1070 clang_getCompletionPriority(completion_result->CompletionString)); 1071 switch (clang_getCompletionAvailability(completion_result->CompletionString)){ 1072 case CXAvailability_Available: 1073 break; 1074 1075 case CXAvailability_Deprecated: 1076 fprintf(file, " (deprecated)"); 1077 break; 1078 1079 case CXAvailability_NotAvailable: 1080 fprintf(file, " (unavailable)"); 1081 break; 1082 1083 case CXAvailability_NotAccessible: 1084 fprintf(file, " (inaccessible)"); 1085 break; 1086 } 1087 1088 annotationCount = clang_getCompletionNumAnnotations( 1089 completion_result->CompletionString); 1090 if (annotationCount) { 1091 unsigned i; 1092 fprintf(file, " ("); 1093 for (i = 0; i < annotationCount; ++i) { 1094 if (i != 0) 1095 fprintf(file, ", "); 1096 fprintf(file, "\"%s\"", 1097 clang_getCString(clang_getCompletionAnnotation( 1098 completion_result->CompletionString, i))); 1099 } 1100 fprintf(file, ")"); 1101 } 1102 1103 fprintf(file, "\n"); 1104} 1105 1106void print_completion_contexts(unsigned long long contexts, FILE *file) { 1107 fprintf(file, "Completion contexts:\n"); 1108 if (contexts == CXCompletionContext_Unknown) { 1109 fprintf(file, "Unknown\n"); 1110 } 1111 if (contexts & CXCompletionContext_AnyType) { 1112 fprintf(file, "Any type\n"); 1113 } 1114 if (contexts & CXCompletionContext_AnyValue) { 1115 fprintf(file, "Any value\n"); 1116 } 1117 if (contexts & CXCompletionContext_ObjCObjectValue) { 1118 fprintf(file, "Objective-C object value\n"); 1119 } 1120 if (contexts & CXCompletionContext_ObjCSelectorValue) { 1121 fprintf(file, "Objective-C selector value\n"); 1122 } 1123 if (contexts & CXCompletionContext_CXXClassTypeValue) { 1124 fprintf(file, "C++ class type value\n"); 1125 } 1126 if (contexts & CXCompletionContext_DotMemberAccess) { 1127 fprintf(file, "Dot member access\n"); 1128 } 1129 if (contexts & CXCompletionContext_ArrowMemberAccess) { 1130 fprintf(file, "Arrow member access\n"); 1131 } 1132 if (contexts & CXCompletionContext_ObjCPropertyAccess) { 1133 fprintf(file, "Objective-C property access\n"); 1134 } 1135 if (contexts & CXCompletionContext_EnumTag) { 1136 fprintf(file, "Enum tag\n"); 1137 } 1138 if (contexts & CXCompletionContext_UnionTag) { 1139 fprintf(file, "Union tag\n"); 1140 } 1141 if (contexts & CXCompletionContext_StructTag) { 1142 fprintf(file, "Struct tag\n"); 1143 } 1144 if (contexts & CXCompletionContext_ClassTag) { 1145 fprintf(file, "Class name\n"); 1146 } 1147 if (contexts & CXCompletionContext_Namespace) { 1148 fprintf(file, "Namespace or namespace alias\n"); 1149 } 1150 if (contexts & CXCompletionContext_NestedNameSpecifier) { 1151 fprintf(file, "Nested name specifier\n"); 1152 } 1153 if (contexts & CXCompletionContext_ObjCInterface) { 1154 fprintf(file, "Objective-C interface\n"); 1155 } 1156 if (contexts & CXCompletionContext_ObjCProtocol) { 1157 fprintf(file, "Objective-C protocol\n"); 1158 } 1159 if (contexts & CXCompletionContext_ObjCCategory) { 1160 fprintf(file, "Objective-C category\n"); 1161 } 1162 if (contexts & CXCompletionContext_ObjCInstanceMessage) { 1163 fprintf(file, "Objective-C instance method\n"); 1164 } 1165 if (contexts & CXCompletionContext_ObjCClassMessage) { 1166 fprintf(file, "Objective-C class method\n"); 1167 } 1168 if (contexts & CXCompletionContext_ObjCSelectorName) { 1169 fprintf(file, "Objective-C selector name\n"); 1170 } 1171 if (contexts & CXCompletionContext_MacroName) { 1172 fprintf(file, "Macro name\n"); 1173 } 1174 if (contexts & CXCompletionContext_NaturalLanguage) { 1175 fprintf(file, "Natural language\n"); 1176 } 1177} 1178 1179int my_stricmp(const char *s1, const char *s2) { 1180 while (*s1 && *s2) { 1181 int c1 = tolower((unsigned char)*s1), c2 = tolower((unsigned char)*s2); 1182 if (c1 < c2) 1183 return -1; 1184 else if (c1 > c2) 1185 return 1; 1186 1187 ++s1; 1188 ++s2; 1189 } 1190 1191 if (*s1) 1192 return 1; 1193 else if (*s2) 1194 return -1; 1195 return 0; 1196} 1197 1198int perform_code_completion(int argc, const char **argv, int timing_only) { 1199 const char *input = argv[1]; 1200 char *filename = 0; 1201 unsigned line; 1202 unsigned column; 1203 CXIndex CIdx; 1204 int errorCode; 1205 struct CXUnsavedFile *unsaved_files = 0; 1206 int num_unsaved_files = 0; 1207 CXCodeCompleteResults *results = 0; 1208 CXTranslationUnit TU = 0; 1209 unsigned I, Repeats = 1; 1210 unsigned completionOptions = clang_defaultCodeCompleteOptions(); 1211 1212 if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS")) 1213 completionOptions |= CXCodeComplete_IncludeCodePatterns; 1214 1215 if (timing_only) 1216 input += strlen("-code-completion-timing="); 1217 else 1218 input += strlen("-code-completion-at="); 1219 1220 if ((errorCode = parse_file_line_column(input, &filename, &line, &column, 1221 0, 0))) 1222 return errorCode; 1223 1224 if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) 1225 return -1; 1226 1227 CIdx = clang_createIndex(0, 0); 1228 1229 if (getenv("CINDEXTEST_EDITING")) 1230 Repeats = 5; 1231 1232 TU = clang_parseTranslationUnit(CIdx, 0, 1233 argv + num_unsaved_files + 2, 1234 argc - num_unsaved_files - 2, 1235 0, 0, getDefaultParsingOptions()); 1236 if (!TU) { 1237 fprintf(stderr, "Unable to load translation unit!\n"); 1238 return 1; 1239 } 1240 1241 if (clang_reparseTranslationUnit(TU, 0, 0, clang_defaultReparseOptions(TU))) { 1242 fprintf(stderr, "Unable to reparse translation init!\n"); 1243 return 1; 1244 } 1245 1246 for (I = 0; I != Repeats; ++I) { 1247 results = clang_codeCompleteAt(TU, filename, line, column, 1248 unsaved_files, num_unsaved_files, 1249 completionOptions); 1250 if (!results) { 1251 fprintf(stderr, "Unable to perform code completion!\n"); 1252 return 1; 1253 } 1254 if (I != Repeats-1) 1255 clang_disposeCodeCompleteResults(results); 1256 } 1257 1258 if (results) { 1259 unsigned i, n = results->NumResults, containerIsIncomplete = 0; 1260 unsigned long long contexts; 1261 enum CXCursorKind containerKind; 1262 CXString objCSelector; 1263 const char *selectorString; 1264 if (!timing_only) { 1265 /* Sort the code-completion results based on the typed text. */ 1266 clang_sortCodeCompletionResults(results->Results, results->NumResults); 1267 1268 for (i = 0; i != n; ++i) 1269 print_completion_result(results->Results + i, stdout); 1270 } 1271 n = clang_codeCompleteGetNumDiagnostics(results); 1272 for (i = 0; i != n; ++i) { 1273 CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); 1274 PrintDiagnostic(diag); 1275 clang_disposeDiagnostic(diag); 1276 } 1277 1278 contexts = clang_codeCompleteGetContexts(results); 1279 print_completion_contexts(contexts, stdout); 1280 1281 containerKind = clang_codeCompleteGetContainerKind(results, 1282 &containerIsIncomplete); 1283 1284 if (containerKind != CXCursor_InvalidCode) { 1285 /* We have found a container */ 1286 CXString containerUSR, containerKindSpelling; 1287 containerKindSpelling = clang_getCursorKindSpelling(containerKind); 1288 printf("Container Kind: %s\n", clang_getCString(containerKindSpelling)); 1289 clang_disposeString(containerKindSpelling); 1290 1291 if (containerIsIncomplete) { 1292 printf("Container is incomplete\n"); 1293 } 1294 else { 1295 printf("Container is complete\n"); 1296 } 1297 1298 containerUSR = clang_codeCompleteGetContainerUSR(results); 1299 printf("Container USR: %s\n", clang_getCString(containerUSR)); 1300 clang_disposeString(containerUSR); 1301 } 1302 1303 objCSelector = clang_codeCompleteGetObjCSelector(results); 1304 selectorString = clang_getCString(objCSelector); 1305 if (selectorString && strlen(selectorString) > 0) { 1306 printf("Objective-C selector: %s\n", selectorString); 1307 } 1308 clang_disposeString(objCSelector); 1309 1310 clang_disposeCodeCompleteResults(results); 1311 } 1312 clang_disposeTranslationUnit(TU); 1313 clang_disposeIndex(CIdx); 1314 free(filename); 1315 1316 free_remapped_files(unsaved_files, num_unsaved_files); 1317 1318 return 0; 1319} 1320 1321typedef struct { 1322 char *filename; 1323 unsigned line; 1324 unsigned column; 1325} CursorSourceLocation; 1326 1327static int inspect_cursor_at(int argc, const char **argv) { 1328 CXIndex CIdx; 1329 int errorCode; 1330 struct CXUnsavedFile *unsaved_files = 0; 1331 int num_unsaved_files = 0; 1332 CXTranslationUnit TU; 1333 CXCursor Cursor; 1334 CursorSourceLocation *Locations = 0; 1335 unsigned NumLocations = 0, Loc; 1336 unsigned Repeats = 1; 1337 unsigned I; 1338 1339 /* Count the number of locations. */ 1340 while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1]) 1341 ++NumLocations; 1342 1343 /* Parse the locations. */ 1344 assert(NumLocations > 0 && "Unable to count locations?"); 1345 Locations = (CursorSourceLocation *)malloc( 1346 NumLocations * sizeof(CursorSourceLocation)); 1347 for (Loc = 0; Loc < NumLocations; ++Loc) { 1348 const char *input = argv[Loc + 1] + strlen("-cursor-at="); 1349 if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, 1350 &Locations[Loc].line, 1351 &Locations[Loc].column, 0, 0))) 1352 return errorCode; 1353 } 1354 1355 if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, 1356 &num_unsaved_files)) 1357 return -1; 1358 1359 if (getenv("CINDEXTEST_EDITING")) 1360 Repeats = 5; 1361 1362 /* Parse the translation unit. When we're testing clang_getCursor() after 1363 reparsing, don't remap unsaved files until the second parse. */ 1364 CIdx = clang_createIndex(1, 1); 1365 TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], 1366 argv + num_unsaved_files + 1 + NumLocations, 1367 argc - num_unsaved_files - 2 - NumLocations, 1368 unsaved_files, 1369 Repeats > 1? 0 : num_unsaved_files, 1370 getDefaultParsingOptions()); 1371 1372 if (!TU) { 1373 fprintf(stderr, "unable to parse input\n"); 1374 return -1; 1375 } 1376 1377 if (checkForErrors(TU) != 0) 1378 return -1; 1379 1380 for (I = 0; I != Repeats; ++I) { 1381 if (Repeats > 1 && 1382 clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, 1383 clang_defaultReparseOptions(TU))) { 1384 clang_disposeTranslationUnit(TU); 1385 return 1; 1386 } 1387 1388 if (checkForErrors(TU) != 0) 1389 return -1; 1390 1391 for (Loc = 0; Loc < NumLocations; ++Loc) { 1392 CXFile file = clang_getFile(TU, Locations[Loc].filename); 1393 if (!file) 1394 continue; 1395 1396 Cursor = clang_getCursor(TU, 1397 clang_getLocation(TU, file, Locations[Loc].line, 1398 Locations[Loc].column)); 1399 1400 if (checkForErrors(TU) != 0) 1401 return -1; 1402 1403 if (I + 1 == Repeats) { 1404 CXCompletionString completionString = clang_getCursorCompletionString( 1405 Cursor); 1406 PrintCursor(Cursor); 1407 if (completionString != NULL) { 1408 printf("\nCompletion string: "); 1409 print_completion_string(completionString, stdout); 1410 } 1411 printf("\n"); 1412 free(Locations[Loc].filename); 1413 } 1414 } 1415 } 1416 1417 PrintDiagnostics(TU); 1418 clang_disposeTranslationUnit(TU); 1419 clang_disposeIndex(CIdx); 1420 free(Locations); 1421 free_remapped_files(unsaved_files, num_unsaved_files); 1422 return 0; 1423} 1424 1425static enum CXVisitorResult findFileRefsVisit(void *context, 1426 CXCursor cursor, CXSourceRange range) { 1427 if (clang_Range_isNull(range)) 1428 return CXVisit_Continue; 1429 1430 PrintCursor(cursor); 1431 PrintRange(range, ""); 1432 printf("\n"); 1433 return CXVisit_Continue; 1434} 1435 1436static int find_file_refs_at(int argc, const char **argv) { 1437 CXIndex CIdx; 1438 int errorCode; 1439 struct CXUnsavedFile *unsaved_files = 0; 1440 int num_unsaved_files = 0; 1441 CXTranslationUnit TU; 1442 CXCursor Cursor; 1443 CursorSourceLocation *Locations = 0; 1444 unsigned NumLocations = 0, Loc; 1445 unsigned Repeats = 1; 1446 unsigned I; 1447 1448 /* Count the number of locations. */ 1449 while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1]) 1450 ++NumLocations; 1451 1452 /* Parse the locations. */ 1453 assert(NumLocations > 0 && "Unable to count locations?"); 1454 Locations = (CursorSourceLocation *)malloc( 1455 NumLocations * sizeof(CursorSourceLocation)); 1456 for (Loc = 0; Loc < NumLocations; ++Loc) { 1457 const char *input = argv[Loc + 1] + strlen("-file-refs-at="); 1458 if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, 1459 &Locations[Loc].line, 1460 &Locations[Loc].column, 0, 0))) 1461 return errorCode; 1462 } 1463 1464 if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, 1465 &num_unsaved_files)) 1466 return -1; 1467 1468 if (getenv("CINDEXTEST_EDITING")) 1469 Repeats = 5; 1470 1471 /* Parse the translation unit. When we're testing clang_getCursor() after 1472 reparsing, don't remap unsaved files until the second parse. */ 1473 CIdx = clang_createIndex(1, 1); 1474 TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], 1475 argv + num_unsaved_files + 1 + NumLocations, 1476 argc - num_unsaved_files - 2 - NumLocations, 1477 unsaved_files, 1478 Repeats > 1? 0 : num_unsaved_files, 1479 getDefaultParsingOptions()); 1480 1481 if (!TU) { 1482 fprintf(stderr, "unable to parse input\n"); 1483 return -1; 1484 } 1485 1486 if (checkForErrors(TU) != 0) 1487 return -1; 1488 1489 for (I = 0; I != Repeats; ++I) { 1490 if (Repeats > 1 && 1491 clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, 1492 clang_defaultReparseOptions(TU))) { 1493 clang_disposeTranslationUnit(TU); 1494 return 1; 1495 } 1496 1497 if (checkForErrors(TU) != 0) 1498 return -1; 1499 1500 for (Loc = 0; Loc < NumLocations; ++Loc) { 1501 CXFile file = clang_getFile(TU, Locations[Loc].filename); 1502 if (!file) 1503 continue; 1504 1505 Cursor = clang_getCursor(TU, 1506 clang_getLocation(TU, file, Locations[Loc].line, 1507 Locations[Loc].column)); 1508 1509 if (checkForErrors(TU) != 0) 1510 return -1; 1511 1512 if (I + 1 == Repeats) { 1513 CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit }; 1514 PrintCursor(Cursor); 1515 printf("\n"); 1516 clang_findReferencesInFile(Cursor, file, visitor); 1517 free(Locations[Loc].filename); 1518 1519 if (checkForErrors(TU) != 0) 1520 return -1; 1521 } 1522 } 1523 } 1524 1525 PrintDiagnostics(TU); 1526 clang_disposeTranslationUnit(TU); 1527 clang_disposeIndex(CIdx); 1528 free(Locations); 1529 free_remapped_files(unsaved_files, num_unsaved_files); 1530 return 0; 1531} 1532 1533typedef struct { 1534 const char *check_prefix; 1535 int first_check_printed; 1536 int fail_for_error; 1537} IndexData; 1538 1539static void printCheck(IndexData *data) { 1540 if (data->check_prefix) { 1541 if (data->first_check_printed) { 1542 printf("// %s-NEXT: ", data->check_prefix); 1543 } else { 1544 printf("// %s : ", data->check_prefix); 1545 data->first_check_printed = 1; 1546 } 1547 } 1548} 1549 1550static void printCXIndexFile(CXIdxClientFile file) { 1551 CXString filename = clang_getFileName((CXFile)file); 1552 printf("%s", clang_getCString(filename)); 1553 clang_disposeString(filename); 1554} 1555 1556static void printCXIndexLoc(CXIdxLoc loc) { 1557 CXString filename; 1558 const char *cname, *end; 1559 CXIdxClientFile file; 1560 unsigned line, column; 1561 int isHeader; 1562 1563 clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); 1564 if (line == 0) { 1565 printf("<null loc>"); 1566 return; 1567 } 1568 filename = clang_getFileName((CXFile)file); 1569 cname = clang_getCString(filename); 1570 end = cname + strlen(cname); 1571 isHeader = (end[-2] == '.' && end[-1] == 'h'); 1572 1573 if (isHeader) { 1574 printCXIndexFile(file); 1575 printf(":"); 1576 } 1577 printf("%d:%d", line, column); 1578} 1579 1580static CXIdxClientContainer makeClientContainer(const CXIdxEntityInfo *info, 1581 CXIdxLoc loc) { 1582 const char *name; 1583 char *newStr; 1584 CXIdxClientFile file; 1585 unsigned line, column; 1586 1587 name = info->name; 1588 if (!name) 1589 name = "<anon-tag>"; 1590 1591 clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); 1592 /* FIXME: free these.*/ 1593 newStr = (char *)malloc(strlen(name) + 10); 1594 sprintf(newStr, "%s:%d:%d", name, line, column); 1595 return (CXIdxClientContainer)newStr; 1596} 1597 1598static void printCXIndexContainer(CXIdxClientContainer container) { 1599 printf("[%s]", (const char *)container); 1600} 1601 1602static const char *getEntityKindString(CXIdxEntityKind kind) { 1603 switch (kind) { 1604 case CXIdxEntity_Unexposed: return "<<UNEXPOSED>>"; 1605 case CXIdxEntity_Typedef: return "typedef"; 1606 case CXIdxEntity_Function: return "function"; 1607 case CXIdxEntity_Variable: return "variable"; 1608 case CXIdxEntity_Field: return "field"; 1609 case CXIdxEntity_EnumConstant: return "enumerator"; 1610 case CXIdxEntity_ObjCClass: return "objc-class"; 1611 case CXIdxEntity_ObjCProtocol: return "objc-protocol"; 1612 case CXIdxEntity_ObjCCategory: return "objc-category"; 1613 case CXIdxEntity_ObjCMethod: return "objc-method"; 1614 case CXIdxEntity_ObjCProperty: return "objc-property"; 1615 case CXIdxEntity_ObjCIvar: return "objc-ivar"; 1616 case CXIdxEntity_Enum: return "enum"; 1617 case CXIdxEntity_Struct: return "struct"; 1618 case CXIdxEntity_Union: return "union"; 1619 case CXIdxEntity_CXXClass: return "c++-class"; 1620 } 1621 assert(0 && "Garbage entity kind"); 1622 return 0; 1623} 1624 1625static void printEntityInfo(const char *cb, 1626 CXClientData client_data, 1627 const CXIdxEntityInfo *info) { 1628 const char *name; 1629 IndexData *index_data; 1630 index_data = (IndexData *)client_data; 1631 printCheck(index_data); 1632 1633 name = info->name; 1634 if (!name) 1635 name = "<anon-tag>"; 1636 1637 printf("%s: kind: %s", cb, getEntityKindString(info->kind)); 1638 printf(" | name: %s", name); 1639 printf(" | USR: %s", info->USR); 1640} 1641 1642static void index_diagnostic(CXClientData client_data, 1643 CXDiagnostic diag, void *reserved) { 1644 CXString str; 1645 const char *cstr; 1646 IndexData *index_data; 1647 index_data = (IndexData *)client_data; 1648 printCheck(index_data); 1649 1650 str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); 1651 cstr = clang_getCString(str); 1652 printf("[diagnostic]: %s\n", cstr); 1653 clang_disposeString(str); 1654 1655 if (getenv("CINDEXTEST_FAILONERROR") && 1656 clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) { 1657 index_data->fail_for_error = 1; 1658 } 1659} 1660 1661static CXIdxClientFile index_enteredMainFile(CXClientData client_data, 1662 CXFile file, void *reserved) { 1663 IndexData *index_data; 1664 index_data = (IndexData *)client_data; 1665 printCheck(index_data); 1666 1667 printf("[enteredMainFile]: "); 1668 printCXIndexFile((CXIdxClientFile)file); 1669 printf("\n"); 1670 1671 return (CXIdxClientFile)file; 1672} 1673 1674static CXIdxClientFile index_ppIncludedFile(CXClientData client_data, 1675 const CXIdxIncludedFileInfo *info) { 1676 IndexData *index_data; 1677 index_data = (IndexData *)client_data; 1678 printCheck(index_data); 1679 1680 printf("[ppIncludedFile]: "); 1681 printCXIndexFile((CXIdxClientFile)info->file); 1682 printf(" | name: \"%s\"", info->filename); 1683 printf(" | hash loc: "); 1684 printCXIndexLoc(info->hashLoc); 1685 printf(" | isImport: %d | isAngled: %d\n", info->isImport, info->isAngled); 1686 1687 return (CXIdxClientFile)info->file; 1688} 1689 1690static CXIdxClientContainer index_startedTranslationUnit(CXClientData client_data, 1691 void *reserved) { 1692 IndexData *index_data; 1693 index_data = (IndexData *)client_data; 1694 printCheck(index_data); 1695 1696 printf("[startedTranslationUnit]\n"); 1697 return (CXIdxClientContainer)"TU"; 1698} 1699 1700static void index_indexDeclaration(CXClientData client_data, 1701 const CXIdxDeclInfo *info, 1702 const CXIdxDeclOut *outData) { 1703 IndexData *index_data; 1704 const CXIdxObjCCategoryDeclInfo *CatInfo; 1705 const CXIdxObjCInterfaceDeclInfo *InterInfo; 1706 const CXIdxObjCProtocolDeclInfo *ProtoInfo; 1707 unsigned i; 1708 index_data = (IndexData *)client_data; 1709 1710 printEntityInfo("[indexDeclaration]", client_data, info->entityInfo); 1711 printf(" | cursor: "); 1712 PrintCursor(info->cursor); 1713 printf(" | loc: "); 1714 printCXIndexLoc(info->loc); 1715 printf(" | container: "); 1716 printCXIndexContainer(info->container); 1717 printf(" | isRedecl: %d", info->isRedeclaration); 1718 printf(" | isDef: %d\n", info->isDefinition); 1719 1720 if (clang_index_isEntityObjCContainerKind(info->entityInfo->kind)) { 1721 const char *kindName = 0; 1722 CXIdxObjCContainerKind K = clang_index_getObjCContainerDeclInfo(info)->kind; 1723 switch (K) { 1724 case CXIdxObjCContainer_ForwardRef: 1725 kindName = "forward-ref"; break; 1726 case CXIdxObjCContainer_Interface: 1727 kindName = "interface"; break; 1728 case CXIdxObjCContainer_Implementation: 1729 kindName = "implementation"; break; 1730 } 1731 printCheck(index_data); 1732 printf(" <ObjCContainerInfo>: kind: %s\n", kindName); 1733 } 1734 1735 if ((CatInfo = clang_index_getObjCCategoryDeclInfo(info))) { 1736 printEntityInfo(" <ObjCCategoryInfo>: class", client_data, 1737 CatInfo->objcClass); 1738 printf("\n"); 1739 } 1740 1741 if ((InterInfo = clang_index_getObjCInterfaceDeclInfo(info))) { 1742 if (InterInfo->superInfo) { 1743 printEntityInfo(" <ObjCInterfaceInfo>: base", client_data, 1744 InterInfo->superInfo->base); 1745 printf(" | cursor: "); 1746 PrintCursor(InterInfo->superInfo->cursor); 1747 printf(" | loc: "); 1748 printCXIndexLoc(InterInfo->superInfo->loc); 1749 printf("\n"); 1750 } 1751 for (i = 0; i < InterInfo->numProtocols; ++i) { 1752 printEntityInfo(" <ObjCInterfaceInfo>: protocol", client_data, 1753 InterInfo->protocols[i]->protocol); 1754 printf(" | cursor: "); 1755 PrintCursor(InterInfo->protocols[i]->cursor); 1756 printf(" | loc: "); 1757 printCXIndexLoc(InterInfo->protocols[i]->loc); 1758 printf("\n"); 1759 } 1760 } 1761 1762 if ((ProtoInfo = clang_index_getObjCProtocolDeclInfo(info))) { 1763 for (i = 0; i < ProtoInfo->numProtocols; ++i) { 1764 printEntityInfo(" <ObjCProtocolInfo>: protocol", client_data, 1765 ProtoInfo->protocols[i]->protocol); 1766 printf(" | cursor: "); 1767 PrintCursor(ProtoInfo->protocols[i]->cursor); 1768 printf(" | loc: "); 1769 printCXIndexLoc(ProtoInfo->protocols[i]->loc); 1770 printf("\n"); 1771 } 1772 } 1773 1774 if (outData->outContainer) 1775 *outData->outContainer = makeClientContainer(info->entityInfo, info->loc); 1776} 1777 1778static void index_indexEntityReference(CXClientData client_data, 1779 const CXIdxEntityRefInfo *info) { 1780 printEntityInfo("[indexEntityReference]", client_data, info->referencedEntity); 1781 printf(" | cursor: "); 1782 PrintCursor(info->cursor); 1783 printf(" | loc: "); 1784 printCXIndexLoc(info->loc); 1785 printEntityInfo(" | <parent>:", client_data, info->parentEntity); 1786 printf(" | container: "); 1787 printCXIndexContainer(info->container); 1788 printf(" | kind: "); 1789 switch (info->kind) { 1790 case CXIdxEntityRef_Direct: printf("direct"); break; 1791 case CXIdxEntityRef_ImplicitProperty: printf("implicit prop"); break; 1792 } 1793 printf("\n"); 1794} 1795 1796static IndexerCallbacks IndexCB = { 1797 0, /*abortQuery*/ 1798 index_diagnostic, 1799 index_enteredMainFile, 1800 index_ppIncludedFile, 1801 0, /*importedASTFile*/ 1802 index_startedTranslationUnit, 1803 index_indexDeclaration, 1804 index_indexEntityReference 1805}; 1806 1807static int index_file(int argc, const char **argv) { 1808 const char *check_prefix; 1809 CXIndex CIdx; 1810 IndexData index_data; 1811 int result; 1812 1813 check_prefix = 0; 1814 if (argc > 0) { 1815 if (strstr(argv[0], "-check-prefix=") == argv[0]) { 1816 check_prefix = argv[0] + strlen("-check-prefix="); 1817 ++argv; 1818 --argc; 1819 } 1820 } 1821 1822 if (argc == 0) { 1823 fprintf(stderr, "no compiler arguments\n"); 1824 return -1; 1825 } 1826 1827 CIdx = clang_createIndex(0, 1); 1828 index_data.check_prefix = check_prefix; 1829 index_data.first_check_printed = 0; 1830 index_data.fail_for_error = 0; 1831 1832 result = clang_indexTranslationUnit(CIdx, &index_data, 1833 &IndexCB,sizeof(IndexCB), 1834 0, 0, argv, argc, 0, 0, 0, 0); 1835 if (index_data.fail_for_error) 1836 return -1; 1837 1838 return result; 1839} 1840 1841int perform_token_annotation(int argc, const char **argv) { 1842 const char *input = argv[1]; 1843 char *filename = 0; 1844 unsigned line, second_line; 1845 unsigned column, second_column; 1846 CXIndex CIdx; 1847 CXTranslationUnit TU = 0; 1848 int errorCode; 1849 struct CXUnsavedFile *unsaved_files = 0; 1850 int num_unsaved_files = 0; 1851 CXToken *tokens; 1852 unsigned num_tokens; 1853 CXSourceRange range; 1854 CXSourceLocation startLoc, endLoc; 1855 CXFile file = 0; 1856 CXCursor *cursors = 0; 1857 unsigned i; 1858 1859 input += strlen("-test-annotate-tokens="); 1860 if ((errorCode = parse_file_line_column(input, &filename, &line, &column, 1861 &second_line, &second_column))) 1862 return errorCode; 1863 1864 if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) 1865 return -1; 1866 1867 CIdx = clang_createIndex(0, 1); 1868 TU = clang_parseTranslationUnit(CIdx, argv[argc - 1], 1869 argv + num_unsaved_files + 2, 1870 argc - num_unsaved_files - 3, 1871 unsaved_files, 1872 num_unsaved_files, 1873 getDefaultParsingOptions()); 1874 if (!TU) { 1875 fprintf(stderr, "unable to parse input\n"); 1876 clang_disposeIndex(CIdx); 1877 free(filename); 1878 free_remapped_files(unsaved_files, num_unsaved_files); 1879 return -1; 1880 } 1881 errorCode = 0; 1882 1883 if (checkForErrors(TU) != 0) 1884 return -1; 1885 1886 if (getenv("CINDEXTEST_EDITING")) { 1887 for (i = 0; i < 5; ++i) { 1888 if (clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, 1889 clang_defaultReparseOptions(TU))) { 1890 fprintf(stderr, "Unable to reparse translation unit!\n"); 1891 errorCode = -1; 1892 goto teardown; 1893 } 1894 } 1895 } 1896 1897 if (checkForErrors(TU) != 0) { 1898 errorCode = -1; 1899 goto teardown; 1900 } 1901 1902 file = clang_getFile(TU, filename); 1903 if (!file) { 1904 fprintf(stderr, "file %s is not in this translation unit\n", filename); 1905 errorCode = -1; 1906 goto teardown; 1907 } 1908 1909 startLoc = clang_getLocation(TU, file, line, column); 1910 if (clang_equalLocations(clang_getNullLocation(), startLoc)) { 1911 fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line, 1912 column); 1913 errorCode = -1; 1914 goto teardown; 1915 } 1916 1917 endLoc = clang_getLocation(TU, file, second_line, second_column); 1918 if (clang_equalLocations(clang_getNullLocation(), endLoc)) { 1919 fprintf(stderr, "invalid source location %s:%d:%d\n", filename, 1920 second_line, second_column); 1921 errorCode = -1; 1922 goto teardown; 1923 } 1924 1925 range = clang_getRange(startLoc, endLoc); 1926 clang_tokenize(TU, range, &tokens, &num_tokens); 1927 1928 if (checkForErrors(TU) != 0) { 1929 errorCode = -1; 1930 goto teardown; 1931 } 1932 1933 cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor)); 1934 clang_annotateTokens(TU, tokens, num_tokens, cursors); 1935 1936 if (checkForErrors(TU) != 0) { 1937 errorCode = -1; 1938 goto teardown; 1939 } 1940 1941 for (i = 0; i != num_tokens; ++i) { 1942 const char *kind = "<unknown>"; 1943 CXString spelling = clang_getTokenSpelling(TU, tokens[i]); 1944 CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]); 1945 unsigned start_line, start_column, end_line, end_column; 1946 1947 switch (clang_getTokenKind(tokens[i])) { 1948 case CXToken_Punctuation: kind = "Punctuation"; break; 1949 case CXToken_Keyword: kind = "Keyword"; break; 1950 case CXToken_Identifier: kind = "Identifier"; break; 1951 case CXToken_Literal: kind = "Literal"; break; 1952 case CXToken_Comment: kind = "Comment"; break; 1953 } 1954 clang_getSpellingLocation(clang_getRangeStart(extent), 1955 0, &start_line, &start_column, 0); 1956 clang_getSpellingLocation(clang_getRangeEnd(extent), 1957 0, &end_line, &end_column, 0); 1958 printf("%s: \"%s\" ", kind, clang_getCString(spelling)); 1959 PrintExtent(stdout, start_line, start_column, end_line, end_column); 1960 if (!clang_isInvalid(cursors[i].kind)) { 1961 printf(" "); 1962 PrintCursor(cursors[i]); 1963 } 1964 printf("\n"); 1965 } 1966 free(cursors); 1967 clang_disposeTokens(TU, tokens, num_tokens); 1968 1969 teardown: 1970 PrintDiagnostics(TU); 1971 clang_disposeTranslationUnit(TU); 1972 clang_disposeIndex(CIdx); 1973 free(filename); 1974 free_remapped_files(unsaved_files, num_unsaved_files); 1975 return errorCode; 1976} 1977 1978/******************************************************************************/ 1979/* USR printing. */ 1980/******************************************************************************/ 1981 1982static int insufficient_usr(const char *kind, const char *usage) { 1983 fprintf(stderr, "USR for '%s' requires: %s\n", kind, usage); 1984 return 1; 1985} 1986 1987static unsigned isUSR(const char *s) { 1988 return s[0] == 'c' && s[1] == ':'; 1989} 1990 1991static int not_usr(const char *s, const char *arg) { 1992 fprintf(stderr, "'%s' argument ('%s') is not a USR\n", s, arg); 1993 return 1; 1994} 1995 1996static void print_usr(CXString usr) { 1997 const char *s = clang_getCString(usr); 1998 printf("%s\n", s); 1999 clang_disposeString(usr); 2000} 2001 2002static void display_usrs() { 2003 fprintf(stderr, "-print-usrs options:\n" 2004 " ObjCCategory <class name> <category name>\n" 2005 " ObjCClass <class name>\n" 2006 " ObjCIvar <ivar name> <class USR>\n" 2007 " ObjCMethod <selector> [0=class method|1=instance method] " 2008 "<class USR>\n" 2009 " ObjCProperty <property name> <class USR>\n" 2010 " ObjCProtocol <protocol name>\n"); 2011} 2012 2013int print_usrs(const char **I, const char **E) { 2014 while (I != E) { 2015 const char *kind = *I; 2016 unsigned len = strlen(kind); 2017 switch (len) { 2018 case 8: 2019 if (memcmp(kind, "ObjCIvar", 8) == 0) { 2020 if (I + 2 >= E) 2021 return insufficient_usr(kind, "<ivar name> <class USR>"); 2022 if (!isUSR(I[2])) 2023 return not_usr("<class USR>", I[2]); 2024 else { 2025 CXString x; 2026 x.data = (void*) I[2]; 2027 x.private_flags = 0; 2028 print_usr(clang_constructUSR_ObjCIvar(I[1], x)); 2029 } 2030 2031 I += 3; 2032 continue; 2033 } 2034 break; 2035 case 9: 2036 if (memcmp(kind, "ObjCClass", 9) == 0) { 2037 if (I + 1 >= E) 2038 return insufficient_usr(kind, "<class name>"); 2039 print_usr(clang_constructUSR_ObjCClass(I[1])); 2040 I += 2; 2041 continue; 2042 } 2043 break; 2044 case 10: 2045 if (memcmp(kind, "ObjCMethod", 10) == 0) { 2046 if (I + 3 >= E) 2047 return insufficient_usr(kind, "<method selector> " 2048 "[0=class method|1=instance method] <class USR>"); 2049 if (!isUSR(I[3])) 2050 return not_usr("<class USR>", I[3]); 2051 else { 2052 CXString x; 2053 x.data = (void*) I[3]; 2054 x.private_flags = 0; 2055 print_usr(clang_constructUSR_ObjCMethod(I[1], atoi(I[2]), x)); 2056 } 2057 I += 4; 2058 continue; 2059 } 2060 break; 2061 case 12: 2062 if (memcmp(kind, "ObjCCategory", 12) == 0) { 2063 if (I + 2 >= E) 2064 return insufficient_usr(kind, "<class name> <category name>"); 2065 print_usr(clang_constructUSR_ObjCCategory(I[1], I[2])); 2066 I += 3; 2067 continue; 2068 } 2069 if (memcmp(kind, "ObjCProtocol", 12) == 0) { 2070 if (I + 1 >= E) 2071 return insufficient_usr(kind, "<protocol name>"); 2072 print_usr(clang_constructUSR_ObjCProtocol(I[1])); 2073 I += 2; 2074 continue; 2075 } 2076 if (memcmp(kind, "ObjCProperty", 12) == 0) { 2077 if (I + 2 >= E) 2078 return insufficient_usr(kind, "<property name> <class USR>"); 2079 if (!isUSR(I[2])) 2080 return not_usr("<class USR>", I[2]); 2081 else { 2082 CXString x; 2083 x.data = (void*) I[2]; 2084 x.private_flags = 0; 2085 print_usr(clang_constructUSR_ObjCProperty(I[1], x)); 2086 } 2087 I += 3; 2088 continue; 2089 } 2090 break; 2091 default: 2092 break; 2093 } 2094 break; 2095 } 2096 2097 if (I != E) { 2098 fprintf(stderr, "Invalid USR kind: %s\n", *I); 2099 display_usrs(); 2100 return 1; 2101 } 2102 return 0; 2103} 2104 2105int print_usrs_file(const char *file_name) { 2106 char line[2048]; 2107 const char *args[128]; 2108 unsigned numChars = 0; 2109 2110 FILE *fp = fopen(file_name, "r"); 2111 if (!fp) { 2112 fprintf(stderr, "error: cannot open '%s'\n", file_name); 2113 return 1; 2114 } 2115 2116 /* This code is not really all that safe, but it works fine for testing. */ 2117 while (!feof(fp)) { 2118 char c = fgetc(fp); 2119 if (c == '\n') { 2120 unsigned i = 0; 2121 const char *s = 0; 2122 2123 if (numChars == 0) 2124 continue; 2125 2126 line[numChars] = '\0'; 2127 numChars = 0; 2128 2129 if (line[0] == '/' && line[1] == '/') 2130 continue; 2131 2132 s = strtok(line, " "); 2133 while (s) { 2134 args[i] = s; 2135 ++i; 2136 s = strtok(0, " "); 2137 } 2138 if (print_usrs(&args[0], &args[i])) 2139 return 1; 2140 } 2141 else 2142 line[numChars++] = c; 2143 } 2144 2145 fclose(fp); 2146 return 0; 2147} 2148 2149/******************************************************************************/ 2150/* Command line processing. */ 2151/******************************************************************************/ 2152int write_pch_file(const char *filename, int argc, const char *argv[]) { 2153 CXIndex Idx; 2154 CXTranslationUnit TU; 2155 struct CXUnsavedFile *unsaved_files = 0; 2156 int num_unsaved_files = 0; 2157 int result = 0; 2158 2159 Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnosics=*/1); 2160 2161 if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { 2162 clang_disposeIndex(Idx); 2163 return -1; 2164 } 2165 2166 TU = clang_parseTranslationUnit(Idx, 0, 2167 argv + num_unsaved_files, 2168 argc - num_unsaved_files, 2169 unsaved_files, 2170 num_unsaved_files, 2171 CXTranslationUnit_Incomplete); 2172 if (!TU) { 2173 fprintf(stderr, "Unable to load translation unit!\n"); 2174 free_remapped_files(unsaved_files, num_unsaved_files); 2175 clang_disposeIndex(Idx); 2176 return 1; 2177 } 2178 2179 switch (clang_saveTranslationUnit(TU, filename, 2180 clang_defaultSaveOptions(TU))) { 2181 case CXSaveError_None: 2182 break; 2183 2184 case CXSaveError_TranslationErrors: 2185 fprintf(stderr, "Unable to write PCH file %s: translation errors\n", 2186 filename); 2187 result = 2; 2188 break; 2189 2190 case CXSaveError_InvalidTU: 2191 fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", 2192 filename); 2193 result = 3; 2194 break; 2195 2196 case CXSaveError_Unknown: 2197 default: 2198 fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename); 2199 result = 1; 2200 break; 2201 } 2202 2203 clang_disposeTranslationUnit(TU); 2204 free_remapped_files(unsaved_files, num_unsaved_files); 2205 clang_disposeIndex(Idx); 2206 return result; 2207} 2208 2209/******************************************************************************/ 2210/* Serialized diagnostics. */ 2211/******************************************************************************/ 2212 2213static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) { 2214 switch (error) { 2215 case CXLoadDiag_CannotLoad: return "Cannot Load File"; 2216 case CXLoadDiag_None: break; 2217 case CXLoadDiag_Unknown: return "Unknown"; 2218 case CXLoadDiag_InvalidFile: return "Invalid File"; 2219 } 2220 return "None"; 2221} 2222 2223static const char *getSeverityString(enum CXDiagnosticSeverity severity) { 2224 switch (severity) { 2225 case CXDiagnostic_Note: return "note"; 2226 case CXDiagnostic_Error: return "error"; 2227 case CXDiagnostic_Fatal: return "fatal"; 2228 case CXDiagnostic_Ignored: return "ignored"; 2229 case CXDiagnostic_Warning: return "warning"; 2230 } 2231 return "unknown"; 2232} 2233 2234static void printIndent(unsigned indent) { 2235 if (indent == 0) 2236 return; 2237 fprintf(stderr, "+"); 2238 --indent; 2239 while (indent > 0) { 2240 fprintf(stderr, "-"); 2241 --indent; 2242 } 2243} 2244 2245static void printLocation(CXSourceLocation L) { 2246 CXFile File; 2247 CXString FileName; 2248 unsigned line, column, offset; 2249 2250 clang_getExpansionLocation(L, &File, &line, &column, &offset); 2251 FileName = clang_getFileName(File); 2252 2253 fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column); 2254 clang_disposeString(FileName); 2255} 2256 2257static void printRanges(CXDiagnostic D, unsigned indent) { 2258 unsigned i, n = clang_getDiagnosticNumRanges(D); 2259 2260 for (i = 0; i < n; ++i) { 2261 CXSourceLocation Start, End; 2262 CXSourceRange SR = clang_getDiagnosticRange(D, i); 2263 Start = clang_getRangeStart(SR); 2264 End = clang_getRangeEnd(SR); 2265 2266 printIndent(indent); 2267 fprintf(stderr, "Range: "); 2268 printLocation(Start); 2269 fprintf(stderr, " "); 2270 printLocation(End); 2271 fprintf(stderr, "\n"); 2272 } 2273} 2274 2275static void printFixIts(CXDiagnostic D, unsigned indent) { 2276 unsigned i, n = clang_getDiagnosticNumFixIts(D); 2277 for (i = 0 ; i < n; ++i) { 2278 CXSourceRange ReplacementRange; 2279 CXString text; 2280 text = clang_getDiagnosticFixIt(D, i, &ReplacementRange); 2281 2282 printIndent(indent); 2283 fprintf(stderr, "FIXIT: ("); 2284 printLocation(clang_getRangeStart(ReplacementRange)); 2285 fprintf(stderr, " - "); 2286 printLocation(clang_getRangeEnd(ReplacementRange)); 2287 fprintf(stderr, "): \"%s\"\n", clang_getCString(text)); 2288 clang_disposeString(text); 2289 } 2290} 2291 2292static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) { 2293 unsigned i, n; 2294 2295 if (!Diags) 2296 return; 2297 2298 n = clang_getNumDiagnosticsInSet(Diags); 2299 for (i = 0; i < n; ++i) { 2300 CXSourceLocation DiagLoc; 2301 CXDiagnostic D; 2302 CXFile File; 2303 CXString FileName, DiagSpelling, DiagOption; 2304 unsigned line, column, offset; 2305 const char *DiagOptionStr = 0; 2306 2307 D = clang_getDiagnosticInSet(Diags, i); 2308 DiagLoc = clang_getDiagnosticLocation(D); 2309 clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset); 2310 FileName = clang_getFileName(File); 2311 DiagSpelling = clang_getDiagnosticSpelling(D); 2312 2313 printIndent(indent); 2314 2315 fprintf(stderr, "%s:%d:%d: %s: %s", 2316 clang_getCString(FileName), 2317 line, 2318 column, 2319 getSeverityString(clang_getDiagnosticSeverity(D)), 2320 clang_getCString(DiagSpelling)); 2321 2322 DiagOption = clang_getDiagnosticOption(D, 0); 2323 DiagOptionStr = clang_getCString(DiagOption); 2324 if (DiagOptionStr) { 2325 fprintf(stderr, " [%s]", DiagOptionStr); 2326 } 2327 2328 fprintf(stderr, "\n"); 2329 2330 printRanges(D, indent); 2331 printFixIts(D, indent); 2332 2333 /* Print subdiagnostics. */ 2334 printDiagnosticSet(clang_getChildDiagnostics(D), indent+2); 2335 2336 clang_disposeString(FileName); 2337 clang_disposeString(DiagSpelling); 2338 clang_disposeString(DiagOption); 2339 } 2340} 2341 2342static int read_diagnostics(const char *filename) { 2343 enum CXLoadDiag_Error error; 2344 CXString errorString; 2345 CXDiagnosticSet Diags = 0; 2346 2347 Diags = clang_loadDiagnostics(filename, &error, &errorString); 2348 if (!Diags) { 2349 fprintf(stderr, "Trouble deserializing file (%s): %s\n", 2350 getDiagnosticCodeStr(error), 2351 clang_getCString(errorString)); 2352 clang_disposeString(errorString); 2353 return 1; 2354 } 2355 2356 printDiagnosticSet(Diags, 0); 2357 fprintf(stderr, "Number of diagnostics: %d\n", 2358 clang_getNumDiagnosticsInSet(Diags)); 2359 clang_disposeDiagnosticSet(Diags); 2360 return 0; 2361} 2362 2363/******************************************************************************/ 2364/* Command line processing. */ 2365/******************************************************************************/ 2366 2367static CXCursorVisitor GetVisitor(const char *s) { 2368 if (s[0] == '\0') 2369 return FilteredPrintingVisitor; 2370 if (strcmp(s, "-usrs") == 0) 2371 return USRVisitor; 2372 if (strncmp(s, "-memory-usage", 13) == 0) 2373 return GetVisitor(s + 13); 2374 return NULL; 2375} 2376 2377static void print_usage(void) { 2378 fprintf(stderr, 2379 "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n" 2380 " c-index-test -code-completion-timing=<site> <compiler arguments>\n" 2381 " c-index-test -cursor-at=<site> <compiler arguments>\n" 2382 " c-index-test -file-refs-at=<site> <compiler arguments>\n" 2383 " c-index-test -index-file [-check-prefix=<FileCheck prefix>] <compiler arguments>\n" 2384 " c-index-test -test-file-scan <AST file> <source file> " 2385 "[FileCheck prefix]\n"); 2386 fprintf(stderr, 2387 " c-index-test -test-load-tu <AST file> <symbol filter> " 2388 "[FileCheck prefix]\n" 2389 " c-index-test -test-load-tu-usrs <AST file> <symbol filter> " 2390 "[FileCheck prefix]\n" 2391 " c-index-test -test-load-source <symbol filter> {<args>}*\n"); 2392 fprintf(stderr, 2393 " c-index-test -test-load-source-memory-usage " 2394 "<symbol filter> {<args>}*\n" 2395 " c-index-test -test-load-source-reparse <trials> <symbol filter> " 2396 " {<args>}*\n" 2397 " c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n" 2398 " c-index-test -test-load-source-usrs-memory-usage " 2399 "<symbol filter> {<args>}*\n" 2400 " c-index-test -test-annotate-tokens=<range> {<args>}*\n" 2401 " c-index-test -test-inclusion-stack-source {<args>}*\n" 2402 " c-index-test -test-inclusion-stack-tu <AST file>\n"); 2403 fprintf(stderr, 2404 " c-index-test -test-print-linkage-source {<args>}*\n" 2405 " c-index-test -test-print-typekind {<args>}*\n" 2406 " c-index-test -print-usr [<CursorKind> {<args>}]*\n" 2407 " c-index-test -print-usr-file <file>\n" 2408 " c-index-test -write-pch <file> <compiler arguments>\n"); 2409 fprintf(stderr, 2410 " c-index-test -read-diagnostics <file>\n\n"); 2411 fprintf(stderr, 2412 " <symbol filter> values:\n%s", 2413 " all - load all symbols, including those from PCH\n" 2414 " local - load all symbols except those in PCH\n" 2415 " category - only load ObjC categories (non-PCH)\n" 2416 " interface - only load ObjC interfaces (non-PCH)\n" 2417 " protocol - only load ObjC protocols (non-PCH)\n" 2418 " function - only load functions (non-PCH)\n" 2419 " typedef - only load typdefs (non-PCH)\n" 2420 " scan-function - scan function bodies (non-PCH)\n\n"); 2421} 2422 2423/***/ 2424 2425int cindextest_main(int argc, const char **argv) { 2426 clang_enableStackTraces(); 2427 if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0) 2428 return read_diagnostics(argv[2]); 2429 if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) 2430 return perform_code_completion(argc, argv, 0); 2431 if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1]) 2432 return perform_code_completion(argc, argv, 1); 2433 if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1]) 2434 return inspect_cursor_at(argc, argv); 2435 if (argc > 2 && strstr(argv[1], "-file-refs-at=") == argv[1]) 2436 return find_file_refs_at(argc, argv); 2437 if (argc > 2 && strcmp(argv[1], "-index-file") == 0) 2438 return index_file(argc - 2, argv + 2); 2439 else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) { 2440 CXCursorVisitor I = GetVisitor(argv[1] + 13); 2441 if (I) 2442 return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I, 2443 NULL); 2444 } 2445 else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){ 2446 CXCursorVisitor I = GetVisitor(argv[1] + 25); 2447 if (I) { 2448 int trials = atoi(argv[2]); 2449 return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, 2450 NULL); 2451 } 2452 } 2453 else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) { 2454 CXCursorVisitor I = GetVisitor(argv[1] + 17); 2455 2456 PostVisitTU postVisit = 0; 2457 if (strstr(argv[1], "-memory-usage")) 2458 postVisit = PrintMemoryUsage; 2459 2460 if (I) 2461 return perform_test_load_source(argc - 3, argv + 3, argv[2], I, 2462 postVisit); 2463 } 2464 else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) 2465 return perform_file_scan(argv[2], argv[3], 2466 argc >= 5 ? argv[4] : 0); 2467 else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1]) 2468 return perform_token_annotation(argc, argv); 2469 else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0) 2470 return perform_test_load_source(argc - 2, argv + 2, "all", NULL, 2471 PrintInclusionStack); 2472 else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0) 2473 return perform_test_load_tu(argv[2], "all", NULL, NULL, 2474 PrintInclusionStack); 2475 else if (argc > 2 && strcmp(argv[1], "-test-print-linkage-source") == 0) 2476 return perform_test_load_source(argc - 2, argv + 2, "all", PrintLinkage, 2477 NULL); 2478 else if (argc > 2 && strcmp(argv[1], "-test-print-typekind") == 0) 2479 return perform_test_load_source(argc - 2, argv + 2, "all", 2480 PrintTypeKind, 0); 2481 else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) { 2482 if (argc > 2) 2483 return print_usrs(argv + 2, argv + argc); 2484 else { 2485 display_usrs(); 2486 return 1; 2487 } 2488 } 2489 else if (argc > 2 && strcmp(argv[1], "-print-usr-file") == 0) 2490 return print_usrs_file(argv[2]); 2491 else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0) 2492 return write_pch_file(argv[2], argc - 3, argv + 3); 2493 2494 print_usage(); 2495 return 1; 2496} 2497 2498/***/ 2499 2500/* We intentionally run in a separate thread to ensure we at least minimal 2501 * testing of a multithreaded environment (for example, having a reduced stack 2502 * size). */ 2503 2504typedef struct thread_info { 2505 int argc; 2506 const char **argv; 2507 int result; 2508} thread_info; 2509void thread_runner(void *client_data_v) { 2510 thread_info *client_data = client_data_v; 2511 client_data->result = cindextest_main(client_data->argc, client_data->argv); 2512} 2513 2514int main(int argc, const char **argv) { 2515 thread_info client_data; 2516 2517 if (getenv("CINDEXTEST_NOTHREADS")) 2518 return cindextest_main(argc, argv); 2519 2520 client_data.argc = argc; 2521 client_data.argv = argv; 2522 clang_executeOnThread(thread_runner, &client_data, 0); 2523 return client_data.result; 2524} 2525