c-index-test.c revision a88084b78fd4ca5d3d858c14b02414f8cc399f02
1/* c-index-test.c */ 2 3#include "clang-c/Index.h" 4#include <stdlib.h> 5#include <stdio.h> 6#include <string.h> 7#include <assert.h> 8 9/******************************************************************************/ 10/* Utility functions. */ 11/******************************************************************************/ 12 13#ifdef _MSC_VER 14char *basename(const char* path) 15{ 16 char* base1 = (char*)strrchr(path, '/'); 17 char* base2 = (char*)strrchr(path, '\\'); 18 if (base1 && base2) 19 return((base1 > base2) ? base1 + 1 : base2 + 1); 20 else if (base1) 21 return(base1 + 1); 22 else if (base2) 23 return(base2 + 1); 24 25 return((char*)path); 26} 27#else 28extern char *basename(const char *); 29#endif 30 31static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column, 32 unsigned end_line, unsigned end_column) { 33 fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column, 34 end_line, end_column); 35} 36 37static unsigned CreateTranslationUnit(CXIndex Idx, const char *file, 38 CXTranslationUnit *TU) { 39 40 *TU = clang_createTranslationUnit(Idx, file); 41 if (!TU) { 42 fprintf(stderr, "Unable to load translation unit from '%s'!\n", file); 43 return 0; 44 } 45 return 1; 46} 47 48void free_remapped_files(struct CXUnsavedFile *unsaved_files, 49 int num_unsaved_files) { 50 int i; 51 for (i = 0; i != num_unsaved_files; ++i) { 52 free((char *)unsaved_files[i].Filename); 53 free((char *)unsaved_files[i].Contents); 54 } 55} 56 57int parse_remapped_files(int argc, const char **argv, int start_arg, 58 struct CXUnsavedFile **unsaved_files, 59 int *num_unsaved_files) { 60 int i; 61 int arg; 62 int prefix_len = strlen("-remap-file="); 63 *unsaved_files = 0; 64 *num_unsaved_files = 0; 65 66 /* Count the number of remapped files. */ 67 for (arg = start_arg; arg < argc; ++arg) { 68 if (strncmp(argv[arg], "-remap-file=", prefix_len)) 69 break; 70 71 ++*num_unsaved_files; 72 } 73 74 if (*num_unsaved_files == 0) 75 return 0; 76 77 *unsaved_files 78 = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) * 79 *num_unsaved_files); 80 for (arg = start_arg, i = 0; i != *num_unsaved_files; ++i, ++arg) { 81 struct CXUnsavedFile *unsaved = *unsaved_files + i; 82 const char *arg_string = argv[arg] + prefix_len; 83 int filename_len; 84 char *filename; 85 char *contents; 86 FILE *to_file; 87 const char *semi = strchr(arg_string, ';'); 88 if (!semi) { 89 fprintf(stderr, 90 "error: -remap-file=from;to argument is missing semicolon\n"); 91 free_remapped_files(*unsaved_files, i); 92 *unsaved_files = 0; 93 *num_unsaved_files = 0; 94 return -1; 95 } 96 97 /* Open the file that we're remapping to. */ 98 to_file = fopen(semi + 1, "r"); 99 if (!to_file) { 100 fprintf(stderr, "error: cannot open file %s that we are remapping to\n", 101 semi + 1); 102 free_remapped_files(*unsaved_files, i); 103 *unsaved_files = 0; 104 *num_unsaved_files = 0; 105 return -1; 106 } 107 108 /* Determine the length of the file we're remapping to. */ 109 fseek(to_file, 0, SEEK_END); 110 unsaved->Length = ftell(to_file); 111 fseek(to_file, 0, SEEK_SET); 112 113 /* Read the contents of the file we're remapping to. */ 114 contents = (char *)malloc(unsaved->Length + 1); 115 if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) { 116 fprintf(stderr, "error: unexpected %s reading 'to' file %s\n", 117 (feof(to_file) ? "EOF" : "error"), semi + 1); 118 fclose(to_file); 119 free_remapped_files(*unsaved_files, i); 120 *unsaved_files = 0; 121 *num_unsaved_files = 0; 122 return -1; 123 } 124 contents[unsaved->Length] = 0; 125 unsaved->Contents = contents; 126 127 /* Close the file. */ 128 fclose(to_file); 129 130 /* Copy the file name that we're remapping from. */ 131 filename_len = semi - arg_string; 132 filename = (char *)malloc(filename_len + 1); 133 memcpy(filename, arg_string, filename_len); 134 filename[filename_len] = 0; 135 unsaved->Filename = filename; 136 } 137 138 return 0; 139} 140 141/******************************************************************************/ 142/* Pretty-printing. */ 143/******************************************************************************/ 144 145static void PrintCursor(CXCursor Cursor) { 146 if (clang_isInvalid(Cursor.kind)) { 147 CXString ks = clang_getCursorKindSpelling(Cursor.kind); 148 printf("Invalid Cursor => %s", clang_getCString(ks)); 149 clang_disposeString(ks); 150 } 151 else { 152 CXString string, ks; 153 CXCursor Referenced; 154 unsigned line, column; 155 156 ks = clang_getCursorKindSpelling(Cursor.kind); 157 string = clang_getCursorSpelling(Cursor); 158 printf("%s=%s", clang_getCString(ks), 159 clang_getCString(string)); 160 clang_disposeString(ks); 161 clang_disposeString(string); 162 163 Referenced = clang_getCursorReferenced(Cursor); 164 if (!clang_equalCursors(Referenced, clang_getNullCursor())) { 165 CXSourceLocation Loc = clang_getCursorLocation(Referenced); 166 clang_getInstantiationLocation(Loc, 0, &line, &column, 0); 167 printf(":%d:%d", line, column); 168 } 169 170 if (clang_isCursorDefinition(Cursor)) 171 printf(" (Definition)"); 172 } 173} 174 175static const char* GetCursorSource(CXCursor Cursor) { 176 CXSourceLocation Loc = clang_getCursorLocation(Cursor); 177 CXString source; 178 CXFile file; 179 clang_getInstantiationLocation(Loc, &file, 0, 0, 0); 180 source = clang_getFileName(file); 181 if (!clang_getCString(source)) { 182 clang_disposeString(source); 183 return "<invalid loc>"; 184 } 185 else { 186 const char *b = basename(clang_getCString(source)); 187 clang_disposeString(source); 188 return b; 189 } 190} 191 192/******************************************************************************/ 193/* Callbacks. */ 194/******************************************************************************/ 195 196typedef void (*PostVisitTU)(CXTranslationUnit); 197 198void PrintDiagnostic(CXDiagnostic Diagnostic) { 199 FILE *out = stderr; 200 CXFile file; 201 unsigned line, column; 202 CXString text; 203 enum CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(Diagnostic); 204 205 /* Ignore diagnostics that should be ignored. */ 206 if (severity == CXDiagnostic_Ignored) 207 return; 208 209 /* Print file:line:column. */ 210 clang_getInstantiationLocation(clang_getDiagnosticLocation(Diagnostic), 211 &file, &line, &column, 0); 212 if (file) { 213 unsigned i, n; 214 unsigned printed_any_ranges = 0; 215 CXString fname; 216 217 fname = clang_getFileName(file); 218 fprintf(out, "%s:%d:%d:", clang_getCString(fname), line, column); 219 clang_disposeString(fname); 220 221 n = clang_getDiagnosticNumRanges(Diagnostic); 222 for (i = 0; i != n; ++i) { 223 CXFile start_file, end_file; 224 CXSourceRange range = clang_getDiagnosticRange(Diagnostic, i); 225 226 unsigned start_line, start_column, end_line, end_column; 227 clang_getInstantiationLocation(clang_getRangeStart(range), 228 &start_file, &start_line, &start_column,0); 229 clang_getInstantiationLocation(clang_getRangeEnd(range), 230 &end_file, &end_line, &end_column, 0); 231 232 if (start_file != end_file || start_file != file) 233 continue; 234 235 PrintExtent(out, start_line, start_column, end_line, end_column); 236 printed_any_ranges = 1; 237 } 238 if (printed_any_ranges) 239 fprintf(out, ":"); 240 241 fprintf(out, " "); 242 } 243 244 /* Print warning/error/etc. */ 245 switch (severity) { 246 case CXDiagnostic_Ignored: assert(0 && "impossible"); break; 247 case CXDiagnostic_Note: fprintf(out, "note: "); break; 248 case CXDiagnostic_Warning: fprintf(out, "warning: "); break; 249 case CXDiagnostic_Error: fprintf(out, "error: "); break; 250 case CXDiagnostic_Fatal: fprintf(out, "fatal error: "); break; 251 } 252 253 text = clang_getDiagnosticSpelling(Diagnostic); 254 if (clang_getCString(text)) 255 fprintf(out, "%s\n", clang_getCString(text)); 256 else 257 fprintf(out, "<no diagnostic text>\n"); 258 clang_disposeString(text); 259 260 if (file) { 261 unsigned i, num_fixits = clang_getDiagnosticNumFixIts(Diagnostic); 262 for (i = 0; i != num_fixits; ++i) { 263 switch (clang_getDiagnosticFixItKind(Diagnostic, i)) { 264 case CXFixIt_Insertion: { 265 CXSourceLocation insertion_loc; 266 CXFile insertion_file; 267 unsigned insertion_line, insertion_column; 268 text = clang_getDiagnosticFixItInsertion(Diagnostic, i, &insertion_loc); 269 clang_getInstantiationLocation(insertion_loc, &insertion_file, 270 &insertion_line, &insertion_column, 0); 271 if (insertion_file == file) 272 fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n", 273 clang_getCString(text), insertion_line, insertion_column); 274 clang_disposeString(text); 275 break; 276 } 277 278 case CXFixIt_Removal: { 279 CXFile start_file, end_file; 280 unsigned start_line, start_column, end_line, end_column; 281 CXSourceRange remove_range 282 = clang_getDiagnosticFixItRemoval(Diagnostic, i); 283 clang_getInstantiationLocation(clang_getRangeStart(remove_range), 284 &start_file, &start_line, &start_column, 285 0); 286 clang_getInstantiationLocation(clang_getRangeEnd(remove_range), 287 &end_file, &end_line, &end_column, 0); 288 if (start_file == file && end_file == file) { 289 fprintf(out, "FIX-IT: Remove "); 290 PrintExtent(out, start_line, start_column, end_line, end_column); 291 fprintf(out, "\n"); 292 } 293 break; 294 } 295 296 case CXFixIt_Replacement: { 297 CXFile start_file, end_file; 298 unsigned start_line, start_column, end_line, end_column; 299 CXSourceRange remove_range; 300 text = clang_getDiagnosticFixItReplacement(Diagnostic, i,&remove_range); 301 clang_getInstantiationLocation(clang_getRangeStart(remove_range), 302 &start_file, &start_line, &start_column, 303 0); 304 clang_getInstantiationLocation(clang_getRangeEnd(remove_range), 305 &end_file, &end_line, &end_column, 0); 306 if (start_file == end_file) { 307 fprintf(out, "FIX-IT: Replace "); 308 PrintExtent(out, start_line, start_column, end_line, end_column); 309 fprintf(out, " with \"%s\"\n", clang_getCString(text)); 310 } 311 clang_disposeString(text); 312 break; 313 } 314 } 315 } 316 } 317} 318 319void PrintDiagnostics(CXTranslationUnit TU) { 320 int i, n = clang_getNumDiagnostics(TU); 321 for (i = 0; i != n; ++i) { 322 CXDiagnostic Diag = clang_getDiagnostic(TU, i); 323 PrintDiagnostic(Diag); 324 clang_disposeDiagnostic(Diag); 325 } 326} 327 328/******************************************************************************/ 329/* Logic for testing traversal. */ 330/******************************************************************************/ 331 332static const char *FileCheckPrefix = "CHECK"; 333 334static void PrintCursorExtent(CXCursor C) { 335 CXSourceRange extent = clang_getCursorExtent(C); 336 CXFile begin_file, end_file; 337 unsigned begin_line, begin_column, end_line, end_column; 338 339 clang_getInstantiationLocation(clang_getRangeStart(extent), 340 &begin_file, &begin_line, &begin_column, 0); 341 clang_getInstantiationLocation(clang_getRangeEnd(extent), 342 &end_file, &end_line, &end_column, 0); 343 if (!begin_file || !end_file) 344 return; 345 346 printf(" Extent="); 347 PrintExtent(stdout, begin_line, begin_column, end_line, end_column); 348} 349 350/* Data used by all of the visitors. */ 351typedef struct { 352 CXTranslationUnit TU; 353 enum CXCursorKind *Filter; 354} VisitorData; 355 356 357enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, 358 CXCursor Parent, 359 CXClientData ClientData) { 360 VisitorData *Data = (VisitorData *)ClientData; 361 if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) { 362 CXSourceLocation Loc = clang_getCursorLocation(Cursor); 363 unsigned line, column; 364 clang_getInstantiationLocation(Loc, 0, &line, &column, 0); 365 printf("// %s: %s:%d:%d: ", FileCheckPrefix, 366 GetCursorSource(Cursor), line, column); 367 PrintCursor(Cursor); 368 PrintCursorExtent(Cursor); 369 printf("\n"); 370 return CXChildVisit_Recurse; 371 } 372 373 return CXChildVisit_Continue; 374} 375 376static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor, 377 CXCursor Parent, 378 CXClientData ClientData) { 379 const char *startBuf, *endBuf; 380 unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn; 381 CXCursor Ref; 382 VisitorData *Data = (VisitorData *)ClientData; 383 384 if (Cursor.kind != CXCursor_FunctionDecl || 385 !clang_isCursorDefinition(Cursor)) 386 return CXChildVisit_Continue; 387 388 clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, 389 &startLine, &startColumn, 390 &endLine, &endColumn); 391 /* Probe the entire body, looking for both decls and refs. */ 392 curLine = startLine; 393 curColumn = startColumn; 394 395 while (startBuf < endBuf) { 396 CXSourceLocation Loc; 397 CXFile file; 398 CXString source; 399 400 if (*startBuf == '\n') { 401 startBuf++; 402 curLine++; 403 curColumn = 1; 404 } else if (*startBuf != '\t') 405 curColumn++; 406 407 Loc = clang_getCursorLocation(Cursor); 408 clang_getInstantiationLocation(Loc, &file, 0, 0, 0); 409 410 source = clang_getFileName(file); 411 if (clang_getCString(source)) { 412 CXSourceLocation RefLoc 413 = clang_getLocation(Data->TU, file, curLine, curColumn); 414 Ref = clang_getCursor(Data->TU, RefLoc); 415 if (Ref.kind == CXCursor_NoDeclFound) { 416 /* Nothing found here; that's fine. */ 417 } else if (Ref.kind != CXCursor_FunctionDecl) { 418 printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), 419 curLine, curColumn); 420 PrintCursor(Ref); 421 printf("\n"); 422 } 423 } 424 clang_disposeString(source); 425 startBuf++; 426 } 427 428 return CXChildVisit_Continue; 429} 430 431/******************************************************************************/ 432/* USR testing. */ 433/******************************************************************************/ 434 435enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, 436 CXClientData ClientData) { 437 VisitorData *Data = (VisitorData *)ClientData; 438 if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { 439 CXString USR = clang_getCursorUSR(C); 440 if (!clang_getCString(USR)) { 441 clang_disposeString(USR); 442 return CXChildVisit_Continue; 443 } 444 printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), 445 clang_getCString(USR)); 446 PrintCursorExtent(C); 447 printf("\n"); 448 clang_disposeString(USR); 449 450 return CXChildVisit_Recurse; 451 } 452 453 return CXChildVisit_Continue; 454} 455 456/******************************************************************************/ 457/* Inclusion stack testing. */ 458/******************************************************************************/ 459 460void InclusionVisitor(CXFile includedFile, CXSourceLocation *includeStack, 461 unsigned includeStackLen, CXClientData data) { 462 463 unsigned i; 464 CXString fname; 465 466 fname = clang_getFileName(includedFile); 467 printf("file: %s\nincluded by:\n", clang_getCString(fname)); 468 clang_disposeString(fname); 469 470 for (i = 0; i < includeStackLen; ++i) { 471 CXFile includingFile; 472 unsigned line, column; 473 clang_getInstantiationLocation(includeStack[i], &includingFile, &line, 474 &column, 0); 475 fname = clang_getFileName(includingFile); 476 printf(" %s:%d:%d\n", clang_getCString(fname), line, column); 477 clang_disposeString(fname); 478 } 479 printf("\n"); 480} 481 482void PrintInclusionStack(CXTranslationUnit TU) { 483 clang_getInclusions(TU, InclusionVisitor, NULL); 484} 485 486/******************************************************************************/ 487/* Loading ASTs/source. */ 488/******************************************************************************/ 489 490static int perform_test_load(CXIndex Idx, CXTranslationUnit TU, 491 const char *filter, const char *prefix, 492 CXCursorVisitor Visitor, 493 PostVisitTU PV) { 494 495 if (prefix) 496 FileCheckPrefix = prefix; 497 498 if (Visitor) { 499 enum CXCursorKind K = CXCursor_NotImplemented; 500 enum CXCursorKind *ck = &K; 501 VisitorData Data; 502 503 /* Perform some simple filtering. */ 504 if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; 505 else if (!strcmp(filter, "none")) K = (enum CXCursorKind) ~0; 506 else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl; 507 else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl; 508 else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl; 509 else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl; 510 else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl; 511 else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor; 512 else { 513 fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter); 514 return 1; 515 } 516 517 Data.TU = TU; 518 Data.Filter = ck; 519 clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data); 520 } 521 522 if (PV) 523 PV(TU); 524 525 PrintDiagnostics(TU); 526 clang_disposeTranslationUnit(TU); 527 return 0; 528} 529 530int perform_test_load_tu(const char *file, const char *filter, 531 const char *prefix, CXCursorVisitor Visitor, 532 PostVisitTU PV) { 533 CXIndex Idx; 534 CXTranslationUnit TU; 535 int result; 536 Idx = clang_createIndex(/* excludeDeclsFromPCH */ 537 !strcmp(filter, "local") ? 1 : 0); 538 539 if (!CreateTranslationUnit(Idx, file, &TU)) { 540 clang_disposeIndex(Idx); 541 return 1; 542 } 543 544 result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV); 545 clang_disposeIndex(Idx); 546 return result; 547} 548 549int perform_test_load_source(int argc, const char **argv, 550 const char *filter, CXCursorVisitor Visitor, 551 PostVisitTU PV) { 552 const char *UseExternalASTs = 553 getenv("CINDEXTEST_USE_EXTERNAL_AST_GENERATION"); 554 CXIndex Idx; 555 CXTranslationUnit TU; 556 struct CXUnsavedFile *unsaved_files = 0; 557 int num_unsaved_files = 0; 558 int result; 559 560 Idx = clang_createIndex(/* excludeDeclsFromPCH */ 561 !strcmp(filter, "local") ? 1 : 0); 562 563 if (UseExternalASTs && strlen(UseExternalASTs)) 564 clang_setUseExternalASTGeneration(Idx, 1); 565 566 if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { 567 clang_disposeIndex(Idx); 568 return -1; 569 } 570 571 TU = clang_createTranslationUnitFromSourceFile(Idx, 0, 572 argc - num_unsaved_files, 573 argv + num_unsaved_files, 574 num_unsaved_files, 575 unsaved_files); 576 if (!TU) { 577 fprintf(stderr, "Unable to load translation unit!\n"); 578 clang_disposeIndex(Idx); 579 return 1; 580 } 581 582 result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV); 583 free_remapped_files(unsaved_files, num_unsaved_files); 584 clang_disposeIndex(Idx); 585 return result; 586} 587 588/******************************************************************************/ 589/* Logic for testing clang_getCursor(). */ 590/******************************************************************************/ 591 592static void print_cursor_file_scan(CXCursor cursor, 593 unsigned start_line, unsigned start_col, 594 unsigned end_line, unsigned end_col, 595 const char *prefix) { 596 printf("// %s: ", FileCheckPrefix); 597 if (prefix) 598 printf("-%s", prefix); 599 PrintExtent(stdout, start_line, start_col, end_line, end_col); 600 printf(" "); 601 PrintCursor(cursor); 602 printf("\n"); 603} 604 605static int perform_file_scan(const char *ast_file, const char *source_file, 606 const char *prefix) { 607 CXIndex Idx; 608 CXTranslationUnit TU; 609 FILE *fp; 610 CXCursor prevCursor = clang_getNullCursor(); 611 CXFile file; 612 unsigned line = 1, col = 1; 613 unsigned start_line = 1, start_col = 1; 614 615 if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1))) { 616 fprintf(stderr, "Could not create Index\n"); 617 return 1; 618 } 619 620 if (!CreateTranslationUnit(Idx, ast_file, &TU)) 621 return 1; 622 623 if ((fp = fopen(source_file, "r")) == NULL) { 624 fprintf(stderr, "Could not open '%s'\n", source_file); 625 return 1; 626 } 627 628 file = clang_getFile(TU, source_file); 629 for (;;) { 630 CXCursor cursor; 631 int c = fgetc(fp); 632 633 if (c == '\n') { 634 ++line; 635 col = 1; 636 } else 637 ++col; 638 639 /* Check the cursor at this position, and dump the previous one if we have 640 * found something new. 641 */ 642 cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col)); 643 if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) && 644 prevCursor.kind != CXCursor_InvalidFile) { 645 print_cursor_file_scan(prevCursor, start_line, start_col, 646 line, col, prefix); 647 start_line = line; 648 start_col = col; 649 } 650 if (c == EOF) 651 break; 652 653 prevCursor = cursor; 654 } 655 656 fclose(fp); 657 return 0; 658} 659 660/******************************************************************************/ 661/* Logic for testing clang_codeComplete(). */ 662/******************************************************************************/ 663 664/* Parse file:line:column from the input string. Returns 0 on success, non-zero 665 on failure. If successful, the pointer *filename will contain newly-allocated 666 memory (that will be owned by the caller) to store the file name. */ 667int parse_file_line_column(const char *input, char **filename, unsigned *line, 668 unsigned *column, unsigned *second_line, 669 unsigned *second_column) { 670 /* Find the second colon. */ 671 const char *last_colon = strrchr(input, ':'); 672 unsigned values[4], i; 673 unsigned num_values = (second_line && second_column)? 4 : 2; 674 675 char *endptr = 0; 676 if (!last_colon || last_colon == input) { 677 if (num_values == 4) 678 fprintf(stderr, "could not parse filename:line:column:line:column in " 679 "'%s'\n", input); 680 else 681 fprintf(stderr, "could not parse filename:line:column in '%s'\n", input); 682 return 1; 683 } 684 685 for (i = 0; i != num_values; ++i) { 686 const char *prev_colon; 687 688 /* Parse the next line or column. */ 689 values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10); 690 if (*endptr != 0 && *endptr != ':') { 691 fprintf(stderr, "could not parse %s in '%s'\n", 692 (i % 2 ? "column" : "line"), input); 693 return 1; 694 } 695 696 if (i + 1 == num_values) 697 break; 698 699 /* Find the previous colon. */ 700 prev_colon = last_colon - 1; 701 while (prev_colon != input && *prev_colon != ':') 702 --prev_colon; 703 if (prev_colon == input) { 704 fprintf(stderr, "could not parse %s in '%s'\n", 705 (i % 2 == 0? "column" : "line"), input); 706 return 1; 707 } 708 709 last_colon = prev_colon; 710 } 711 712 *line = values[0]; 713 *column = values[1]; 714 715 if (second_line && second_column) { 716 *second_line = values[2]; 717 *second_column = values[3]; 718 } 719 720 /* Copy the file name. */ 721 *filename = (char*)malloc(last_colon - input + 1); 722 memcpy(*filename, input, last_colon - input); 723 (*filename)[last_colon - input] = 0; 724 return 0; 725} 726 727const char * 728clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) { 729 switch (Kind) { 730 case CXCompletionChunk_Optional: return "Optional"; 731 case CXCompletionChunk_TypedText: return "TypedText"; 732 case CXCompletionChunk_Text: return "Text"; 733 case CXCompletionChunk_Placeholder: return "Placeholder"; 734 case CXCompletionChunk_Informative: return "Informative"; 735 case CXCompletionChunk_CurrentParameter: return "CurrentParameter"; 736 case CXCompletionChunk_LeftParen: return "LeftParen"; 737 case CXCompletionChunk_RightParen: return "RightParen"; 738 case CXCompletionChunk_LeftBracket: return "LeftBracket"; 739 case CXCompletionChunk_RightBracket: return "RightBracket"; 740 case CXCompletionChunk_LeftBrace: return "LeftBrace"; 741 case CXCompletionChunk_RightBrace: return "RightBrace"; 742 case CXCompletionChunk_LeftAngle: return "LeftAngle"; 743 case CXCompletionChunk_RightAngle: return "RightAngle"; 744 case CXCompletionChunk_Comma: return "Comma"; 745 case CXCompletionChunk_ResultType: return "ResultType"; 746 case CXCompletionChunk_Colon: return "Colon"; 747 case CXCompletionChunk_SemiColon: return "SemiColon"; 748 case CXCompletionChunk_Equal: return "Equal"; 749 case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace"; 750 case CXCompletionChunk_VerticalSpace: return "VerticalSpace"; 751 } 752 753 return "Unknown"; 754} 755 756void print_completion_string(CXCompletionString completion_string, FILE *file) { 757 int I, N; 758 759 N = clang_getNumCompletionChunks(completion_string); 760 for (I = 0; I != N; ++I) { 761 CXString text; 762 const char *cstr; 763 enum CXCompletionChunkKind Kind 764 = clang_getCompletionChunkKind(completion_string, I); 765 766 if (Kind == CXCompletionChunk_Optional) { 767 fprintf(file, "{Optional "); 768 print_completion_string( 769 clang_getCompletionChunkCompletionString(completion_string, I), 770 file); 771 fprintf(file, "}"); 772 continue; 773 } 774 775 text = clang_getCompletionChunkText(completion_string, I); 776 cstr = clang_getCString(text); 777 fprintf(file, "{%s %s}", 778 clang_getCompletionChunkKindSpelling(Kind), 779 cstr ? cstr : ""); 780 clang_disposeString(text); 781 } 782 783} 784 785void print_completion_result(CXCompletionResult *completion_result, 786 CXClientData client_data) { 787 FILE *file = (FILE *)client_data; 788 CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind); 789 790 fprintf(file, "%s:", clang_getCString(ks)); 791 clang_disposeString(ks); 792 793 print_completion_string(completion_result->CompletionString, file); 794 fprintf(file, "\n"); 795} 796 797int perform_code_completion(int argc, const char **argv) { 798 const char *input = argv[1]; 799 char *filename = 0; 800 unsigned line; 801 unsigned column; 802 CXIndex CIdx; 803 int errorCode; 804 struct CXUnsavedFile *unsaved_files = 0; 805 int num_unsaved_files = 0; 806 CXCodeCompleteResults *results = 0; 807 808 input += strlen("-code-completion-at="); 809 if ((errorCode = parse_file_line_column(input, &filename, &line, &column, 810 0, 0))) 811 return errorCode; 812 813 if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) 814 return -1; 815 816 CIdx = clang_createIndex(0); 817 results = clang_codeComplete(CIdx, 818 argv[argc - 1], argc - num_unsaved_files - 3, 819 argv + num_unsaved_files + 2, 820 num_unsaved_files, unsaved_files, 821 filename, line, column); 822 823 if (results) { 824 unsigned i, n = results->NumResults; 825 for (i = 0; i != n; ++i) 826 print_completion_result(results->Results + i, stdout); 827 n = clang_codeCompleteGetNumDiagnostics(results); 828 for (i = 0; i != n; ++i) { 829 CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); 830 PrintDiagnostic(diag); 831 clang_disposeDiagnostic(diag); 832 } 833 clang_disposeCodeCompleteResults(results); 834 } 835 836 clang_disposeIndex(CIdx); 837 free(filename); 838 839 free_remapped_files(unsaved_files, num_unsaved_files); 840 841 return 0; 842} 843 844typedef struct { 845 char *filename; 846 unsigned line; 847 unsigned column; 848} CursorSourceLocation; 849 850int inspect_cursor_at(int argc, const char **argv) { 851 CXIndex CIdx; 852 int errorCode; 853 struct CXUnsavedFile *unsaved_files = 0; 854 int num_unsaved_files = 0; 855 CXTranslationUnit TU; 856 CXCursor Cursor; 857 CursorSourceLocation *Locations = 0; 858 unsigned NumLocations = 0, Loc; 859 860 /* Count the number of locations. */ 861 while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1]) 862 ++NumLocations; 863 864 /* Parse the locations. */ 865 assert(NumLocations > 0 && "Unable to count locations?"); 866 Locations = (CursorSourceLocation *)malloc( 867 NumLocations * sizeof(CursorSourceLocation)); 868 for (Loc = 0; Loc < NumLocations; ++Loc) { 869 const char *input = argv[Loc + 1] + strlen("-cursor-at="); 870 if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, 871 &Locations[Loc].line, 872 &Locations[Loc].column, 0, 0))) 873 return errorCode; 874 } 875 876 if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, 877 &num_unsaved_files)) 878 return -1; 879 880 CIdx = clang_createIndex(0); 881 TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1], 882 argc - num_unsaved_files - 2 - NumLocations, 883 argv + num_unsaved_files + 1 + NumLocations, 884 num_unsaved_files, 885 unsaved_files); 886 if (!TU) { 887 fprintf(stderr, "unable to parse input\n"); 888 return -1; 889 } 890 891 for (Loc = 0; Loc < NumLocations; ++Loc) { 892 CXFile file = clang_getFile(TU, Locations[Loc].filename); 893 if (!file) 894 continue; 895 896 Cursor = clang_getCursor(TU, 897 clang_getLocation(TU, file, Locations[Loc].line, 898 Locations[Loc].column)); 899 PrintCursor(Cursor); 900 printf("\n"); 901 free(Locations[Loc].filename); 902 } 903 904 PrintDiagnostics(TU); 905 clang_disposeTranslationUnit(TU); 906 clang_disposeIndex(CIdx); 907 free(Locations); 908 free_remapped_files(unsaved_files, num_unsaved_files); 909 return 0; 910} 911 912int perform_token_annotation(int argc, const char **argv) { 913 const char *input = argv[1]; 914 char *filename = 0; 915 unsigned line, second_line; 916 unsigned column, second_column; 917 CXIndex CIdx; 918 CXTranslationUnit TU = 0; 919 int errorCode; 920 struct CXUnsavedFile *unsaved_files = 0; 921 int num_unsaved_files = 0; 922 CXToken *tokens; 923 unsigned num_tokens; 924 CXSourceRange range; 925 CXSourceLocation startLoc, endLoc; 926 CXFile file = 0; 927 CXCursor *cursors = 0; 928 unsigned i; 929 930 input += strlen("-test-annotate-tokens="); 931 if ((errorCode = parse_file_line_column(input, &filename, &line, &column, 932 &second_line, &second_column))) 933 return errorCode; 934 935 if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) 936 return -1; 937 938 CIdx = clang_createIndex(0); 939 TU = clang_createTranslationUnitFromSourceFile(CIdx, argv[argc - 1], 940 argc - num_unsaved_files - 3, 941 argv + num_unsaved_files + 2, 942 num_unsaved_files, 943 unsaved_files); 944 if (!TU) { 945 fprintf(stderr, "unable to parse input\n"); 946 clang_disposeIndex(CIdx); 947 free(filename); 948 free_remapped_files(unsaved_files, num_unsaved_files); 949 return -1; 950 } 951 errorCode = 0; 952 953 file = clang_getFile(TU, filename); 954 if (!file) { 955 fprintf(stderr, "file %s is not in this translation unit\n", filename); 956 errorCode = -1; 957 goto teardown; 958 } 959 960 startLoc = clang_getLocation(TU, file, line, column); 961 if (clang_equalLocations(clang_getNullLocation(), startLoc)) { 962 fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line, 963 column); 964 errorCode = -1; 965 goto teardown; 966 } 967 968 endLoc = clang_getLocation(TU, file, second_line, second_column); 969 if (clang_equalLocations(clang_getNullLocation(), endLoc)) { 970 fprintf(stderr, "invalid source location %s:%d:%d\n", filename, 971 second_line, second_column); 972 errorCode = -1; 973 goto teardown; 974 } 975 976 range = clang_getRange(startLoc, endLoc); 977 clang_tokenize(TU, range, &tokens, &num_tokens); 978 cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor)); 979 clang_annotateTokens(TU, tokens, num_tokens, cursors); 980 for (i = 0; i != num_tokens; ++i) { 981 const char *kind = "<unknown>"; 982 CXString spelling = clang_getTokenSpelling(TU, tokens[i]); 983 CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]); 984 unsigned start_line, start_column, end_line, end_column; 985 986 switch (clang_getTokenKind(tokens[i])) { 987 case CXToken_Punctuation: kind = "Punctuation"; break; 988 case CXToken_Keyword: kind = "Keyword"; break; 989 case CXToken_Identifier: kind = "Identifier"; break; 990 case CXToken_Literal: kind = "Literal"; break; 991 case CXToken_Comment: kind = "Comment"; break; 992 } 993 clang_getInstantiationLocation(clang_getRangeStart(extent), 994 0, &start_line, &start_column, 0); 995 clang_getInstantiationLocation(clang_getRangeEnd(extent), 996 0, &end_line, &end_column, 0); 997 printf("%s: \"%s\" ", kind, clang_getCString(spelling)); 998 PrintExtent(stdout, start_line, start_column, end_line, end_column); 999 if (!clang_isInvalid(cursors[i].kind)) { 1000 printf(" "); 1001 PrintCursor(cursors[i]); 1002 } 1003 printf("\n"); 1004 } 1005 free(cursors); 1006 1007 teardown: 1008 PrintDiagnostics(TU); 1009 clang_disposeTranslationUnit(TU); 1010 clang_disposeIndex(CIdx); 1011 free(filename); 1012 free_remapped_files(unsaved_files, num_unsaved_files); 1013 return errorCode; 1014} 1015 1016/******************************************************************************/ 1017/* Command line processing. */ 1018/******************************************************************************/ 1019 1020static CXCursorVisitor GetVisitor(const char *s) { 1021 if (s[0] == '\0') 1022 return FilteredPrintingVisitor; 1023 if (strcmp(s, "-usrs") == 0) 1024 return USRVisitor; 1025 return NULL; 1026} 1027 1028static void print_usage(void) { 1029 fprintf(stderr, 1030 "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n" 1031 " c-index-test -cursor-at=<site> <compiler arguments>\n" 1032 " c-index-test -test-file-scan <AST file> <source file> " 1033 "[FileCheck prefix]\n" 1034 " c-index-test -test-load-tu <AST file> <symbol filter> " 1035 "[FileCheck prefix]\n" 1036 " c-index-test -test-load-tu-usrs <AST file> <symbol filter> " 1037 "[FileCheck prefix]\n" 1038 " c-index-test -test-load-source <symbol filter> {<args>}*\n" 1039 " c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n"); 1040 fprintf(stderr, 1041 " c-index-test -test-annotate-tokens=<range> {<args>}*\n" 1042 " c-index-test -test-inclusion-stack-source {<args>}*\n" 1043 " c-index-test -test-inclusion-stack-tu <AST file>\n\n" 1044 " <symbol filter> values:\n%s", 1045 " all - load all symbols, including those from PCH\n" 1046 " local - load all symbols except those in PCH\n" 1047 " category - only load ObjC categories (non-PCH)\n" 1048 " interface - only load ObjC interfaces (non-PCH)\n" 1049 " protocol - only load ObjC protocols (non-PCH)\n" 1050 " function - only load functions (non-PCH)\n" 1051 " typedef - only load typdefs (non-PCH)\n" 1052 " scan-function - scan function bodies (non-PCH)\n\n"); 1053} 1054 1055int main(int argc, const char **argv) { 1056 if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) 1057 return perform_code_completion(argc, argv); 1058 if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1]) 1059 return inspect_cursor_at(argc, argv); 1060 else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) { 1061 CXCursorVisitor I = GetVisitor(argv[1] + 13); 1062 if (I) 1063 return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I, 1064 NULL); 1065 } 1066 else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) { 1067 CXCursorVisitor I = GetVisitor(argv[1] + 17); 1068 if (I) 1069 return perform_test_load_source(argc - 3, argv + 3, argv[2], I, NULL); 1070 } 1071 else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) 1072 return perform_file_scan(argv[2], argv[3], 1073 argc >= 5 ? argv[4] : 0); 1074 else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1]) 1075 return perform_token_annotation(argc, argv); 1076 else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0) 1077 return perform_test_load_source(argc - 2, argv + 2, "all", NULL, 1078 PrintInclusionStack); 1079 else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0) 1080 return perform_test_load_tu(argv[2], "all", NULL, NULL, 1081 PrintInclusionStack); 1082 1083 print_usage(); 1084 return 1; 1085} 1086