CIndexCodeCompletion.cpp revision 39b49bcaaddb1049234fca9500c0ac02c088e23d
1//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the Clang-C Source Indexing library hooks for 11// code completion. 12// 13//===----------------------------------------------------------------------===// 14 15#include "CIndexer.h" 16#include "CXTranslationUnit.h" 17#include "CXString.h" 18#include "CIndexDiagnostic.h" 19#include "clang/Basic/SourceManager.h" 20#include "clang/Basic/FileManager.h" 21#include "clang/Frontend/ASTUnit.h" 22#include "clang/Frontend/CompilerInstance.h" 23#include "clang/Frontend/FrontendDiagnostic.h" 24#include "clang/Sema/CodeCompleteConsumer.h" 25#include "llvm/ADT/SmallString.h" 26#include "llvm/ADT/StringExtras.h" 27#include "llvm/Support/CrashRecoveryContext.h" 28#include "llvm/Support/MemoryBuffer.h" 29#include "llvm/Support/Timer.h" 30#include "llvm/Support/raw_ostream.h" 31#include "llvm/System/Program.h" 32#include <cstdlib> 33#include <cstdio> 34 35 36#ifdef UDP_CODE_COMPLETION_LOGGER 37#include "clang/Basic/Version.h" 38#include <arpa/inet.h> 39#include <sys/socket.h> 40#include <sys/types.h> 41#include <unistd.h> 42#endif 43 44using namespace clang; 45using namespace clang::cxstring; 46 47namespace { 48 /// \brief Stored representation of a completion string. 49 /// 50 /// This is the representation behind a CXCompletionString. 51 class CXStoredCodeCompletionString : public CodeCompletionString { 52 unsigned Priority; 53 CXAvailabilityKind Availability; 54 55 public: 56 CXStoredCodeCompletionString(unsigned Priority, 57 CXAvailabilityKind Availability) 58 : Priority(Priority), Availability(Availability) { } 59 60 unsigned getPriority() const { return Priority; } 61 CXAvailabilityKind getAvailability() const { return Availability; } 62 }; 63} 64 65extern "C" { 66 67enum CXCompletionChunkKind 68clang_getCompletionChunkKind(CXCompletionString completion_string, 69 unsigned chunk_number) { 70 CXStoredCodeCompletionString *CCStr 71 = (CXStoredCodeCompletionString *)completion_string; 72 if (!CCStr || chunk_number >= CCStr->size()) 73 return CXCompletionChunk_Text; 74 75 switch ((*CCStr)[chunk_number].Kind) { 76 case CodeCompletionString::CK_TypedText: 77 return CXCompletionChunk_TypedText; 78 case CodeCompletionString::CK_Text: 79 return CXCompletionChunk_Text; 80 case CodeCompletionString::CK_Optional: 81 return CXCompletionChunk_Optional; 82 case CodeCompletionString::CK_Placeholder: 83 return CXCompletionChunk_Placeholder; 84 case CodeCompletionString::CK_Informative: 85 return CXCompletionChunk_Informative; 86 case CodeCompletionString::CK_ResultType: 87 return CXCompletionChunk_ResultType; 88 case CodeCompletionString::CK_CurrentParameter: 89 return CXCompletionChunk_CurrentParameter; 90 case CodeCompletionString::CK_LeftParen: 91 return CXCompletionChunk_LeftParen; 92 case CodeCompletionString::CK_RightParen: 93 return CXCompletionChunk_RightParen; 94 case CodeCompletionString::CK_LeftBracket: 95 return CXCompletionChunk_LeftBracket; 96 case CodeCompletionString::CK_RightBracket: 97 return CXCompletionChunk_RightBracket; 98 case CodeCompletionString::CK_LeftBrace: 99 return CXCompletionChunk_LeftBrace; 100 case CodeCompletionString::CK_RightBrace: 101 return CXCompletionChunk_RightBrace; 102 case CodeCompletionString::CK_LeftAngle: 103 return CXCompletionChunk_LeftAngle; 104 case CodeCompletionString::CK_RightAngle: 105 return CXCompletionChunk_RightAngle; 106 case CodeCompletionString::CK_Comma: 107 return CXCompletionChunk_Comma; 108 case CodeCompletionString::CK_Colon: 109 return CXCompletionChunk_Colon; 110 case CodeCompletionString::CK_SemiColon: 111 return CXCompletionChunk_SemiColon; 112 case CodeCompletionString::CK_Equal: 113 return CXCompletionChunk_Equal; 114 case CodeCompletionString::CK_HorizontalSpace: 115 return CXCompletionChunk_HorizontalSpace; 116 case CodeCompletionString::CK_VerticalSpace: 117 return CXCompletionChunk_VerticalSpace; 118 } 119 120 // Should be unreachable, but let's be careful. 121 return CXCompletionChunk_Text; 122} 123 124CXString clang_getCompletionChunkText(CXCompletionString completion_string, 125 unsigned chunk_number) { 126 CXStoredCodeCompletionString *CCStr 127 = (CXStoredCodeCompletionString *)completion_string; 128 if (!CCStr || chunk_number >= CCStr->size()) 129 return createCXString((const char*)0); 130 131 switch ((*CCStr)[chunk_number].Kind) { 132 case CodeCompletionString::CK_TypedText: 133 case CodeCompletionString::CK_Text: 134 case CodeCompletionString::CK_Placeholder: 135 case CodeCompletionString::CK_CurrentParameter: 136 case CodeCompletionString::CK_Informative: 137 case CodeCompletionString::CK_LeftParen: 138 case CodeCompletionString::CK_RightParen: 139 case CodeCompletionString::CK_LeftBracket: 140 case CodeCompletionString::CK_RightBracket: 141 case CodeCompletionString::CK_LeftBrace: 142 case CodeCompletionString::CK_RightBrace: 143 case CodeCompletionString::CK_LeftAngle: 144 case CodeCompletionString::CK_RightAngle: 145 case CodeCompletionString::CK_Comma: 146 case CodeCompletionString::CK_ResultType: 147 case CodeCompletionString::CK_Colon: 148 case CodeCompletionString::CK_SemiColon: 149 case CodeCompletionString::CK_Equal: 150 case CodeCompletionString::CK_HorizontalSpace: 151 case CodeCompletionString::CK_VerticalSpace: 152 return createCXString((*CCStr)[chunk_number].Text, false); 153 154 case CodeCompletionString::CK_Optional: 155 // Note: treated as an empty text block. 156 return createCXString(""); 157 } 158 159 // Should be unreachable, but let's be careful. 160 return createCXString((const char*)0); 161} 162 163 164CXCompletionString 165clang_getCompletionChunkCompletionString(CXCompletionString completion_string, 166 unsigned chunk_number) { 167 CXStoredCodeCompletionString *CCStr 168 = (CXStoredCodeCompletionString *)completion_string; 169 if (!CCStr || chunk_number >= CCStr->size()) 170 return 0; 171 172 switch ((*CCStr)[chunk_number].Kind) { 173 case CodeCompletionString::CK_TypedText: 174 case CodeCompletionString::CK_Text: 175 case CodeCompletionString::CK_Placeholder: 176 case CodeCompletionString::CK_CurrentParameter: 177 case CodeCompletionString::CK_Informative: 178 case CodeCompletionString::CK_LeftParen: 179 case CodeCompletionString::CK_RightParen: 180 case CodeCompletionString::CK_LeftBracket: 181 case CodeCompletionString::CK_RightBracket: 182 case CodeCompletionString::CK_LeftBrace: 183 case CodeCompletionString::CK_RightBrace: 184 case CodeCompletionString::CK_LeftAngle: 185 case CodeCompletionString::CK_RightAngle: 186 case CodeCompletionString::CK_Comma: 187 case CodeCompletionString::CK_ResultType: 188 case CodeCompletionString::CK_Colon: 189 case CodeCompletionString::CK_SemiColon: 190 case CodeCompletionString::CK_Equal: 191 case CodeCompletionString::CK_HorizontalSpace: 192 case CodeCompletionString::CK_VerticalSpace: 193 return 0; 194 195 case CodeCompletionString::CK_Optional: 196 // Note: treated as an empty text block. 197 return (*CCStr)[chunk_number].Optional; 198 } 199 200 // Should be unreachable, but let's be careful. 201 return 0; 202} 203 204unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { 205 CXStoredCodeCompletionString *CCStr 206 = (CXStoredCodeCompletionString *)completion_string; 207 return CCStr? CCStr->size() : 0; 208} 209 210unsigned clang_getCompletionPriority(CXCompletionString completion_string) { 211 CXStoredCodeCompletionString *CCStr 212 = (CXStoredCodeCompletionString *)completion_string; 213 return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely); 214} 215 216enum CXAvailabilityKind 217clang_getCompletionAvailability(CXCompletionString completion_string) { 218 CXStoredCodeCompletionString *CCStr 219 = (CXStoredCodeCompletionString *)completion_string; 220 return CCStr? CCStr->getAvailability() : CXAvailability_Available; 221} 222 223/// \brief The CXCodeCompleteResults structure we allocate internally; 224/// the client only sees the initial CXCodeCompleteResults structure. 225struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { 226 AllocatedCXCodeCompleteResults(); 227 ~AllocatedCXCodeCompleteResults(); 228 229 /// \brief Diagnostics produced while performing code completion. 230 llvm::SmallVector<StoredDiagnostic, 8> Diagnostics; 231 232 /// \brief Diag object 233 llvm::IntrusiveRefCntPtr<Diagnostic> Diag; 234 235 /// \brief Language options used to adjust source locations. 236 LangOptions LangOpts; 237 238 FileSystemOptions FileSystemOpts; 239 240 /// \brief File manager, used for diagnostics. 241 FileManager FileMgr; 242 243 /// \brief Source manager, used for diagnostics. 244 SourceManager SourceMgr; 245 246 /// \brief Temporary files that should be removed once we have finished 247 /// with the code-completion results. 248 std::vector<llvm::sys::Path> TemporaryFiles; 249 250 /// \brief Temporary buffers that will be deleted once we have finished with 251 /// the code-completion results. 252 llvm::SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers; 253}; 254 255/// \brief Tracks the number of code-completion result objects that are 256/// currently active. 257/// 258/// Used for debugging purposes only. 259static unsigned CodeCompletionResultObjects; 260 261AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults() 262 : CXCodeCompleteResults(), 263 Diag(new Diagnostic( 264 llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs))), 265 FileMgr(FileSystemOpts), 266 SourceMgr(*Diag, FileMgr) { 267 if (getenv("LIBCLANG_OBJTRACKING")) { 268 ++CodeCompletionResultObjects; 269 fprintf(stderr, "+++ %d completion results\n", CodeCompletionResultObjects); 270 } 271} 272 273AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { 274 for (unsigned I = 0, N = NumResults; I != N; ++I) 275 delete (CXStoredCodeCompletionString *)Results[I].CompletionString; 276 delete [] Results; 277 278 for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) 279 TemporaryFiles[I].eraseFromDisk(); 280 for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) 281 delete TemporaryBuffers[I]; 282 283 if (getenv("LIBCLANG_OBJTRACKING")) { 284 --CodeCompletionResultObjects; 285 fprintf(stderr, "--- %d completion results\n", CodeCompletionResultObjects); 286 } 287} 288 289} // end extern "C" 290 291namespace { 292 class CaptureCompletionResults : public CodeCompleteConsumer { 293 AllocatedCXCodeCompleteResults &AllocatedResults; 294 llvm::SmallVector<CXCompletionResult, 16> StoredResults; 295 296 public: 297 explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results) 298 : CodeCompleteConsumer(true, false, true, false), 299 AllocatedResults(Results) { } 300 ~CaptureCompletionResults() { Finish(); } 301 302 virtual void ProcessCodeCompleteResults(Sema &S, 303 CodeCompletionContext Context, 304 CodeCompletionResult *Results, 305 unsigned NumResults) { 306 StoredResults.reserve(StoredResults.size() + NumResults); 307 for (unsigned I = 0; I != NumResults; ++I) { 308 CXStoredCodeCompletionString *StoredCompletion 309 = new CXStoredCodeCompletionString(Results[I].Priority, 310 Results[I].Availability); 311 (void)Results[I].CreateCodeCompletionString(S, StoredCompletion); 312 313 CXCompletionResult R; 314 R.CursorKind = Results[I].CursorKind; 315 R.CompletionString = StoredCompletion; 316 StoredResults.push_back(R); 317 } 318 } 319 320 virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, 321 OverloadCandidate *Candidates, 322 unsigned NumCandidates) { 323 StoredResults.reserve(StoredResults.size() + NumCandidates); 324 for (unsigned I = 0; I != NumCandidates; ++I) { 325 // FIXME: Set priority, availability appropriately. 326 CXStoredCodeCompletionString *StoredCompletion 327 = new CXStoredCodeCompletionString(1, CXAvailability_Available); 328 (void)Candidates[I].CreateSignatureString(CurrentArg, S, 329 StoredCompletion); 330 331 CXCompletionResult R; 332 R.CursorKind = CXCursor_NotImplemented; 333 R.CompletionString = StoredCompletion; 334 StoredResults.push_back(R); 335 } 336 } 337 338 private: 339 void Finish() { 340 AllocatedResults.Results = new CXCompletionResult [StoredResults.size()]; 341 AllocatedResults.NumResults = StoredResults.size(); 342 std::memcpy(AllocatedResults.Results, StoredResults.data(), 343 StoredResults.size() * sizeof(CXCompletionResult)); 344 StoredResults.clear(); 345 } 346 }; 347} 348 349extern "C" { 350struct CodeCompleteAtInfo { 351 CXTranslationUnit TU; 352 const char *complete_filename; 353 unsigned complete_line; 354 unsigned complete_column; 355 struct CXUnsavedFile *unsaved_files; 356 unsigned num_unsaved_files; 357 unsigned options; 358 CXCodeCompleteResults *result; 359}; 360void clang_codeCompleteAt_Impl(void *UserData) { 361 CodeCompleteAtInfo *CCAI = static_cast<CodeCompleteAtInfo*>(UserData); 362 CXTranslationUnit TU = CCAI->TU; 363 const char *complete_filename = CCAI->complete_filename; 364 unsigned complete_line = CCAI->complete_line; 365 unsigned complete_column = CCAI->complete_column; 366 struct CXUnsavedFile *unsaved_files = CCAI->unsaved_files; 367 unsigned num_unsaved_files = CCAI->num_unsaved_files; 368 unsigned options = CCAI->options; 369 CCAI->result = 0; 370 371#ifdef UDP_CODE_COMPLETION_LOGGER 372#ifdef UDP_CODE_COMPLETION_LOGGER_PORT 373 const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); 374#endif 375#endif 376 377 bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0; 378 379 ASTUnit *AST = static_cast<ASTUnit *>(TU->TUData); 380 if (!AST) 381 return; 382 383 ASTUnit::ConcurrencyCheck Check(*AST); 384 385 // Perform the remapping of source files. 386 llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; 387 for (unsigned I = 0; I != num_unsaved_files; ++I) { 388 llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); 389 const llvm::MemoryBuffer *Buffer 390 = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); 391 RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename, 392 Buffer)); 393 } 394 395 if (EnableLogging) { 396 // FIXME: Add logging. 397 } 398 399 // Parse the resulting source file to find code-completion results. 400 AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults; 401 Results->Results = 0; 402 Results->NumResults = 0; 403 404 // Create a code-completion consumer to capture the results. 405 CaptureCompletionResults Capture(*Results); 406 407 // Perform completion. 408 AST->CodeComplete(complete_filename, complete_line, complete_column, 409 RemappedFiles.data(), RemappedFiles.size(), 410 (options & CXCodeComplete_IncludeMacros), 411 (options & CXCodeComplete_IncludeCodePatterns), 412 Capture, 413 *Results->Diag, Results->LangOpts, Results->SourceMgr, 414 Results->FileMgr, Results->Diagnostics, 415 Results->TemporaryBuffers); 416 417 418 419#ifdef UDP_CODE_COMPLETION_LOGGER 420#ifdef UDP_CODE_COMPLETION_LOGGER_PORT 421 const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); 422 llvm::SmallString<256> LogResult; 423 llvm::raw_svector_ostream os(LogResult); 424 425 // Figure out the language and whether or not it uses PCH. 426 const char *lang = 0; 427 bool usesPCH = false; 428 429 for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); 430 I != E; ++I) { 431 if (*I == 0) 432 continue; 433 if (strcmp(*I, "-x") == 0) { 434 if (I + 1 != E) { 435 lang = *(++I); 436 continue; 437 } 438 } 439 else if (strcmp(*I, "-include") == 0) { 440 if (I+1 != E) { 441 const char *arg = *(++I); 442 llvm::SmallString<512> pchName; 443 { 444 llvm::raw_svector_ostream os(pchName); 445 os << arg << ".pth"; 446 } 447 pchName.push_back('\0'); 448 struct stat stat_results; 449 if (stat(pchName.data(), &stat_results) == 0) 450 usesPCH = true; 451 continue; 452 } 453 } 454 } 455 456 os << "{ "; 457 os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); 458 os << ", \"numRes\": " << Results->NumResults; 459 os << ", \"diags\": " << Results->Diagnostics.size(); 460 os << ", \"pch\": " << (usesPCH ? "true" : "false"); 461 os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; 462 const char *name = getlogin(); 463 os << ", \"user\": \"" << (name ? name : "unknown") << '"'; 464 os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; 465 os << " }"; 466 467 llvm::StringRef res = os.str(); 468 if (res.size() > 0) { 469 do { 470 // Setup the UDP socket. 471 struct sockaddr_in servaddr; 472 bzero(&servaddr, sizeof(servaddr)); 473 servaddr.sin_family = AF_INET; 474 servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); 475 if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, 476 &servaddr.sin_addr) <= 0) 477 break; 478 479 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); 480 if (sockfd < 0) 481 break; 482 483 sendto(sockfd, res.data(), res.size(), 0, 484 (struct sockaddr *)&servaddr, sizeof(servaddr)); 485 close(sockfd); 486 } 487 while (false); 488 } 489#endif 490#endif 491 CCAI->result = Results; 492} 493CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, 494 const char *complete_filename, 495 unsigned complete_line, 496 unsigned complete_column, 497 struct CXUnsavedFile *unsaved_files, 498 unsigned num_unsaved_files, 499 unsigned options) { 500 CodeCompleteAtInfo CCAI = { TU, complete_filename, complete_line, 501 complete_column, unsaved_files, num_unsaved_files, 502 options, 0 }; 503 llvm::CrashRecoveryContext CRC; 504 505 if (!RunSafely(CRC, clang_codeCompleteAt_Impl, &CCAI)) { 506 fprintf(stderr, "libclang: crash detected in code completion\n"); 507 static_cast<ASTUnit *>(TU->TUData)->setUnsafeToFree(true); 508 return 0; 509 } 510 511 return CCAI.result; 512} 513 514unsigned clang_defaultCodeCompleteOptions(void) { 515 return CXCodeComplete_IncludeMacros; 516} 517 518void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { 519 if (!ResultsIn) 520 return; 521 522 AllocatedCXCodeCompleteResults *Results 523 = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); 524 delete Results; 525} 526 527unsigned 528clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { 529 AllocatedCXCodeCompleteResults *Results 530 = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); 531 if (!Results) 532 return 0; 533 534 return Results->Diagnostics.size(); 535} 536 537CXDiagnostic 538clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, 539 unsigned Index) { 540 AllocatedCXCodeCompleteResults *Results 541 = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); 542 if (!Results || Index >= Results->Diagnostics.size()) 543 return 0; 544 545 return new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts); 546} 547 548 549} // end extern "C" 550 551/// \brief Simple utility function that appends a \p New string to the given 552/// \p Old string, using the \p Buffer for storage. 553/// 554/// \param Old The string to which we are appending. This parameter will be 555/// updated to reflect the complete string. 556/// 557/// 558/// \param New The string to append to \p Old. 559/// 560/// \param Buffer A buffer that stores the actual, concatenated string. It will 561/// be used if the old string is already-non-empty. 562static void AppendToString(llvm::StringRef &Old, llvm::StringRef New, 563 llvm::SmallString<256> &Buffer) { 564 if (Old.empty()) { 565 Old = New; 566 return; 567 } 568 569 if (Buffer.empty()) 570 Buffer.append(Old.begin(), Old.end()); 571 Buffer.append(New.begin(), New.end()); 572 Old = Buffer.str(); 573} 574 575/// \brief Get the typed-text blocks from the given code-completion string 576/// and return them as a single string. 577/// 578/// \param String The code-completion string whose typed-text blocks will be 579/// concatenated. 580/// 581/// \param Buffer A buffer used for storage of the completed name. 582static llvm::StringRef GetTypedName(CodeCompletionString *String, 583 llvm::SmallString<256> &Buffer) { 584 llvm::StringRef Result; 585 for (CodeCompletionString::iterator C = String->begin(), CEnd = String->end(); 586 C != CEnd; ++C) { 587 if (C->Kind == CodeCompletionString::CK_TypedText) 588 AppendToString(Result, C->Text, Buffer); 589 } 590 591 return Result; 592} 593 594namespace { 595 struct OrderCompletionResults { 596 bool operator()(const CXCompletionResult &XR, 597 const CXCompletionResult &YR) const { 598 CXStoredCodeCompletionString *X 599 = (CXStoredCodeCompletionString *)XR.CompletionString; 600 CXStoredCodeCompletionString *Y 601 = (CXStoredCodeCompletionString *)YR.CompletionString; 602 603 llvm::SmallString<256> XBuffer; 604 llvm::StringRef XText = GetTypedName(X, XBuffer); 605 llvm::SmallString<256> YBuffer; 606 llvm::StringRef YText = GetTypedName(Y, YBuffer); 607 608 if (XText.empty() || YText.empty()) 609 return !XText.empty(); 610 611 int result = XText.compare_lower(YText); 612 if (result < 0) 613 return true; 614 if (result > 0) 615 return false; 616 617 result = XText.compare(YText); 618 return result < 0; 619 } 620 }; 621} 622 623extern "C" { 624 void clang_sortCodeCompletionResults(CXCompletionResult *Results, 625 unsigned NumResults) { 626 std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); 627 } 628} 629