diagnostic::fix_it diagnostic::get_fixit(int idx) { CXSourceRange replacement_range; string str(clang_getDiagnosticFixIt(diag, idx, &replacement_range)); return { str.str(), {replacement_range} }; }
void PrintDiagnostic(CXDiagnostic Diagnostic) { FILE *out = stderr; CXFile file; CXString Msg; unsigned display_opts = CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges; unsigned i, num_fixits; if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored) return; Msg = clang_formatDiagnostic(Diagnostic, display_opts); fprintf(stderr, "%s\n", clang_getCString(Msg)); clang_disposeString(Msg); clang_getInstantiationLocation(clang_getDiagnosticLocation(Diagnostic), &file, 0, 0, 0); if (!file) return; num_fixits = clang_getDiagnosticNumFixIts(Diagnostic); for (i = 0; i != num_fixits; ++i) { CXSourceRange range; CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range); CXSourceLocation start = clang_getRangeStart(range); CXSourceLocation end = clang_getRangeEnd(range); unsigned start_line, start_column, end_line, end_column; CXFile start_file, end_file; clang_getInstantiationLocation(start, &start_file, &start_line, &start_column, 0); clang_getInstantiationLocation(end, &end_file, &end_line, &end_column, 0); if (clang_equalLocations(start, end)) { /* Insertion. */ if (start_file == file) fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n", clang_getCString(insertion_text), start_line, start_column); } else if (strcmp(clang_getCString(insertion_text), "") == 0) { /* Removal. */ if (start_file == file && end_file == file) { fprintf(out, "FIX-IT: Remove "); PrintExtent(out, start_line, start_column, end_line, end_column); fprintf(out, "\n"); } } else { /* Replacement. */ if (start_file == end_file) { fprintf(out, "FIX-IT: Replace "); PrintExtent(out, start_line, start_column, end_line, end_column); fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text)); } break; } clang_disposeString(insertion_text); } }
bool IndexerJob::diagnose(int build, int *errorCount) { if (errorCount) *errorCount = 0; if (!mUnits.at(build).second) { abort(); return false; } List<String> compilationErrors; const unsigned diagnosticCount = clang_getNumDiagnostics(mUnits.at(build).second); const unsigned options = Server::instance()->options().options; for (unsigned i=0; i<diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(mUnits.at(build).second, i); int logLevel = INT_MAX; const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diagnostic); switch (severity) { case CXDiagnostic_Fatal: case CXDiagnostic_Error: if (errorCount) ++*errorCount; logLevel = Error; break; case CXDiagnostic_Warning: logLevel = Warning; break; case CXDiagnostic_Note: logLevel = Debug; break; case CXDiagnostic_Ignored: break; } const unsigned diagnosticOptions = (CXDiagnostic_DisplaySourceLocation| CXDiagnostic_DisplayColumn| CXDiagnostic_DisplaySourceRanges| CXDiagnostic_DisplayOption| CXDiagnostic_DisplayCategoryId| CXDiagnostic_DisplayCategoryName); const uint32_t fileId = createLocation(clang_getDiagnosticLocation(diagnostic), 0).fileId(); if (mVisitedFiles.contains(fileId)) { const String text = RTags::eatString(clang_formatDiagnostic(diagnostic, diagnosticOptions)); if (testLog(logLevel) || testLog(CompilationError)) { log(logLevel, "%s: %s => %s", mSourceInformation.sourceFile.constData(), mClangLines.at(build).constData(), text.constData()); compilationErrors.append(text); } const unsigned fixItCount = clang_getDiagnosticNumFixIts(diagnostic); RegExp rx; if (options & Server::IgnorePrintfFixits) { rx = "^%[A-Za-z0-9]\\+$"; } for (unsigned f=0; f<fixItCount; ++f) { CXSourceRange range; const String string = RTags::eatString(clang_getDiagnosticFixIt(diagnostic, f, &range)); unsigned startOffset; CXFile file; clang_getSpellingLocation(clang_getRangeStart(range), &file, 0, 0, &startOffset); unsigned endOffset; clang_getSpellingLocation(clang_getRangeEnd(range), 0, 0, 0, &endOffset); const Location loc(file, startOffset); if (options & Server::IgnorePrintfFixits && rx.indexIn(string) == 0) { error("Ignored fixit for %s: Replace %d-%d with [%s]", loc.path().constData(), startOffset, endOffset, string.constData()); } else { error("Fixit for %s: Replace %d-%d with [%s]", loc.path().constData(), startOffset, endOffset, string.constData()); compilationErrors.append(String::format<128>("Fixit for %s: Replace %d-%d with [%s]", loc.path().constData(), startOffset, endOffset, string.constData())); mData->fixIts[loc.fileId()].insert(FixIt(startOffset, endOffset, string)); } } } clang_disposeDiagnostic(diagnostic); } if (testLog(CompilationError)) sendDiagnostics(compilationErrors); return !isAborted(); }
bool ClangIndexer::diagnose() { if (!mClangUnit) { return false; } List<String> compilationErrors; const unsigned diagnosticCount = clang_getNumDiagnostics(mClangUnit); Map<Location, XmlEntry> xmlEntries; for (unsigned i=0; i<diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(mClangUnit, i); const CXSourceLocation diagLoc = clang_getDiagnosticLocation(diagnostic); const Location loc = createLocation(diagLoc, 0); const uint32_t fileId = loc.fileId(); if (mData->visited.value(fileId)) { const String msg = RTags::eatString(clang_getDiagnosticSpelling(diagnostic)); const CXDiagnosticSeverity sev = clang_getDiagnosticSeverity(diagnostic); XmlEntry::Type type = XmlEntry::None; switch (sev) { case CXDiagnostic_Warning: type = XmlEntry::Warning; break; case CXDiagnostic_Error: case CXDiagnostic_Fatal: type = XmlEntry::Error; break; default: break; } if (type != XmlEntry::None) { const unsigned rangeCount = clang_getDiagnosticNumRanges(diagnostic); bool ok = false; for (unsigned rangePos = 0; rangePos < rangeCount; ++rangePos) { const CXSourceRange range = clang_getDiagnosticRange(diagnostic, rangePos); const CXSourceLocation start = clang_getRangeStart(range); const CXSourceLocation end = clang_getRangeEnd(range); unsigned startOffset, endOffset; clang_getSpellingLocation(start, 0, 0, 0, &startOffset); clang_getSpellingLocation(end, 0, 0, 0, &endOffset); if (!rangePos && !startOffset && !endOffset) { // huh, range invalid? fall back to diag location break; } else { unsigned int line, column; clang_getPresumedLocation(start, 0, &line, &column); const Location key(loc.fileId(), line, column); xmlEntries[key] = XmlEntry(type, msg, endOffset - startOffset); ok = true; break; } } if (!ok) { unsigned line, column; clang_getPresumedLocation(diagLoc, 0, &line, &column); const Location key(loc.fileId(), line, column); xmlEntries[key] = XmlEntry(type, msg); // no length } } // logDirect(RTags::CompilationError, msg.constData()); const unsigned fixItCount = clang_getDiagnosticNumFixIts(diagnostic); for (unsigned f=0; f<fixItCount; ++f) { CXSourceRange range; const CXStringScope stringScope = clang_getDiagnosticFixIt(diagnostic, f, &range); CXSourceLocation start = clang_getRangeStart(range); unsigned line, column; CXString file; clang_getPresumedLocation(start, &file, &line, &column); CXStringScope fileScope(file); const Location loc = createLocation(clang_getCString(file), line, column); if (mData->visited.value(loc.fileId())) { unsigned int startOffset, endOffset; CXSourceLocation end = clang_getRangeEnd(range); clang_getSpellingLocation(start, 0, 0, 0, &startOffset); clang_getSpellingLocation(end, 0, 0, 0, &endOffset); const char *string = clang_getCString(stringScope); error("Fixit for %s:%d:%d: Replace %d characters with [%s]", loc.path().constData(), line, column, endOffset - startOffset, string); XmlEntry &entry = xmlEntries[Location(loc.fileId(), line, column)]; entry.type = XmlEntry::Fixit; if (entry.message.isEmpty()) { entry.message = String::format<64>("did you mean '%s'?", string); } entry.length = endOffset - startOffset; mData->fixIts[loc.fileId()].insert(FixIt(line, column, endOffset - startOffset, string)); } } } clang_disposeDiagnostic(diagnostic); } mData->xmlDiagnostics = "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>"; if (!xmlEntries.isEmpty()) { Map<Location, XmlEntry>::const_iterator entry = xmlEntries.begin(); const Map<Location, XmlEntry>::const_iterator end = xmlEntries.end(); const char *severities[] = { "none", "warning", "error", "fixit" }; uint32_t lastFileId = 0; while (entry != end) { const Location &loc = entry->first; const XmlEntry &xmlEntry = entry->second; if (loc.fileId() != lastFileId) { if (lastFileId) mData->xmlDiagnostics += "</file>"; lastFileId = loc.fileId(); mData->xmlDiagnostics += String::format<128>("<file name=\"%s\">", loc.path().constData()); } mData->xmlDiagnostics += String::format("<error line=\"%d\" column=\"%d\" %sseverity=\"%s\" message=\"%s\"/>", loc.line(), loc.column(), (xmlEntry.length <= 0 ? "" : String::format<32>("length=\"%d\" ", xmlEntry.length).constData()), severities[xmlEntry.type], xmlEscape(xmlEntry.message).constData()); ++entry; } if (lastFileId) mData->xmlDiagnostics += "</file>"; } for (Hash<uint32_t, bool>::const_iterator it = mData->visited.begin(); it != mData->visited.end(); ++it) { if (it->second) { const Map<Location, XmlEntry>::const_iterator x = xmlEntries.lower_bound(Location(it->first, 0, 0)); if (x == xmlEntries.end() || x->first.fileId() != it->first) { const String fn = Location::path(it->first); mData->xmlDiagnostics += String::format("<file name=\"%s\"/>", fn.constData()); } } } mData->xmlDiagnostics += "</checkstyle>"; return true; }