示例#1
0
void ClangUtils::printCompletionDiagnostics(CXCodeCompleteResults *res)
{
	//// Report diagnostics to the log file
	const unsigned diagCount = clang_codeCompleteGetNumDiagnostics(res);
	for(unsigned i=0; i<diagCount; i++) {
		CXDiagnostic diag = clang_codeCompleteGetDiagnostic(res, i);
		CXString diagStr = clang_getDiagnosticSpelling(diag);
		wxString wxDiagString = wxString(clang_getCString(diagStr), wxConvUTF8);

		CL_DEBUG(wxT("Completion diagnostic [%d]: %s"), clang_getDiagnosticSeverity(diag), wxDiagString.c_str());
		clang_disposeString(diagStr);
		clang_disposeDiagnostic(diag);
	}
}
示例#2
0
int perform_code_completion(int argc, const char **argv) {
  const char *input = argv[1];
  char *filename = 0;
  unsigned line;
  unsigned column;
  CXIndex CIdx;
  int errorCode;
  struct CXUnsavedFile *unsaved_files = 0;
  int num_unsaved_files = 0;
  CXCodeCompleteResults *results = 0;

  input += strlen("-code-completion-at=");
  if ((errorCode = parse_file_line_column(input, &filename, &line, &column,
                                          0, 0)))
    return errorCode;

  if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files))
    return -1;

  CIdx = clang_createIndex(0, 1);
  results = clang_codeComplete(CIdx,
                               argv[argc - 1], argc - num_unsaved_files - 3,
                               argv + num_unsaved_files + 2,
                               num_unsaved_files, unsaved_files,
                               filename, line, column);

  if (results) {
    unsigned i, n = results->NumResults;
    for (i = 0; i != n; ++i)
      print_completion_result(results->Results + i, stdout);
    n = clang_codeCompleteGetNumDiagnostics(results);
    for (i = 0; i != n; ++i) {
      CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i);
      PrintDiagnostic(diag);
      clang_disposeDiagnostic(diag);
    }
    clang_disposeCodeCompleteResults(results);
  }

  clang_disposeIndex(CIdx);
  free(filename);

  free_remapped_files(unsaved_files, num_unsaved_files);

  return 0;
}
示例#3
0
void GmacsPreprocessor::codeCompletion(const QTextCursor &cursor)
{
	int line = cursor.blockNumber();
	int column = cursor.columnNumber();
	//const char *name = filename->toLocal8Bit().data();
	const char *name = clang_getCString(clang_getTranslationUnitSpelling(unit));
	//qDebug() << cursor.document()->toPlainText();
	//qDebug() << cursor.document()->toPlainText().size();
	QString document = cursor.document()->toPlainText();
	QString text = cursor.block().text();
	QRegExp exp("\t");
	int tab_count = text.count(exp);
	fprintf(stderr, "column = [%d]\n", cursor.positionInBlock());
	//unsaved_file->Filename = name;
	//unsaved_file->Contents = document.toLocal8Bit().data();
	//unsaved_file->Length = document.size();
	if (!unit) return;
	CLANG_REPARSE(unit, NULL);//unsaved_file);
	fprintf(stderr, "line = [%d], column = [%d]\n", line+1, column + tab_count);
	fprintf(stderr, "name = [%s]\n", name);
	CXCodeCompleteResults *res = CLANG_CODE_COMPLETION(unit, name, line+1, column + tab_count);
	if (!res) fprintf(stderr, "ERROR: could not complete\n");
	for (size_t i = 0; i < clang_codeCompleteGetNumDiagnostics(res); i++) {
		const CXDiagnostic &diag = clang_codeCompleteGetDiagnostic(res, i);
		const CXString &s = clang_getDiagnosticSpelling(diag);
		fprintf(stderr, "%s\n", clang_getCString(s));
	}
	unsigned num_results = res->NumResults;
	fprintf(stderr, "num_results = [%d]\n");
	for (unsigned i = 0; i < num_results; i++) {
		const CXCompletionString& str = res->Results[i].CompletionString;
		unsigned chunks = clang_getNumCompletionChunks(str);
		for (unsigned j = 0; j < chunks; j++) {
			const CXString& out = clang_getCompletionChunkText(str, j);
			//std::cout << clang_getCString(out) << " ";
			if (clang_getCompletionChunkKind(str, j) != CXCompletionChunk_TypedText)
				continue;
		}
		//std::cout << std::endl;
	}
	clang_disposeCodeCompleteResults(res);
}
void CompletionJob::processDiagnostics(CXCodeCompleteResults* results)
{
    if (!testLog(CompilationError))
        return;

    const unsigned int numDiags = clang_codeCompleteGetNumDiagnostics(results);
    for (unsigned int curDiag = 0; curDiag < numDiags; ++curDiag) {
        CXDiagnostic diagnostic = clang_codeCompleteGetDiagnostic(results, curDiag);
        const unsigned diagnosticOptions = (CXDiagnostic_DisplaySourceLocation|
                                            CXDiagnostic_DisplayColumn|
                                            CXDiagnostic_DisplaySourceRanges|
                                            CXDiagnostic_DisplayOption|
                                            CXDiagnostic_DisplayCategoryId|
                                            CXDiagnostic_DisplayCategoryName);
        const ByteArray text = RTags::eatString(clang_formatDiagnostic(diagnostic, diagnosticOptions));
        log(CompilationError, "%s", text.constData());

        clang_disposeDiagnostic(diagnostic);
    }
    log(CompilationError, "$");
}
示例#5
0
void Irony::complete(const std::string &file,
                     unsigned line,
                     unsigned col,
                     const std::vector<std::string> &flags,
                     const std::vector<CXUnsavedFile> &unsavedFiles) {
  CXTranslationUnit tu = tuManager_.getOrCreateTU(file, flags, unsavedFiles);

  if (tu == nullptr) {
    std::cout << "nil\n";
    return;
  }

  if (CXCodeCompleteResults *completions =
          clang_codeCompleteAt(tu,
                               file.c_str(),
                               line,
                               col,
                               const_cast<CXUnsavedFile *>(unsavedFiles.data()),
                               unsavedFiles.size(),
                               (clang_defaultCodeCompleteOptions() &
                                ~CXCodeComplete_IncludeCodePatterns)
#if HAS_BRIEF_COMMENTS_IN_COMPLETION
                                   |
                                   CXCodeComplete_IncludeBriefComments
#endif
                               )) {

    if (debug_) {
      unsigned numDiags = clang_codeCompleteGetNumDiagnostics(completions);
      std::clog << "debug: complete: " << numDiags << " diagnostic(s)\n";
      for (unsigned i = 0; i < numDiags; ++i) {
        CXDiagnostic diagnostic =
            clang_codeCompleteGetDiagnostic(completions, i);
        CXString s = clang_formatDiagnostic(
            diagnostic, clang_defaultDiagnosticDisplayOptions());

        std::clog << clang_getCString(s) << std::endl;
        clang_disposeString(s);
        clang_disposeDiagnostic(diagnostic);
      }
    }

    clang_sortCodeCompletionResults(completions->Results,
                                    completions->NumResults);

    std::cout << "(\n";

    // re-use the same buffers to avoid unnecessary allocations
    std::string typedtext, brief, resultType, prototype, postCompCar;
    std::vector<unsigned> postCompCdr;

    for (unsigned i = 0; i < completions->NumResults; ++i) {
      CXCompletionResult candidate = completions->Results[i];
      CXAvailabilityKind availability =
          clang_getCompletionAvailability(candidate.CompletionString);

      unsigned priority =
          clang_getCompletionPriority(candidate.CompletionString);
      unsigned annotationStart = 0;
      bool typedTextSet = false;

      if (availability == CXAvailability_NotAccessible ||
          availability == CXAvailability_NotAvailable) {
        continue;
      }

      typedtext.clear();
      brief.clear();
      resultType.clear();
      prototype.clear();
      postCompCar.clear();
      postCompCdr.clear();

      for (CompletionChunk chunk(candidate.CompletionString); chunk.hasNext();
           chunk.next()) {
        char ch = 0;

        auto chunkKind = chunk.kind();

        switch (chunkKind) {
        case CXCompletionChunk_ResultType:
          resultType = chunk.text();
          break;

        case CXCompletionChunk_TypedText:
        case CXCompletionChunk_Text:
        case CXCompletionChunk_Placeholder:
        case CXCompletionChunk_Informative:
        case CXCompletionChunk_CurrentParameter:
          prototype += chunk.text();
          break;

        case CXCompletionChunk_LeftParen:       ch = '(';  break;
        case CXCompletionChunk_RightParen:      ch = ')';  break;
        case CXCompletionChunk_LeftBracket:     ch = '[';  break;
        case CXCompletionChunk_RightBracket:    ch = ']';  break;
        case CXCompletionChunk_LeftBrace:       ch = '{';  break;
        case CXCompletionChunk_RightBrace:      ch = '}';  break;
        case CXCompletionChunk_LeftAngle:       ch = '<';  break;
        case CXCompletionChunk_RightAngle:      ch = '>';  break;
        case CXCompletionChunk_Comma:           ch = ',';  break;
        case CXCompletionChunk_Colon:           ch = ':';  break;
        case CXCompletionChunk_SemiColon:       ch = ';';  break;
        case CXCompletionChunk_Equal:           ch = '=';  break;
        case CXCompletionChunk_HorizontalSpace: ch = ' ';  break;
        case CXCompletionChunk_VerticalSpace:   ch = '\n'; break;

        case CXCompletionChunk_Optional:
          // ignored for now
          break;
        }

        if (ch != 0) {
          prototype += ch;
          // commas look better followed by a space
          if (ch == ',') {
            prototype += ' ';
          }
        }

        if (typedTextSet) {
          if (ch != 0) {
            postCompCar += ch;
            if (ch == ',') {
              postCompCar += ' ';
            }
          } else if (chunkKind == CXCompletionChunk_Text ||
                     chunkKind == CXCompletionChunk_TypedText) {
            postCompCar += chunk.text();
          } else if (chunkKind == CXCompletionChunk_Placeholder ||
                     chunkKind == CXCompletionChunk_CurrentParameter) {
            postCompCdr.push_back(postCompCar.size());
            postCompCar += chunk.text();
            postCompCdr.push_back(postCompCar.size());
          }
        }

        // Consider only the first typed text. The CXCompletionChunk_TypedText
        // doc suggests that exactly one typed text will be given but at least
        // in Objective-C it seems that more than one can appear, see:
        // https://github.com/Sarcasm/irony-mode/pull/78#issuecomment-37115538
        if (chunkKind == CXCompletionChunk_TypedText && !typedTextSet) {
          typedtext = chunk.text();
          // annotation is what comes after the typedtext
          annotationStart = prototype.size();
          typedTextSet = true;
        }
      }

#if HAS_BRIEF_COMMENTS_IN_COMPLETION
      brief = cxStringToStd(
          clang_getCompletionBriefComment(candidate.CompletionString));
#endif

      // see irony-completion.el#irony-completion-candidates
      std::cout << '(' << support::quoted(typedtext)  //
                << ' ' << priority                    //
                << ' ' << support::quoted(resultType) //
                << ' ' << support::quoted(brief)      //
                << ' ' << support::quoted(prototype)  //
                << ' ' << annotationStart             //
                << " (" << support::quoted(postCompCar);
      for (unsigned index : postCompCdr)
        std::cout << ' ' << index;
      std::cout << ")"
                << ")\n";
    }

    clang_disposeCodeCompleteResults(completions);
    std::cout << ")\n";
  }
}
示例#6
0
diagnostic code_complete_results::getDiagnostic(unsigned idx)
{
    return { clang_codeCompleteGetDiagnostic(results, idx) };
}
void ClangWorkerThread::ProcessRequest(ThreadRequest* request)
{
    // Send start event
    PostEvent(wxEVT_CLANG_PCH_CACHE_STARTED, "");

    ClangThreadRequest* task = dynamic_cast<ClangThreadRequest*>(request);
    wxASSERT_MSG(task, "ClangWorkerThread: NULL task");

    {
        // A bit of optimization
        wxCriticalSectionLocker locker(m_criticalSection);
        if(task->GetContext() == CTX_CachePCH && m_cache.Contains(task->GetFileName())) {
            // Nothing to be done here
            PostEvent(wxEVT_CLANG_PCH_CACHE_ENDED, task->GetFileName());
            return;
        }
    }

    CL_DEBUG(wxT("==========> [ ClangPchMakerThread ] ProcessRequest started: %s"), task->GetFileName().c_str());
    CL_DEBUG(wxT("ClangWorkerThread:: processing request %d"), (int)task->GetContext());

    ClangCacheEntry cacheEntry = findEntry(task->GetFileName());
    CXTranslationUnit TU = cacheEntry.TU;
    CL_DEBUG(wxT("ClangWorkerThread:: found cached TU: %p"), (void*)TU);

    bool reparseRequired = true;
    if(!TU) {

        // First time creating the TU
        TU = DoCreateTU(task->GetIndex(), task, true);
        reparseRequired = false;
        cacheEntry.lastReparse = time(NULL);
        cacheEntry.TU = TU;
        cacheEntry.sourceFile = task->GetFileName();
    }

    if(!TU) {
        CL_DEBUG(wxT("Failed to parse Translation UNIT..."));
        PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName());
        return;
    }

    if(reparseRequired && task->GetContext() == ::CTX_ReparseTU) {
        DoSetStatusMsg(wxString::Format(wxT("clang: re-parsing file %s..."), task->GetFileName().c_str()));

        // We need to reparse the TU
        CL_DEBUG(wxT("Calling clang_reparseTranslationUnit... [CTX_ReparseTU]"));
        if(clang_reparseTranslationUnit(TU, 0, NULL, clang_defaultReparseOptions(TU)) == 0) {
            CL_DEBUG(wxT("Calling clang_reparseTranslationUnit... done [CTX_ReparseTU]"));
            cacheEntry.lastReparse = time(NULL);

        } else {

            CL_DEBUG(wxT("An error occured during reparsing of the TU for file %s. TU: %p"),
                     task->GetFileName().c_str(),
                     (void*)TU);

            // The only thing that left to be done here, is to dispose the TU
            clang_disposeTranslationUnit(TU);
            PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName());

            return;
        }
    }

    // Construct a cache-returner class
    // which makes sure that the TU is cached
    // when we leave the current scope
    CacheReturner cr(this, cacheEntry);

    // Prepare the 'End' event
    wxCommandEvent eEnd(wxEVT_CLANG_PCH_CACHE_ENDED);
    ClangThreadReply* reply = new ClangThreadReply;
    reply->context = task->GetContext();
    reply->filterWord = task->GetFilterWord();
    reply->filename = task->GetFileName().c_str();
    reply->results = NULL;

    wxFileName realFileName(reply->filename);
    if(realFileName.GetFullName().StartsWith(CODELITE_CLANG_FILE_PREFIX)) {
        realFileName.SetFullName(realFileName.GetFullName().Mid(strlen(CODELITE_CLANG_FILE_PREFIX)));
    }
    reply->filename = realFileName.GetFullPath();

    if(task->GetContext() == CTX_CodeCompletion || task->GetContext() == CTX_WordCompletion ||
       task->GetContext() == CTX_Calltip) {
        CL_DEBUG(wxT("Calling clang_codeCompleteAt..."));

        ClangThreadRequest::List_t usList = task->GetModifiedBuffers();
        usList.push_back(std::make_pair(task->GetFileName(), task->GetDirtyBuffer()));
        ClangUnsavedFiles usf(usList);

        CL_DEBUG(wxT("Location: %s:%u:%u"), task->GetFileName().c_str(), task->GetLine(), task->GetColumn());
        reply->results = clang_codeCompleteAt(TU,
                                              cstr(task->GetFileName()),
                                              task->GetLine(),
                                              task->GetColumn(),
                                              usf.GetUnsavedFiles(),
                                              usf.GetCount(),
                                              clang_defaultCodeCompleteOptions()
#if HAS_LIBCLANG_BRIEFCOMMENTS
                                                  |
                                                  CXCodeComplete_IncludeBriefComments
#endif
                                              );

        cacheEntry.lastReparse = time(NULL);

        CL_DEBUG(wxT("Calling clang_codeCompleteAt... done"));
        wxString displayTip;
        bool hasErrors(false);
        if(reply->results) {
            unsigned maxErrorToDisplay = 10;
            std::set<wxString> errorMessages;
            unsigned errorCount = clang_codeCompleteGetNumDiagnostics(reply->results);
            // Collect all errors / fatal errors and report them back to user
            for(unsigned i = 0; i < errorCount; i++) {
                CXDiagnostic diag = clang_codeCompleteGetDiagnostic(reply->results, i);
                CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diag);
                if(!hasErrors) {
                    hasErrors = (severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal);
                }

                if(severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal || severity == CXDiagnostic_Note) {
                    CXString diagStr = clang_getDiagnosticSpelling(diag);
                    wxString wxDiagString = wxString(clang_getCString(diagStr), wxConvUTF8);

                    // Collect up to 10 error messages
                    // and dont collect the same error twice
                    if(errorMessages.find(wxDiagString) == errorMessages.end() &&
                       errorMessages.size() <= maxErrorToDisplay) {
                        errorMessages.insert(wxDiagString);
                        displayTip << wxDiagString.c_str() << wxT("\n");
                    }

                    clang_disposeString(diagStr);
                }
                clang_disposeDiagnostic(diag);
            }

            CL_DEBUG(wxT("Found %u matches"), reply->results->NumResults);
            ClangUtils::printCompletionDiagnostics(reply->results);
        }

        if(!displayTip.IsEmpty() && hasErrors) {
            // Send back the error messages
            reply->errorMessage << "clang: " << displayTip;
            reply->errorMessage.RemoveLast();
            
            // Free the results
            clang_disposeCodeCompleteResults(reply->results);
            reply->results = NULL;
        }

        // Send the event
        eEnd.SetClientData(reply);
        EventNotifier::Get()->AddPendingEvent(eEnd);

    } else if(task->GetContext() == CTX_GotoDecl || task->GetContext() == CTX_GotoImpl) {

        // Check to see if the file was modified since it was last reparsed
        // If it does, we need to re-parse it again
        wxFileName fnSource(cacheEntry.sourceFile);
        time_t fileModificationTime = fnSource.GetModificationTime().GetTicks();
        time_t lastReparseTime = cacheEntry.lastReparse;

        if(fileModificationTime > lastReparseTime) {

            // The file needs to be re-parsed
            DoSetStatusMsg(wxString::Format(wxT("clang: re-parsing file %s...\n"), cacheEntry.sourceFile));

            // Try reparsing the TU
            ClangThreadRequest::List_t usList = task->GetModifiedBuffers();
            usList.push_back(std::make_pair(task->GetFileName(), task->GetDirtyBuffer()));
            ClangUnsavedFiles usf(usList);

            if(clang_reparseTranslationUnit(
                   TU, usf.GetCount(), usf.GetUnsavedFiles(), clang_defaultReparseOptions(TU)) != 0) {
                // Failed to reparse
                cr.SetCancelled(true); // cancel the re-caching of the TU

                DoSetStatusMsg(
                    wxString::Format("clang: clang_reparseTranslationUnit '%s' failed\n", cacheEntry.sourceFile));

                clang_disposeTranslationUnit(TU);
                wxDELETE(reply);
                PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName());
                return;
            }

            DoSetStatusMsg(
                wxString::Format("clang: clang_reparseTranslationUnit '%s' - done\n", cacheEntry.sourceFile));
            // Update the 'lastReparse' field
            cacheEntry.lastReparse = time(NULL);
        }

        bool success = DoGotoDefinition(TU, task, reply);
        if(success) {
            eEnd.SetClientData(reply);
            EventNotifier::Get()->AddPendingEvent(eEnd);

        } else {
            DoSetStatusMsg(wxT("clang: no matches were found"));
            CL_DEBUG(wxT("Clang Goto Decl/Impl: could not find a cursor matching for position %s:%d:%d"),
                     task->GetFileName().c_str(),
                     (int)task->GetLine(),
                     (int)task->GetColumn());

            // Failed, delete the 'reply' allocatd earlier
            wxDELETE(reply);
            PostEvent(wxEVT_CLANG_TU_CREATE_ERROR, task->GetFileName());
        }
    } else {

        wxDELETE(reply);
        PostEvent(wxEVT_CLANG_PCH_CACHE_ENDED, task->GetFileName());
    }
}
QList<ClangCodeCompletionItem> TranslationUnit::completeAt(
    const int line
  , const int column
  , const unsigned completion_flags
  , const clang::unsaved_files_list& unsaved_files
  , const PluginConfiguration::sanitize_rules_list_type& sanitize_rules
  )
{
    auto files = unsaved_files.get();
#ifndef NDEBUG
    for (auto& item : files)
        assert(
            "Sanity check"
          && item.Filename
          && std::strlen(item.Filename)
          && item.Contents
          && item.Length
          );
#endif

    clang::DCXCodeCompleteResults res = {
        clang_codeCompleteAt(
            m_unit
          , m_filename.constData()
          , unsigned(line)
          , unsigned(column)
          , files.data()
          , files.size()
          , completion_flags
          )
      };
    if (!res)
    {
        throw Exception::CompletionFailure(
            i18nc("@item:intext", "Unable to perform code completion").toAscii().constData()
          );
    }

#if 0
    clang_sortCodeCompletionResults(res->Results, res->NumResults);
#endif

    // Collect some diagnostic SPAM
    for (auto i = 0u; i < clang_codeCompleteGetNumDiagnostics(res); ++i)
    {
        clang::DCXDiagnostic diag = {clang_codeCompleteGetDiagnostic(res, i)};
        appendDiagnostic(diag);
    }

    QList<ClangCodeCompletionItem> completions;
    completions.reserve(res->NumResults);                   // Peallocate enough space for completion results

    // Lets look what we've got...
    for (auto i = 0u; i < res->NumResults; ++i)
    {
        const auto str = res->Results[i].CompletionString;
        const auto priority = clang_getCompletionPriority(str);
        const auto cursor_kind = res->Results[i].CursorKind;
        debugShowCompletionResult(i, priority, str, cursor_kind);

        // Skip unusable completions
        // 0) check availability
        const auto availability = clang_getCompletionAvailability(str);
        if (availability != CXAvailability_Available && availability != CXAvailability_Deprecated)
        {
            kDebug(DEBUG_AREA) << "!! Skip result" << i << "as not available";
            continue;
        }
        // 1) check usefulness
        /// \todo Make it configurable
        if (cursor_kind == CXCursor_NotImplemented)
            continue;

        // Collect all completion chunks and from a format string
        QString text_before;
        QString typed_text;
        QString text_after;
        QStringList placeholders;
        int optional_placeholers_start_position = -1;
        // A lambda to append given text to different parts
        // of future completion string, depending on already processed text
        auto appender = [&](const QString& text)
        {
            if (typed_text.isEmpty())
                text_before += text;
            else
                text_after += text;
        };
        auto skip_this_item = false;
        for (
            auto j = 0u
          , chunks = clang_getNumCompletionChunks(str)
          ; j < chunks && !skip_this_item
          ; ++j
          )
        {
            auto kind = clang_getCompletionChunkKind(str, j);
            auto text = toString(clang::DCXString{clang_getCompletionChunkText(str, j)});
            switch (kind)
            {
                // Text that a user would be expected to type to get this code-completion result
                case CXCompletionChunk_TypedText:
                // Text that should be inserted as part of a code-completion result
                case CXCompletionChunk_Text:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                        typed_text += p.second;
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // Placeholder text that should be replaced by the user
                case CXCompletionChunk_Placeholder:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                    {
                        appender(
                            QLatin1String{"%"}
                          + QString::number(placeholders.size() + 1)
                          + QLatin1String{"%"}
                          );
                        placeholders.push_back(p.second);
                    }
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // A code-completion string that describes "optional" text that
                // could be a part of the template (but is not required)
                case CXCompletionChunk_Optional:
                {
                    auto ostr = clang_getCompletionChunkCompletionString(str, j);
                    for (
                        auto oci = 0u
                      , ocn = clang_getNumCompletionChunks(ostr)
                      ; oci < ocn
                      ; ++oci
                      )
                    {
                        auto otext = toString(clang::DCXString{clang_getCompletionChunkText(ostr, oci)});
                        // Pipe given piece of text through sanitizer
                        auto p = sanitize(otext, sanitize_rules);
                        if (p.first)
                        {
                            auto okind = clang::kind_of(ostr, oci);
                            if (okind == CXCompletionChunk_Placeholder)
                            {
                                appender(
                                    QLatin1String{"%"}
                                  + QString::number(placeholders.size() + 1)
                                  + QLatin1String{"%"}
                                  );
                                placeholders.push_back(p.second);
                                optional_placeholers_start_position = placeholders.size();
                            }
                            else appender(p.second);
                        }
                        else
                        {
                            skip_this_item = true;
                            break;
                        }
                    }
                    break;
                }
                case CXCompletionChunk_ResultType:
                case CXCompletionChunk_LeftParen:
                case CXCompletionChunk_RightParen:
                case CXCompletionChunk_LeftBracket:
                case CXCompletionChunk_RightBracket:
                case CXCompletionChunk_LeftBrace:
                case CXCompletionChunk_RightBrace:
                case CXCompletionChunk_LeftAngle:
                case CXCompletionChunk_RightAngle:
                case CXCompletionChunk_Comma:
                case CXCompletionChunk_Colon:
                case CXCompletionChunk_SemiColon:
                case CXCompletionChunk_Equal:
                case CXCompletionChunk_CurrentParameter:
                case CXCompletionChunk_HorizontalSpace:
                /// \todo Kate can't handle \c '\n' well in completions list
                case CXCompletionChunk_VerticalSpace:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                        appender(p.second);
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // Informative text that should be displayed but never inserted
                // as part of the template
                case CXCompletionChunk_Informative:
                    // Informative text before CXCompletionChunk_TypedText usually
                    // just a method scope (i.e. long name of an owner class)
                    // and it's useless for completer cuz it can group items
                    // by parent already...
                    if (!typed_text.isEmpty())
                    {
                        // Pipe given piece of text through sanitizer
                        auto p = sanitize(text, sanitize_rules);
                        if (p.first)
                            appender(p.second);
                        else                                // Go for next completion item
                            skip_this_item = true;
                    }
                    break;
                default:
                    break;
            }
        }
        // Does it pass the completion items sanitizer?
        if (skip_this_item) continue;                       // No! Skip it!

        assert("Priority expected to be less than 100" && priority < 101u);

        const auto comment = toString(clang::DCXString{clang_getCompletionBriefComment(str)});
        //
        completions.push_back({
            makeParentText(str, cursor_kind)
          , text_before
          , typed_text
          , text_after
          , placeholders
          , optional_placeholers_start_position
          , priority
          , cursor_kind
          , comment
          , availability == CXAvailability_Deprecated
          });
    }

    return completions;
}