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