diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 31f124bc5b0..ed0c44885df 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -162,7 +162,9 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : connect(mUI->mActionAnalyzeFiles, &QAction::triggered, this, &MainWindow::analyzeFiles); connect(mUI->mActionAnalyzeDirectory, &QAction::triggered, this, &MainWindow::analyzeDirectory); connect(mUI->mActionSettings, &QAction::triggered, this, &MainWindow::programSettings); - connect(mUI->mActionClearResults, &QAction::triggered, this, &MainWindow::clearResults); + connect(mUI->mActionClearResults, &QAction::triggered, this, [this]() { + clearResults(); + }); connect(mUI->mActionOpenXML, &QAction::triggered, this, &MainWindow::openResults); connect(mUI->mActionShowStyle, &QAction::toggled, this, &MainWindow::showStyle); @@ -573,7 +575,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo }); } - clearResults(); + clearResults(recheckFiles); mIsLogfileLoaded = false; if (mProjectFile) { @@ -619,7 +621,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo if (!checkSettings.buildDir.empty()) { checkSettings.loadSummaries(); std::list sourcefiles; - AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, p.fileSettings); + AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, p.fileSettings, !recheckFiles.isEmpty()); } //mThread->SetanalyzeProject(true); @@ -638,7 +640,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo mUI->mResults->setCheckSettings(checkSettings); } -void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, const bool checkConfig) +void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, const bool checkConfig, const bool partialRecheck) { if (files.isEmpty()) return; @@ -648,7 +650,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c if (!getCppcheckSettings(checkSettings, *supprs)) return; - clearResults(); + clearResults(partialRecheck ? files : QStringList()); mIsLogfileLoaded = false; FileList pathList; @@ -660,7 +662,6 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c } QStringList fileNames = pathList.getFileList(); - mUI->mResults->clear(true); mUI->mResults->setResultsSource(ResultsTree::ResultsSource::Analysis); mThread->clearFiles(); @@ -701,7 +702,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c std::transform(fileNames.cbegin(), fileNames.cend(), std::back_inserter(sourcefiles), [](const QString& s) { return s.toStdString(); }); - AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, {}); + AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, {}, partialRecheck); } mThread->setCheckFiles(true); @@ -1445,8 +1446,14 @@ void MainWindow::reAnalyze(bool all) mUI->mResults->setCheckSettings(checkSettings); } -void MainWindow::clearResults() +void MainWindow::clearResults(const QStringList& selectedFiles) { + if (!selectedFiles.isEmpty()) { + mUI->mResults->clear(false); + for (const QString& f : selectedFiles) + mUI->mResults->clearRecheckFile(f); + return; + } if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { QDir dir(QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir()); for (const QString& f: dir.entryList(QDir::Files)) { @@ -1978,7 +1985,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis if (paths.isEmpty()) { paths << mCurrentDirectory; } - doAnalyzeFiles(paths, checkLib, checkConfig); + doAnalyzeFiles(paths, checkLib, checkConfig, !recheckFiles.isEmpty()); } void MainWindow::newProjectFile() diff --git a/gui/mainwindow.h b/gui/mainwindow.h index c654a2c94f0..72b11c49ec4 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -107,8 +107,11 @@ public slots: /** @brief Slot to reanalyze modified files */ void reAnalyzeModified(); - /** @brief Slot to clear all search results */ - void clearResults(); + /** + * @brief Slot to clear all search results or selected + * @param selectedFiles list to clear, leave empty for all + */ + void clearResults(const QStringList& selectedFiles = QStringList()); /** @brief Slot to open XML report file */ void openResults(); @@ -309,7 +312,7 @@ private slots: * @param checkLib Flag to indicate if library should be checked * @param checkConfig Flag to indicate if the configuration should be checked. */ - void doAnalyzeProject(ImportProject p, bool checkLib = false, bool checkConfig = false, const QStringList& recheckFiles = QStringList()); + void doAnalyzeProject(ImportProject p, bool checkLib = false, bool checkConfig = false, const QStringList &recheckFiles = QStringList()); /** * @brief Analyze all files specified in parameter files @@ -317,8 +320,9 @@ private slots: * @param files List of files and/or directories to analyze * @param checkLib Flag to indicate if library should be checked * @param checkConfig Flag to indicate if the configuration should be checked. + * @param partialRecheck Flag to indicate a partial recheck. */ - void doAnalyzeFiles(const QStringList &files, bool checkLib = false, bool checkConfig = false); + void doAnalyzeFiles(const QStringList &files, bool checkLib = false, bool checkConfig = false, bool partialRecheck = false); /** * @brief Get our default cppcheck settings and read project file. diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index ed8b3c7412e..408866fc2ef 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -427,7 +427,7 @@ void ResultsTree::clear(const QString &filename) if (stripped == fileItem->text() || filename == fileItem->errorItem->file0) { - mErrorList.removeAll(fileItem->errorItem->toString()); + removeFileErrorsFromErrorList(fileItem); mModel->removeRow(i); break; } @@ -445,7 +445,7 @@ void ResultsTree::clearRecheckFile(const QString &filename) QString storedfile = fileItem->getErrorPathItem().file; storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile); if (actualfile == storedfile) { - mErrorList.removeAll(fileItem->errorItem->toString()); + removeFileErrorsFromErrorList(fileItem); mModel->removeRow(i); break; } @@ -1132,6 +1132,15 @@ void ResultsTree::saveErrors(Report *report, const ResultItem *fileItem) const } } +void ResultsTree::removeFileErrorsFromErrorList(const ResultItem *fileItem) +{ + for (int i{0}; i < fileItem->rowCount(); ++i) { + const auto *errorItem = dynamic_cast(fileItem->child(i, COLUMN_FILE)); + if (errorItem && errorItem->errorItem) + mErrorList.removeAll(errorItem->errorItem->toString()); + } +} + void ResultsTree::updateFromOldReport(const QString &filename) { showColumn(COLUMN_SINCE_DATE); @@ -1195,6 +1204,7 @@ void ResultsTree::updateSettings(bool showFullPath, void ResultsTree::setCheckDirectory(const QString &dir) { mCheckPath = dir; + refreshFilePaths(); } const QString& ResultsTree::getCheckDirectory() const diff --git a/gui/resultstree.h b/gui/resultstree.h index 23fe5567ed9..b0634e96396 100644 --- a/gui/resultstree.h +++ b/gui/resultstree.h @@ -356,6 +356,12 @@ protected slots: */ void saveErrors(Report *report, const ResultItem *fileItem) const; + /** + * @brief Remove all errors of a file item from the duplicate-check list + * @param fileItem Item whose errors to remove + */ + void removeFileErrorsFromErrorList(const ResultItem *fileItem); + /** * @brief Convert a severity string to a icon filename * diff --git a/lib/analyzerinfo.cpp b/lib/analyzerinfo.cpp index 36803b897b1..cf722e17431 100644 --- a/lib/analyzerinfo.cpp +++ b/lib/analyzerinfo.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -51,18 +52,50 @@ static std::string getFilename(const std::string &fullpath) return fullpath.substr(pos1,pos2); } -void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings) +void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings, bool merge) { const std::string filesTxt(buildDir + "/files.txt"); + + std::string keptLines; + std::map fileCount; + + if (merge) { + std::set current; + for (const std::string &f : sourcefiles) + current.insert(Path::simplifyPath(f)); + for (const FileSettings &fs : fileSettings) + current.insert(Path::simplifyPath(fs.filename())); + + std::ifstream fin(filesTxt); + std::string line; + while (std::getline(fin, line)) { + Info info; + if (!info.parse(line)) + continue; + if (current.count(info.sourceFile)) + continue; + keptLines += line + '\n'; + + const std::string::size_type dotA = info.afile.rfind(".a"); + if (dotA != std::string::npos) { + const std::string base = info.afile.substr(0, dotA); + unsigned int n = 0; + if (strToInt(info.afile.substr(dotA + 2), n)) { + unsigned int &count = fileCount[base]; + count = std::max(count, n); + } + } + } + } + std::ofstream fout(filesTxt); - fout << getFilesTxt(sourcefiles, fileSettings); + fout << keptLines; + fout << getFilesTxt(sourcefiles, fileSettings, fileCount); } -std::string AnalyzerInformation::getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings) { +std::string AnalyzerInformation::getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings, std::map fileCount) { std::ostringstream ret; - std::map fileCount; - for (const std::string &f : sourcefiles) { const std::string afile = getFilename(f); ret << afile << ".a" << (++fileCount[afile]) << sep << sep << sep << Path::simplifyPath(f) << '\n'; diff --git a/lib/analyzerinfo.h b/lib/analyzerinfo.h index 75674a22f82..4f7a1a72a39 100644 --- a/lib/analyzerinfo.h +++ b/lib/analyzerinfo.h @@ -27,6 +27,7 @@ #include #include #include +#include #include class ErrorMessage; @@ -57,7 +58,7 @@ class CPPCHECKLIB AnalyzerInformation { public: ~AnalyzerInformation(); - static void writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings); + static void writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::list &fileSettings, bool merge = false); /** Close current TU.analyzerinfo file */ void close(); @@ -85,7 +86,7 @@ class CPPCHECKLIB AnalyzerInformation { static std::string processFilesTxt(const std::string& buildDir, const std::function& handler, bool debug = false); protected: - static std::string getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings); + static std::string getFilesTxt(const std::list &sourcefiles, const std::list &fileSettings, std::map fileCount = {}); static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, size_t fsFileId);