/* Handle syntax checking request, message format: source_length: [#src_length#] <# SOURCE CODE #> */ void completion_doSyntaxCheck(completion_Session *session, FILE *fp) { unsigned int i_diag = 0, n_diag; CXDiagnostic diag; CXString dmsg; /* get a copy of fresh source file */ completion_readSourcefile(session, fp); /* reparse the source to retrieve diagnostic message */ completion_reparseTranslationUnit(session); /* dump all diagnostic messages to fp */ n_diag = clang_getNumDiagnostics(session->cx_tu); for ( ; i_diag < n_diag; i_diag++) { diag = clang_getDiagnostic(session->cx_tu, i_diag); dmsg = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); fprintf(stdout, "%s\n", clang_getCString(dmsg)); clang_disposeString(dmsg); clang_disposeDiagnostic(diag); } fprintf(stdout, "$"); fflush(stdout); /* end of output */ }
Diagnostic::~Diagnostic() { if (diagnostic_) { clang_disposeDiagnostic(diagnostic_); } }
std::vector<diagnostic> translation_unit::diagnose() { // Get all the diagnostics uint32_t n = clang_getNumDiagnostics(mUnit); if (n == 0) return {}; std::vector<diagnostic> ret; ret.reserve(n); for (uint32_t i = 0; i < n; ++i) { CXFile file; uint32_t row = 0, col = 0, offset = 0; CXDiagnostic diag = clang_getDiagnostic(mUnit, i); CXSourceLocation loc = clang_getDiagnosticLocation(diag); clang_getExpansionLocation( loc, &file, &row, &col, &offset ); ret.push_back({ { cx2std(clang_getFileName(file)), row, col }, clang_getDiagnosticSeverity(diag), diagnostic_text(diag), diagnostic_summary(diag) }); clang_disposeDiagnostic(diag); } return ret; }
void PrintDiagnostics(CXTranslationUnit TU) { int i, n = clang_getNumDiagnostics(TU); for (i = 0; i != n; ++i) { CXDiagnostic Diag = clang_getDiagnostic(TU, i); PrintDiagnostic(Diag); clang_disposeDiagnostic(Diag); } }
void DisplayDiagnostics() { unsigned NumDiagnostics = clang_getNumDiagnostics(ClangTU); for (unsigned i = 0; i < NumDiagnostics; ++i) { auto Diag = clang_getDiagnostic(ClangTU, i); DEBUG(llvm::dbgs() << clang_getCString(clang_formatDiagnostic( Diag, clang_defaultDiagnosticDisplayOptions())) << "\n"); clang_disposeDiagnostic(Diag); } }
Diagnostic &Diagnostic::operator=(Diagnostic &&other) { if (this != &other) { clang_disposeDiagnostic(cxDiagnostic); cxDiagnostic = std::move(other.cxDiagnostic); other.cxDiagnostic = nullptr; } return *this; }
Diagnostic& Diagnostic::operator=(Diagnostic&& other) { if (diagnostic_) { clang_disposeDiagnostic(diagnostic_); } diagnostic_ = std::move(other.diagnostic_); other.diagnostic_ = nullptr; return *this; }
void Irony::check(const std::string &file, const std::vector<std::string> &flags, const std::vector<CXUnsavedFile> &unsavedFiles) { std::cout << "("; unsigned numDiag = 0; int fatals = 0; int errors = 0; int warnings = 0; CXTranslationUnit tu = tuManager_.parse(file, flags, unsavedFiles); if (tu) { numDiag = clang_getNumDiagnostics(tu); } else { fatals = 1; } for (unsigned i = 0; i < numDiag; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(tu, i); switch (clang_getDiagnosticSeverity(diagnostic)) { case CXDiagnostic_Fatal: fatals++; break; case CXDiagnostic_Error: errors++; break; case CXDiagnostic_Warning: warnings++; break; default: break; } clang_disposeDiagnostic(diagnostic); } if (fatals > 0) std::cout << " :fatals " << fatals; if (errors > 0) std::cout << " :errors " << errors; if (warnings > 0) std::cout << " :warnings " << warnings; std::cout << ")\n"; }
static void dumpDiagnostics(const CXTranslationUnit &tu) { std::cout << "(\n"; std::string file; for (unsigned i = 0, diagnosticCount = clang_getNumDiagnostics(tu); i < diagnosticCount; ++i) { CXDiagnostic diagnostic = clang_getDiagnostic(tu, i); CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); unsigned line, column, offset; if (clang_equalLocations(location, clang_getNullLocation())) { file.clear(); line = 0; column = 0; offset = 0; } else { CXFile cxFile; // clang_getInstantiationLocation() has been marked deprecated and // is aimed to be replaced by clang_getExpansionLocation(). #if CINDEX_VERSION >= 6 clang_getExpansionLocation(location, &cxFile, &line, &column, &offset); #else clang_getInstantiationLocation(location, &cxFile, &line, &column, &offset); #endif file = cxStringToStd(clang_getFileName(cxFile)); } const char *severity = diagnosticSeverity(diagnostic); std::string message = cxStringToStd(clang_getDiagnosticSpelling(diagnostic)); std::cout << '(' << support::quoted(file) // << ' ' << line // << ' ' << column // << ' ' << offset // << ' ' << severity // << ' ' << support::quoted(message) // << ")\n"; clang_disposeDiagnostic(diagnostic); } std::cout << ")\n"; }
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); } }
void ClangUtils::printDiagnosticsToLog(CXTranslationUnit& TU) { //// Report diagnostics to the log file const unsigned diagCount = clang_getNumDiagnostics(TU); for(unsigned i=0; i<diagCount; i++) { CXDiagnostic diag = clang_getDiagnostic(TU, i); CXString diagStr = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); wxString wxDiagString = wxString(clang_getCString(diagStr), wxConvUTF8); if(!wxDiagString.Contains(wxT("'dllimport' attribute"))) { CL_DEBUG(wxT("Diagnostic: %s"), wxDiagString.c_str()); } clang_disposeString(diagStr); clang_disposeDiagnostic(diag); } }
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; }
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, "$"); }
QStringList formattedDiagnostics(const Unit::Ptr &unit) { QStringList diags; if (!unit->isLoaded()) return diags; const unsigned count = unit->getNumDiagnostics(); for (unsigned i = 0; i < count; ++i) { CXDiagnostic diag = unit->getDiagnostic(i); unsigned opt = CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges | CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId | CXDiagnostic_DisplayCategoryName ; diags << getQString(clang_formatDiagnostic(diag, opt)); clang_disposeDiagnostic(diag); } return diags; }
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"; } }
void ClangParser::start(const QString &fileName, QStringList &includeFiles) { static QStringList includePath = Config::getList("include-path"); static QStringList clangFlags = Config::getList("clang-flags"); p->fileName = fileName; p->index = clang_createIndex(0, 0); p->curLine = 1; p->curToken = 0; char **argv = (char **)malloc(sizeof(char *) * (4 + Doxy_Globals::inputPaths.count() + includePath.count() + clangFlags.count())); int argc = 0; // add include paths for input files for (auto item : Doxy_Globals::inputPaths) { QString inc = "-I" + item; argv[argc] = strdup(inc.toUtf8()); ++argc; } // add external include paths for (uint i = 0; i < includePath.count(); i++) { QString inc = "-I" + includePath.at(i); argv[argc++] = strdup(inc.toUtf8()); } // user specified options for (uint i = 0; i < clangFlags.count(); i++) { argv[argc++] = strdup(clangFlags.at(i).toUtf8()); } // extra options argv[argc++] = strdup("-ferror-limit=0"); argv[argc++] = strdup("-x"); // Since we can be presented with an .h file that can contain C, C++, or Objective C, // we need to configure the parser before knowing this. // Use the source file to detected the language. Detection will fail if you // pass a bunch of .h files containing ObjC code and no source SrcLangExt lang = getLanguageFromFileName(fileName); if (lang == SrcLangExt_ObjC || p->detectedLang != ClangParser::Private::Detected_Cpp) { QString fn = fileName; if (p->detectedLang == ClangParser::Private::Detected_Cpp && (fn.right(4).toLower() == ".cpp" || fn.right(4).toLower() == ".cxx" || fn.right(3).toLower() == ".cc" || fn.right(2).toLower() == ".c")) { // fall back to C/C++ once we see an extension that indicates this p->detectedLang = ClangParser::Private::Detected_Cpp; } else if (fn.right(3).toLower() == ".mm") { // switch to Objective C++ p->detectedLang = ClangParser::Private::Detected_ObjCpp; } else if (fn.right(2).toLower() == ".m") { // switch to Objective C p->detectedLang = ClangParser::Private::Detected_ObjC; } } switch (p->detectedLang) { case ClangParser::Private::Detected_Cpp: argv[argc++] = strdup("c++"); break; case ClangParser::Private::Detected_ObjC: argv[argc++] = strdup("objective-c"); break; case ClangParser::Private::Detected_ObjCpp: argv[argc++] = strdup("objective-c++"); break; } // provide the input and and its dependencies as unsaved files so we can pass the filtered versions static bool filterSourceFiles = Config::getBool("filter-source-files"); argv[argc++] = strdup(fileName.toUtf8()); uint numUnsavedFiles = includeFiles.count() + 1; p->numFiles = numUnsavedFiles; p->sources = new QByteArray[numUnsavedFiles]; p->ufs = new CXUnsavedFile[numUnsavedFiles]; p->sources[0] = detab(fileToString(fileName, filterSourceFiles, true)).toUtf8(); p->ufs[0].Filename = strdup(fileName.toUtf8()); p->ufs[0].Contents = p->sources[0].constData(); p->ufs[0].Length = p->sources[0].length(); // uint i = 1; for (auto item : includeFiles) { p->fileMapping.insert(item, i); p->sources[i] = detab(fileToString(item, filterSourceFiles, true)).toUtf8(); p->ufs[i].Filename = strdup(item.toUtf8()); p->ufs[i].Contents = p->sources[i].constData(); p->ufs[i].Length = p->sources[i].length(); i++; } // let libclang do the actual parsing CXErrorCode errorCode = clang_parseTranslationUnit2(p->index, 0, argv, argc, 0, 0, CXTranslationUnit_DetailedPreprocessingRecord, &(p->tu) ); // free arguments for (int i = 0; i < argc; ++i) { free(argv[i]); } free(argv); if (p->tu) { // filter out any includes not found by the clang parser determineInputFiles(includeFiles); // show any warnings the compiler produced uint n = clang_getNumDiagnostics(p->tu); for (uint i = 0; i != n; ++i) { CXDiagnostic diag = clang_getDiagnostic(p->tu, i); CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); err("Clang parser warning -- %s\n", clang_getCString(string)); clang_disposeString(string); clang_disposeDiagnostic(diag); } // create a source range for the given file QFileInfo fi(fileName); CXFile f = clang_getFile(p->tu, fileName.toUtf8()); CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0); CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[0].Length); CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd); // produce a token stream for the file clang_tokenize(p->tu, fileRange, &p->tokens, &p->numTokens); // produce cursors for each token in the stream p->cursors = new CXCursor[p->numTokens]; clang_annotateTokens(p->tu, p->tokens, p->numTokens, p->cursors); } else { p->tokens = 0; p->numTokens = 0; p->cursors = 0; err("Clang failed to parse translation unit -- %s\n", qPrintable(fileName)); } }
Diagnostic::~Diagnostic() { clang_disposeDiagnostic(cxDiagnostic); }
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(); }
diagnostic::~diagnostic() { if(diag) clang_disposeDiagnostic(diag); }
void ClangParser::start(const char *fileName,QStrList &filesInTranslationUnit) { static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING); static QStrList &includePath = Config_getList(INCLUDE_PATH); static QStrList clangOptions = Config_getList(CLANG_OPTIONS); static QCString clangCompileDatabase = Config_getList(CLANG_COMPILATION_DATABASE_PATH); if (!clangAssistedParsing) return; //printf("ClangParser::start(%s)\n",fileName); p->fileName = fileName; p->index = clang_createIndex(0, 0); p->curLine = 1; p->curToken = 0; QDictIterator<void> di(Doxygen::inputPaths); int argc=0; std::string error; // load a clang compilation database (https://clang.llvm.org/docs/JSONCompilationDatabase.html) // this only needs to be loaded once, and could be refactored to a higher level function static std::unique_ptr<clang::tooling::CompilationDatabase> db = clang::tooling::CompilationDatabase::loadFromDirectory(clangCompileDatabase.data(), error); int clang_option_len = 0; std::vector<clang::tooling::CompileCommand> command; if (strcmp(clangCompileDatabase, "0") != 0) { if (db == nullptr) { // user specified a path, but DB file was not found err("%s using clang compilation database path of: \"%s\"\n", error.c_str(), clangCompileDatabase.data()); } else { // check if the file we are parsing is in the DB command = db->getCompileCommands(fileName); if (!command.empty() ) { // it's possible to have multiple entries for the same file, so use the last entry clang_option_len = command[command.size()-1].CommandLine.size(); } } } char **argv = (char**)malloc(sizeof(char*)*(4+Doxygen::inputPaths.count()+includePath.count()+clangOptions.count()+clang_option_len)); if (!command.empty() ) { std::vector<std::string> options = command[command.size()-1].CommandLine; // copy each compiler option used from the database. Skip the first which is compiler exe. for (auto option = options.begin()+1; option != options.end(); option++) { argv[argc++] = strdup(option->c_str()); } // this extra addition to argv is accounted for as we are skipping the first entry in argv[argc++]=strdup("-w"); // finally, turn off warnings. } else { // add include paths for input files for (di.toFirst();di.current();++di,++argc) { QCString inc = QCString("-I")+di.currentKey(); argv[argc]=strdup(inc.data()); //printf("argv[%d]=%s\n",argc,argv[argc]); } // add external include paths for (uint i=0;i<includePath.count();i++) { QCString inc = QCString("-I")+includePath.at(i); argv[argc++]=strdup(inc.data()); } // user specified options for (uint i=0;i<clangOptions.count();i++) { argv[argc++]=strdup(clangOptions.at(i)); } // extra options argv[argc++]=strdup("-ferror-limit=0"); argv[argc++]=strdup("-x"); // Since we can be presented with a .h file that can contain C/C++ or // Objective C code and we need to configure the parser before knowing this, // we use the source file to detected the language. Detection will fail if you // pass a bunch of .h files containing ObjC code, and no sources :-( SrcLangExt lang = getLanguageFromFileName(fileName); if (lang==SrcLangExt_ObjC || p->detectedLang!=ClangParser::Private::Detected_Cpp) { QCString fn = fileName; if (p->detectedLang==ClangParser::Private::Detected_Cpp && (fn.right(4).lower()==".cpp" || fn.right(4).lower()==".cxx" || fn.right(3).lower()==".cc" || fn.right(2).lower()==".c")) { // fall back to C/C++ once we see an extension that indicates this p->detectedLang = ClangParser::Private::Detected_Cpp; } else if (fn.right(3).lower()==".mm") // switch to Objective C++ { p->detectedLang = ClangParser::Private::Detected_ObjCpp; } else if (fn.right(2).lower()==".m") // switch to Objective C { p->detectedLang = ClangParser::Private::Detected_ObjC; } } switch(p->detectedLang) { case ClangParser::Private::Detected_Cpp: argv[argc++]=strdup("c++"); break; case ClangParser::Private::Detected_ObjC: argv[argc++]=strdup("objective-c"); break; case ClangParser::Private::Detected_ObjCpp: argv[argc++]=strdup("objective-c++"); break; } // provide the input and and its dependencies as unsaved files so we can // pass the filtered versions argv[argc++]=strdup(fileName); } static bool filterSourceFiles = Config_getBool(FILTER_SOURCE_FILES); //printf("source %s ----------\n%s\n-------------\n\n", // fileName,p->source.data()); uint numUnsavedFiles = filesInTranslationUnit.count()+1; p->numFiles = numUnsavedFiles; p->sources = new QCString[numUnsavedFiles]; p->ufs = new CXUnsavedFile[numUnsavedFiles]; p->sources[0] = detab(fileToString(fileName,filterSourceFiles,TRUE)); p->ufs[0].Filename = strdup(fileName); p->ufs[0].Contents = p->sources[0].data(); p->ufs[0].Length = p->sources[0].length(); QStrListIterator it(filesInTranslationUnit); uint i=1; for (it.toFirst();it.current() && i<numUnsavedFiles;++it,i++) { p->fileMapping.insert(it.current(),new uint(i)); p->sources[i] = detab(fileToString(it.current(),filterSourceFiles,TRUE)); p->ufs[i].Filename = strdup(it.current()); p->ufs[i].Contents = p->sources[i].data(); p->ufs[i].Length = p->sources[i].length(); } // let libclang do the actual parsing p->tu = clang_parseTranslationUnit(p->index, 0, argv, argc, p->ufs, numUnsavedFiles, CXTranslationUnit_DetailedPreprocessingRecord); // free arguments for (int i=0;i<argc;++i) { free(argv[i]); } free(argv); if (p->tu) { // filter out any includes not found by the clang parser determineInputFilesInSameTu(filesInTranslationUnit); // show any warnings that the compiler produced for (uint i=0, n=clang_getNumDiagnostics(p->tu); i!=n; ++i) { CXDiagnostic diag = clang_getDiagnostic(p->tu, i); CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); err("%s [clang]\n",clang_getCString(string)); clang_disposeString(string); clang_disposeDiagnostic(diag); } // create a source range for the given file QFileInfo fi(fileName); CXFile f = clang_getFile(p->tu, fileName); CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0); CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[0].Length); CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd); // produce a token stream for the file clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens); // produce cursors for each token in the stream p->cursors=new CXCursor[p->numTokens]; clang_annotateTokens(p->tu,p->tokens,p->numTokens,p->cursors); } else { p->tokens = 0; p->numTokens = 0; p->cursors = 0; err("clang: Failed to parse translation unit %s\n",fileName); } }
void ClangParser::start(const char *fileName,QStrList &filesInTranslationUnit) { static bool clangAssistedParsing = Config_getBool("CLANG_ASSISTED_PARSING"); static QStrList &includePath = Config_getList("INCLUDE_PATH"); static QStrList clangOptions = Config_getList("CLANG_OPTIONS"); if (!clangAssistedParsing) return; //printf("ClangParser::start(%s)\n",fileName); p->fileName = fileName; p->index = clang_createIndex(0, 0); p->curLine = 1; p->curToken = 0; char **argv = (char**)malloc(sizeof(char*)*(4+Doxygen::inputPaths.count()+includePath.count()+clangOptions.count())); QDictIterator<void> di(Doxygen::inputPaths); int argc=0; // add include paths for input files for (di.toFirst();di.current();++di,++argc) { QCString inc = QCString("-I")+di.currentKey(); argv[argc]=strdup(inc.data()); //printf("argv[%d]=%s\n",argc,argv[argc]); } // add external include paths for (uint i=0;i<includePath.count();i++) { QCString inc = QCString("-I")+includePath.at(i); argv[argc++]=strdup(inc.data()); } // user specified options for (uint i=0;i<clangOptions.count();i++) { argv[argc++]=strdup(clangOptions.at(i)); } // extra options argv[argc++]=strdup("-ferror-limit=0"); argv[argc++]=strdup("-x"); // Since we can be presented with a .h file that can contain C/C++ or // Objective C code and we need to configure the parser before knowing this, // we use the source file to detected the language. Detection will fail if you // pass a bunch of .h files containing ObjC code, and no sources :-( SrcLangExt lang = getLanguageFromFileName(fileName); if (lang==SrcLangExt_ObjC || p->detectedLang!=ClangParser::Private::Detected_Cpp) { QCString fn = fileName; if (p->detectedLang==ClangParser::Private::Detected_Cpp && (fn.right(4).lower()==".cpp" || fn.right(4).lower()==".cxx" || fn.right(3).lower()==".cc" || fn.right(2).lower()==".c")) { // fall back to C/C++ once we see an extension that indicates this p->detectedLang = ClangParser::Private::Detected_Cpp; } else if (fn.right(3).lower()==".mm") // switch to Objective C++ { p->detectedLang = ClangParser::Private::Detected_ObjCpp; } else if (fn.right(2).lower()==".m") // switch to Objective C { p->detectedLang = ClangParser::Private::Detected_ObjC; } } switch(p->detectedLang) { case ClangParser::Private::Detected_Cpp: argv[argc++]=strdup("c++"); break; case ClangParser::Private::Detected_ObjC: argv[argc++]=strdup("objective-c"); break; case ClangParser::Private::Detected_ObjCpp: argv[argc++]=strdup("objective-c++"); break; } // provide the input and and its dependencies as unsaved files so we can // pass the filtered versions argv[argc++]=strdup(fileName); static bool filterSourceFiles = Config_getBool("FILTER_SOURCE_FILES"); //printf("source %s ----------\n%s\n-------------\n\n", // fileName,p->source.data()); uint numUnsavedFiles = filesInTranslationUnit.count()+1; p->numFiles = numUnsavedFiles; p->sources = new QCString[numUnsavedFiles]; p->ufs = new CXUnsavedFile[numUnsavedFiles]; p->sources[0] = detab(fileToString(fileName,filterSourceFiles,TRUE)); p->ufs[0].Filename = strdup(fileName); p->ufs[0].Contents = p->sources[0].data(); p->ufs[0].Length = p->sources[0].length(); QStrListIterator it(filesInTranslationUnit); uint i=1; for (it.toFirst();it.current() && i<numUnsavedFiles;++it,i++) { p->fileMapping.insert(it.current(),new uint(i)); p->sources[i] = detab(fileToString(it.current(),filterSourceFiles,TRUE)); p->ufs[i].Filename = strdup(it.current()); p->ufs[i].Contents = p->sources[i].data(); p->ufs[i].Length = p->sources[i].length(); } // let libclang do the actual parsing p->tu = clang_parseTranslationUnit(p->index, 0, argv, argc, p->ufs, numUnsavedFiles, CXTranslationUnit_DetailedPreprocessingRecord); // free arguments for (int i=0;i<argc;++i) { free(argv[i]); } free(argv); if (p->tu) { // filter out any includes not found by the clang parser determineInputFilesInSameTu(filesInTranslationUnit); // show any warnings that the compiler produced for (uint i=0, n=clang_getNumDiagnostics(p->tu); i!=n; ++i) { CXDiagnostic diag = clang_getDiagnostic(p->tu, i); CXString string = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); err("%s [clang]\n",clang_getCString(string)); clang_disposeString(string); clang_disposeDiagnostic(diag); } // create a source range for the given file QFileInfo fi(fileName); CXFile f = clang_getFile(p->tu, fileName); CXSourceLocation fileBegin = clang_getLocationForOffset(p->tu, f, 0); CXSourceLocation fileEnd = clang_getLocationForOffset(p->tu, f, p->ufs[0].Length); CXSourceRange fileRange = clang_getRange(fileBegin, fileEnd); // produce a token stream for the file clang_tokenize(p->tu,fileRange,&p->tokens,&p->numTokens); // produce cursors for each token in the stream p->cursors=new CXCursor[p->numTokens]; clang_annotateTokens(p->tu,p->tokens,p->numTokens,p->cursors); } else { p->tokens = 0; p->numTokens = 0; p->cursors = 0; err("clang: Failed to parse translation unit %s\n",fileName); } }
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()); } }
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; }