|
13 | 13 | #include "Irony.h" |
14 | 14 |
|
15 | 15 | #include "support/iomanip_quoted.h" |
| 16 | +#include "support/Sexp.h" |
| 17 | +using sexp::repr; |
16 | 18 |
|
17 | 19 | #include <algorithm> |
18 | 20 | #include <array> |
@@ -342,3 +344,289 @@ void Irony::complete(const std::string &file, |
342 | 344 | std::cout << ")\n"; |
343 | 345 | } |
344 | 346 | } |
| 347 | + |
| 348 | +unsigned getOffset(CXSourceLocation loc) { |
| 349 | + unsigned offset, line, col; |
| 350 | + clang_getSpellingLocation(loc, nullptr, &line, &col, &offset); |
| 351 | + return offset; |
| 352 | +} |
| 353 | + |
| 354 | +unsigned getOffset(CXCursor cursor) { |
| 355 | + CXSourceLocation sloc = clang_getCursorLocation(cursor); |
| 356 | + return getOffset(sloc); |
| 357 | +} |
| 358 | + |
| 359 | +CXCursor cursorParent(CXCursor cursor) { |
| 360 | + CXCursor parent = clang_getCursorLexicalParent(cursor); |
| 361 | + if (clang_isInvalid(clang_getCursorKind(parent))) { |
| 362 | + parent = clang_getCursorSemanticParent(cursor); |
| 363 | + } |
| 364 | + return parent; |
| 365 | +} |
| 366 | + |
| 367 | +/// Whether cursor is suitable for having its information being shown |
| 368 | +/// to the user. |
| 369 | +bool isCursorPrintable(CXCursor cursor) { |
| 370 | + CXType type = clang_getCursorType(cursor); |
| 371 | + bool valid = !clang_isInvalid(cursor.kind) && |
| 372 | + !clang_isUnexposed(cursor.kind) && |
| 373 | + !clang_isTranslationUnit(cursor.kind); |
| 374 | + // Unexposed types are allowed (common in C++) |
| 375 | + bool validType = type.kind != CXType_Invalid; |
| 376 | + bool isLiteral = cursor.kind == CXCursor_IntegerLiteral || |
| 377 | + cursor.kind == CXCursor_FloatingLiteral || |
| 378 | + cursor.kind == CXCursor_ImaginaryLiteral || |
| 379 | + cursor.kind == CXCursor_StringLiteral || |
| 380 | + cursor.kind == CXCursor_CharacterLiteral; |
| 381 | + return valid && validType && !isLiteral; |
| 382 | +} |
| 383 | + |
| 384 | +CXCursor getToplevelCursor(CXTranslationUnit tu, CXSourceLocation sloc) { |
| 385 | + CXCursor cursor = clang_getCursor(tu, sloc); |
| 386 | + CXCursorKind kind = clang_getCursorKind(cursor); |
| 387 | + while (!clang_isTranslationUnit(kind) && !clang_isInvalid(kind)) { |
| 388 | + CXCursor parent = clang_getCursorLexicalParent(cursor); |
| 389 | + if (clang_isInvalid(clang_getCursorKind(parent))) { |
| 390 | + parent = clang_getCursorSemanticParent(cursor); |
| 391 | + } |
| 392 | + if (clang_isTranslationUnit(clang_getCursorKind(parent))) { |
| 393 | + break; |
| 394 | + } |
| 395 | + cursor = parent; |
| 396 | + kind = clang_getCursorKind(cursor); |
| 397 | + } |
| 398 | + return cursor; |
| 399 | +} |
| 400 | + |
| 401 | +CXCursor getCursorFirstChild(CXCursor cursor) { |
| 402 | + CXCursor first_child = clang_getNullCursor(); |
| 403 | + auto visitor = [](CXCursor current, CXCursor, CXClientData client_data) |
| 404 | + -> CXChildVisitResult { |
| 405 | + *static_cast<CXCursor *>(client_data) = current; |
| 406 | + return CXChildVisit_Break; |
| 407 | + }; |
| 408 | + clang_visitChildren(cursor, visitor, &first_child); |
| 409 | + return first_child; |
| 410 | +} |
| 411 | + |
| 412 | +/// Wrapper around clang_getCursorExtent |
| 413 | +struct Range { |
| 414 | + Range(CXCursor cursor) |
| 415 | + : range(clang_getCursorExtent(cursor)) |
| 416 | + , start(clang_getRangeStart(range)) |
| 417 | + , end(clang_getRangeEnd(range)) |
| 418 | + , start_offset(getOffset(start)) |
| 419 | + , end_offset(getOffset(end)) { |
| 420 | + } |
| 421 | + bool contains(unsigned offset) { |
| 422 | + return start_offset <= offset && offset < end_offset; |
| 423 | + } |
| 424 | + bool contains(unsigned a, unsigned b) { |
| 425 | + return start_offset <= std::min(a, b) && std::max(a, b) <= end_offset; |
| 426 | + } |
| 427 | + CXSourceRange range; |
| 428 | + CXSourceLocation start, end; |
| 429 | + unsigned start_offset, end_offset; |
| 430 | +}; |
| 431 | + |
| 432 | +struct exprtypeAlist { |
| 433 | + exprtypeAlist(const CXCursor &cursor) : cursor(cursor) { |
| 434 | + } |
| 435 | + const CXCursor &cursor; |
| 436 | +}; |
| 437 | + |
| 438 | +struct typeAlist { |
| 439 | + typeAlist(const CXType &type) : type(type) { |
| 440 | + } |
| 441 | + const CXType &type; |
| 442 | +}; |
| 443 | + |
| 444 | +std::ostream &operator<<(std::ostream &out, const typeAlist &proxy) { |
| 445 | + CXType type = proxy.type; |
| 446 | + if (type.kind == CXType_Invalid) |
| 447 | + return out << "nil"; |
| 448 | + out << "("; |
| 449 | + if (type.kind != CXType_Unexposed) |
| 450 | + out << sexp::alistEntry("kind", repr(type.kind)); |
| 451 | + out << sexp::alistEntry("spelling", repr(clang_getTypeSpelling(type))); |
| 452 | + |
| 453 | + int numargs = clang_getNumArgTypes(type); |
| 454 | + if (numargs >= 0) { |
| 455 | + out << " (args"; |
| 456 | + for (int i = 0; i < numargs; ++i) { |
| 457 | + CXType arg = clang_getArgType(type, i); |
| 458 | + out << sexp::alistEntry("type", typeAlist(arg)); |
| 459 | + } |
| 460 | + out << ")"; |
| 461 | + } |
| 462 | + |
| 463 | + if (clang_isFunctionTypeVariadic(type)) { |
| 464 | + out << " (variadic . t)"; |
| 465 | + } |
| 466 | + |
| 467 | + return out << ")"; |
| 468 | +} |
| 469 | + |
| 470 | +std::ostream& operator<<(std::ostream& out, const exprtypeAlist &proxy) { |
| 471 | + CXCursor cursor = proxy.cursor; |
| 472 | + |
| 473 | + /* Examples of what might be printed (from test.cc) |
| 474 | +
|
| 475 | +FIXME More robust examples and testing |
| 476 | +
|
| 477 | +(exprtype (bounds 832 836) (kind . CallExpr) (type (kind . Dependent) (spelling . "<dependent type>")) (call (bounds 832 833) (kind . OverloadedDeclRef) (spelling . "h") (overloaded (completion (comment . "docstring for h(X).") (priority . 50) (chunks (ResultType . "X") "h(" (Placeholder . "const X &x")")")) (completion (comment . "docstring for h<T>") (priority . 50) (chunks (ResultType . "T") "h(" (Placeholder . "const T &x")")")))) (args (834 . 835))) |
| 478 | +
|
| 479 | +(exprtype (bounds 834 835) (kind . DeclRefExpr) (spelling . "t") (type (spelling . "T")) (completion (priority . 50) (chunks (ResultType . "T") "t"))) |
| 480 | +
|
| 481 | +(exprtype (bounds 892 902) (kind . ParmDecl) (spelling . "y") (type (kind . LValueReference) (spelling . "const Y &")) (completion (priority . 50) (chunks (ResultType . "const Y &") "y"))) |
| 482 | +
|
| 483 | +(exprtype (bounds 1287 1291) (kind . CallExpr) (spelling . "f") (type (kind . Void) (spelling . "void")) (completion (comment . "docstring for f") (priority . 50) (chunks (ResultType . "void") "f(" (Placeholder . "string x") ")")) (call (bounds 1287 1288) (kind . DeclRefExpr) (spelling . "f") (type (kind . FunctionProto) (spelling . "void (string)") (args (type (kind . Typedef) (spelling . "string")))) (completion (comment . "docstring for f") (priority . 50) (chunks (ResultType . "void") "f(" (Placeholder . "string x") ")")) (comment . "docstring for f")) (args (1289 . 1290)) (comment . "docstring for f")) |
| 484 | +
|
| 485 | +(exprtype (bounds 1289 1290) (kind . DeclRefExpr) (spelling . "x") (type (kind . Typedef) (spelling . "string")) (completion (priority . 50) (chunks (ResultType . "string") "x"))) |
| 486 | +
|
| 487 | +NOTE: no arguments here despite this being a CallExpr |
| 488 | +(exprtype (bounds 1205 1227) (kind . CallExpr) (spelling . "vector") (type (spelling . "vector<float>")) (completion (parent . "std::__1::vector") (priority . 50) (chunks "vector(" (Placeholder . "size_type __n") "," (Placeholder . "const_reference __x") ")")) (call (bounds 1205 1211) (kind . TemplateRef) (spelling . "vector") (completion (parent . "std::__1") (priority . 50) (chunks "vector<" (Placeholder . "class _Tp") (Optional . (completion (priority . 0) (chunks "," (Placeholder . "class _Allocator")))) ">")))) |
| 489 | +
|
| 490 | + */ |
| 491 | + |
| 492 | + Range range{cursor}; |
| 493 | + CXType type = clang_getCursorType(cursor); |
| 494 | + |
| 495 | + // This is a long alist of essentially arbitrary elements. |
| 496 | + if (!clang_isInvalid(cursor.kind)) |
| 497 | + out << " (bounds " << range.start_offset << " " << range.end_offset << ")"; |
| 498 | + out << sexp::alistEntry("kind", repr(cursor.kind)) |
| 499 | + << sexp::alistEntry("spelling", repr(clang_getCursorSpelling(cursor))) |
| 500 | + << sexp::alistEntry("type", typeAlist(type)); |
| 501 | + |
| 502 | + // Find a suitable completion string |
| 503 | + { |
| 504 | + CXCompletionString completion = |
| 505 | + clang_getCursorCompletionString(clang_getCursorDefinition(cursor)); |
| 506 | + if (!completion) |
| 507 | + completion = |
| 508 | + clang_getCursorCompletionString(clang_getCursorReferenced(cursor)); |
| 509 | + if (!completion) |
| 510 | + completion = clang_getCursorCompletionString(cursor); |
| 511 | + if (completion) |
| 512 | + out << " " << sexp::completion(completion); |
| 513 | + } |
| 514 | + |
| 515 | + // If cursor is a call to a function, we show the type of the |
| 516 | + // function as well. Then the type is the return type. |
| 517 | + if (cursor.kind == CXCursor_CallExpr) { |
| 518 | + // Find the first child, which should be the function |
| 519 | + // FIXME Is this true for obj-c? I'm not familiar with it. |
| 520 | + CXCursor funref = cursor; |
| 521 | + for (CXCursor cursor_child = funref; !clang_isInvalid(cursor_child.kind); |
| 522 | + funref = cursor_child, cursor_child = getCursorFirstChild(funref)) |
| 523 | + ; |
| 524 | + |
| 525 | + out << " (call" << exprtypeAlist(funref) << ")"; |
| 526 | + } |
| 527 | + |
| 528 | + // FIXME Constructor arguments don't seem to appear here. |
| 529 | + // Are they of kind CallExpr? |
| 530 | + int numargs = clang_Cursor_getNumArguments(cursor); |
| 531 | + if (numargs >= 0) { |
| 532 | + out << " (args"; |
| 533 | + for (int i = 0; i < numargs; ++i) { |
| 534 | + CXCursor arg = clang_Cursor_getArgument(cursor, i); |
| 535 | + Range range{arg}; |
| 536 | + out << " (" << range.start_offset << " . " << range.end_offset << ")"; |
| 537 | + } |
| 538 | + out << ")"; |
| 539 | + } |
| 540 | + |
| 541 | + out << sexp::alistEntry("comment", sexp::comment(cursor)); |
| 542 | + |
| 543 | + // Overloaded decl ref |
| 544 | + { |
| 545 | + CXCursor ref = cursor; |
| 546 | + int num_overloaded = clang_getNumOverloadedDecls(ref); |
| 547 | + if (!num_overloaded) { |
| 548 | + ref = clang_getCursorReferenced(cursor); |
| 549 | + num_overloaded = clang_getNumOverloadedDecls(ref); |
| 550 | + } |
| 551 | + if (num_overloaded > 0) { |
| 552 | + out << " (overloaded"; |
| 553 | + for (int i = 0; i < num_overloaded; ++i) { |
| 554 | + CXCursor decl = clang_getOverloadedDecl(ref, i); |
| 555 | + out << " " << sexp::completion(decl); |
| 556 | + } |
| 557 | + out << ")"; |
| 558 | + } |
| 559 | + } |
| 560 | + |
| 561 | + return out; |
| 562 | +} |
| 563 | + |
| 564 | +void Irony::exprtype(const std::string &file, |
| 565 | + unsigned start_offset, |
| 566 | + unsigned end_offset, |
| 567 | + const std::vector<std::string> &flags, |
| 568 | + const std::vector<CXUnsavedFile> &unsavedFiles) { |
| 569 | + // NOTE Duplicate from complete(..) above |
| 570 | + TUManager::Settings settings; |
| 571 | + settings.parseTUOptions |= CXTranslationUnit_CacheCompletionResults; |
| 572 | +#if HAS_BRIEF_COMMENTS_IN_COMPLETION |
| 573 | + settings.parseTUOptions |= |
| 574 | + CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; |
| 575 | +#endif |
| 576 | + (void)tuManager_.registerSettings(settings); |
| 577 | + CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, unsavedFiles); |
| 578 | + |
| 579 | + if (!tu) { |
| 580 | + std::cout << "nil\n"; |
| 581 | + return; |
| 582 | + } |
| 583 | + |
| 584 | + // if (true || debug_) { |
| 585 | + // std::cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; |
| 586 | + // dumpDiagnostics(tu); |
| 587 | + // std::cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"; |
| 588 | + // } |
| 589 | + |
| 590 | + if (end_offset < start_offset) |
| 591 | + end_offset = start_offset; |
| 592 | + |
| 593 | + CXFile cxfile = clang_getFile(tu, file.c_str()); |
| 594 | + CXSourceLocation |
| 595 | + start_loc = clang_getLocationForOffset(tu, cxfile, start_offset); |
| 596 | + |
| 597 | + CXCursor cursor = clang_getCursor(tu, start_loc); |
| 598 | + |
| 599 | + /* The lexical parent of a cursor doesn't seem to be its parent in |
| 600 | + the AST, which is what we want. So go to the toplevel cursor, just |
| 601 | + below the translation unit, and walk down looking for printable |
| 602 | + cursors covering the region. */ |
| 603 | + if (!isCursorPrintable(cursor) || |
| 604 | + !Range(cursor).contains(start_offset, end_offset)) { |
| 605 | + cursor = getToplevelCursor(tu, start_loc); |
| 606 | + struct data_t { |
| 607 | + const unsigned start_offset, end_offset; |
| 608 | + CXCursor cursor; |
| 609 | + } data{start_offset, end_offset, clang_getNullCursor()}; |
| 610 | + auto visitor = [](CXCursor current, CXCursor, CXClientData client_data) |
| 611 | + -> CXChildVisitResult { |
| 612 | + Range range{current}; |
| 613 | + data_t &data = *static_cast<data_t *>(client_data); |
| 614 | + if (range.contains(data.start_offset, data.end_offset)) { |
| 615 | + if (isCursorPrintable(current)) |
| 616 | + data.cursor = current; |
| 617 | + return CXChildVisit_Recurse; |
| 618 | + } |
| 619 | + return range.start_offset >= data.end_offset ? CXChildVisit_Break |
| 620 | + : CXChildVisit_Continue; |
| 621 | + }; |
| 622 | + clang_visitChildren(cursor, visitor, &data); |
| 623 | + if (!clang_Cursor_isNull(data.cursor)) |
| 624 | + cursor = data.cursor; |
| 625 | + } |
| 626 | + |
| 627 | + std::cout << "(exprtype"; |
| 628 | + unsigned numdiag = clang_getNumDiagnostics(tu); |
| 629 | + if (numdiag) |
| 630 | + std::cout << sexp::alistEntry("diagnostics", numdiag); |
| 631 | + std::cout << exprtypeAlist(cursor) << ")" << std::endl; |
| 632 | +} |
0 commit comments