Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions gui/checkthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,15 @@ CheckThread::CheckThread(ThreadResult &result, int threadIndex)
void CheckThread::setSettings(const Settings &settings, std::shared_ptr<Suppressions> supprs)
{
mFiles.clear();
mFileSettings.clear();
mSettings = settings; // this is a copy
mSuppressions = std::move(supprs);
}

void CheckThread::analyseWholeProgram(const std::list<FileWithDetails> &files, const std::string& ctuInfo)
void CheckThread::analyseWholeProgram(const std::list<FileWithDetails> &files, const std::list<FileSettings> &fileSettings, const std::string& ctuInfo)
{
mFiles = files;
mFileSettings = fileSettings;
mAnalyseWholeProgram = true;
mCtuInfo = ctuInfo;
start();
Expand All @@ -136,8 +138,9 @@ void CheckThread::run()
std::string ctuInfo;
ctuInfo.swap(mCtuInfo);
qDebug() << "Whole program analysis";
cppcheck.analyseWholeProgram(mSettings.buildDir, mFiles, {}, ctuInfo);
cppcheck.analyseWholeProgram(mSettings.buildDir, mFiles, mFileSettings, ctuInfo);
mFiles.clear();
mFileSettings.clear();
emit done();
return;
}
Expand Down
8 changes: 5 additions & 3 deletions gui/checkthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,11 @@ class CheckThread : public QThread {

/**
* @brief Run whole program analysis
* @param files All files
* @param ctuInfo Ctu info for addons
* @param files All files
* @param fileSettings All files of the project
* @param ctuInfo Ctu info for addons
*/
void analyseWholeProgram(const std::list<FileWithDetails> &files, const std::string& ctuInfo);
void analyseWholeProgram(const std::list<FileWithDetails> &files, const std::list<FileSettings> &fileSettings, const std::string& ctuInfo);

void setAddonsAndTools(const QStringList &addonsAndTools) {
mAddonsAndTools = addonsAndTools;
Expand Down Expand Up @@ -153,6 +154,7 @@ class CheckThread : public QThread {
bool isSuppressed(const SuppressionList::ErrorMessage &errorMessage) const;

std::list<FileWithDetails> mFiles;
std::list<FileSettings> mFileSettings;
bool mAnalyseWholeProgram{};
std::string mCtuInfo;
QStringList mAddonsAndTools;
Expand Down
102 changes: 80 additions & 22 deletions gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

#include <algorithm>
#include <cstddef>
#include <fstream>
#include <iterator>
#include <list>
#include <memory>
Expand Down Expand Up @@ -96,6 +97,7 @@
#include <QtNetwork/QNetworkRequest>
#include <QPushButton>
#include <QRegularExpression>
#include <QSet>
#include <QSettings>
#include <QSize>
#include <QTimer>
Expand All @@ -118,6 +120,18 @@ static QString fromNativePath(const QString& p) {
#endif
}

// The files selected in the results view are not necessarily spelled the same way as the
// project or files.txt spells them, compare them as absolute paths
static QString comparablePath(const QString& path) {
return QDir::cleanPath(QFileInfo(path).absoluteFilePath());
}

static QSet<QString> comparablePaths(const QStringList& paths) {
QSet<QString> ret;
std::transform(paths.cbegin(), paths.cend(), std::inserter(ret, ret.end()), comparablePath);
return ret;
}

template<typename T = std::vector<std::string>>
static T toStdStringList(const QStringList& stringList) {
T ret;
Expand Down Expand Up @@ -568,21 +582,31 @@ void MainWindow::saveSettings() const
mUI->mResults->saveSettings(mSettings);
}

void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bool checkConfig, const QStringList& recheckFiles)
void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bool checkConfig, const QStringList& selectedFiles, const Recheck recheck)
{
Settings checkSettings;
auto supprs = std::make_shared<Suppressions>();
if (!getCppcheckSettings(checkSettings, *supprs))
return;

// filter requested files
if (!recheckFiles.isEmpty()) {
// Filter the selected files
if (recheck == Recheck::Selected) {
const QSet<QString> selected = comparablePaths(selectedFiles);
p.fileSettings.remove_if([&](const FileSettings& fs) {
return !recheckFiles.contains(QString::fromStdString(fs.filename()));
return !selected.contains(comparablePath(QString::fromStdString(fs.filename())));
});
if (p.fileSettings.empty()) {
QMessageBox msg(QMessageBox::Information,
"Cppcheck",
tr("None of the selected files is part of the project, nothing to analyze."),
QMessageBox::Ok,
this);
msg.exec();
return;
}
}

clearResults();
clearResultsForAnalysis(checkSettings.buildDir, selectedFiles, recheck);

mIsLogfileLoaded = false;
if (mProjectFile) {
Expand Down Expand Up @@ -647,7 +671,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 QStringList& selectedFiles, const Recheck recheck)
{
if (files.isEmpty())
return;
Expand All @@ -657,7 +681,7 @@ void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLib, c
if (!getCppcheckSettings(checkSettings, *supprs))
return;

clearResults();
clearResultsForAnalysis(checkSettings.buildDir, selectedFiles, recheck);

mIsLogfileLoaded = false;
FileList pathList;
Expand All @@ -669,7 +693,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();

Expand Down Expand Up @@ -1379,11 +1402,6 @@ void MainWindow::reAnalyzeSelected(const QStringList& files)
return;

if (mProjectFile) {
// Clear details, statistics and progress
mUI->mResults->clear(false);
for (int i = 0; i < files.size(); ++i)
mUI->mResults->clearRecheckFile(files[i]);

analyzeProject(mProjectFile, files);
return;
}
Expand All @@ -1393,10 +1411,7 @@ void MainWindow::reAnalyzeSelected(const QStringList& files)
if (!getCppcheckSettings(checkSettings, *supprs))
return;

// Clear details, statistics and progress
mUI->mResults->clear(false);
for (int i = 0; i < files.size(); ++i)
mUI->mResults->clearRecheckFile(files[i]);
clearResultsForAnalysis(checkSettings.buildDir, files, Recheck::Selected);

mCurrentDirectory = mUI->mResults->getCheckDirectory();
FileList pathList;
Expand Down Expand Up @@ -1451,7 +1466,7 @@ void MainWindow::reAnalyze(bool all)
void MainWindow::clearResults()
{
if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) {
QDir dir(QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir());
QDir dir(QDir(QFileInfo(mProjectFile->getFilename()).absolutePath()).filePath(mProjectFile->getBuildDir()));
for (const QString& f: dir.entryList(QDir::Files)) {
if (!f.endsWith("files.txt")) {
static const QRegularExpression rx("^.*.s[0-9]+$");
Expand All @@ -1465,6 +1480,42 @@ void MainWindow::clearResults()
enableResultsButtons();
}

void MainWindow::clearResultsForAnalysis(const std::string& buildDir, const QStringList& selectedFiles, const Recheck recheck)
{
switch (recheck) {
case Recheck::None:
clearResults();
break;
case Recheck::Selected:
// Only the selected files are analyzed, the results of the other files are kept
mUI->mResults->clear(false);
for (const QString& f : selectedFiles)
mUI->mResults->clearRecheckFile(f);
enableResultsButtons();
break;
case Recheck::Cached: {
// Every file reports its results again, either analyzed or replayed from the analyzer
// information. Remove the analyzer information of the selected files so that they really
// are analyzed again, also by the clang tools that keep their results next to it.
const QSet<QString> selected = comparablePaths(selectedFiles);
QDir dir(QString::fromStdString(buildDir));
std::ifstream filesTxt(buildDir + "/files.txt");
std::string line;
while (std::getline(filesTxt, line)) {
AnalyzerInformation::Info info;
if (!info.parse(line) || !selected.contains(comparablePath(QString::fromStdString(info.sourceFile))))
continue;
const QString afile = QString::fromStdString(info.afile);
for (const QString& f : dir.entryList({afile, afile + ".*"}, QDir::Files))
dir.remove(f);
}
mUI->mResults->clear(true);
enableResultsButtons();
break;
}
}
}

void MainWindow::openResults()
{
if (mUI->mResults->hasResults()) {
Expand Down Expand Up @@ -1863,10 +1914,17 @@ bool MainWindow::loadLastResults()
return true;
}

void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringList& recheckFiles, const bool checkLib, const bool checkConfig)
void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringList& selectedFiles, const bool checkLib, const bool checkConfig)
{
Settings::terminate(false);

// With a build directory the whole project is analyzed: the analyzer information cache skips
// the unchanged files, and files.txt stays complete so whole program analysis sees every file.
// Without a build directory there is no cache, so only the selected files are analyzed.
Recheck recheck = Recheck::None;
if (!selectedFiles.isEmpty())
recheck = projectFile->getBuildDir().isEmpty() ? Recheck::Selected : Recheck::Cached;

QFileInfo inf(projectFile->getFilename());
const QString& rootpath = projectFile->getRootPath();

Expand Down Expand Up @@ -1968,11 +2026,11 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis
msg.exec();
return;
}
doAnalyzeProject(p, checkLib, checkConfig, recheckFiles); // TODO: avoid copy
doAnalyzeProject(p, checkLib, checkConfig, selectedFiles, recheck); // TODO: avoid copy
return;
}

QStringList paths = recheckFiles.isEmpty() ? projectFile->getCheckPaths() : recheckFiles;
QStringList paths = recheck == Recheck::Selected ? selectedFiles : projectFile->getCheckPaths();

// If paths not given then check the root path (which may be the project
// file's location, see above). This is to keep the compatibility with
Expand All @@ -1981,7 +2039,7 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const QStringLis
if (paths.isEmpty()) {
paths << mCurrentDirectory;
}
doAnalyzeFiles(paths, checkLib, checkConfig);
doAnalyzeFiles(paths, checkLib, checkConfig, selectedFiles, recheck);
}

void MainWindow::newProjectFile()
Expand Down
34 changes: 30 additions & 4 deletions gui/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <cstdint>
#include <list>
#include <string>

#include <QFileDialog>
#include <QMainWindow>
Expand Down Expand Up @@ -249,6 +250,19 @@ private slots:
void changeReportType();
private:

/** @brief How an analysis treats the files selected for a recheck */
enum class Recheck : std::uint8_t {
/** Not a recheck, everything is analyzed from scratch */
None,
/** Only the selected files are analyzed, the results of the other files are kept */
Selected,
/**
* Everything is analyzed. The analyzer information in the build directory is kept,
* except for the selected files, so the unchanged files are not analyzed again.
*/
Cached
};

bool isCppcheckPremium() const;

/** Get filename for last results */
Expand All @@ -266,11 +280,11 @@ private slots:
/**
* @brief Analyze the project.
* @param projectFile Pointer to the project to analyze.
* @param recheckFiles files to recheck, empty => check all files
* @param selectedFiles files to recheck, empty => check all files
* @param checkLib Flag to indicate if the library should be checked.
* @param checkConfig Flag to indicate if the configuration should be checked.
*/
void analyzeProject(const ProjectFile *projectFile, const QStringList& recheckFiles, bool checkLib = false, bool checkConfig = false);
void analyzeProject(const ProjectFile *projectFile, const QStringList& selectedFiles, bool checkLib = false, bool checkConfig = false);

/**
* @brief Set current language
Expand Down Expand Up @@ -308,17 +322,29 @@ private slots:
* @param p imported project
* @param checkLib Flag to indicate if library should be checked
* @param checkConfig Flag to indicate if the configuration should be checked.
* @param selectedFiles Files selected for a recheck
* @param recheck How the selected files are rechecked
*/
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& selectedFiles = QStringList(), Recheck recheck = Recheck::None);

/**
* @brief Analyze all files specified in parameter files
*
* @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 selectedFiles Files selected for a recheck
* @param recheck How the selected files are rechecked
*/
void doAnalyzeFiles(const QStringList &files, bool checkLib = false, bool checkConfig = false, const QStringList& selectedFiles = QStringList(), Recheck recheck = Recheck::None);

/**
* @brief Clear the results that a new analysis replaces
* @param buildDir Build directory of the analysis
* @param selectedFiles Files selected for a recheck
* @param recheck How the selected files are rechecked
*/
void doAnalyzeFiles(const QStringList &files, bool checkLib = false, bool checkConfig = false);
void clearResultsForAnalysis(const std::string& buildDir, const QStringList& selectedFiles, Recheck recheck);

/**
* @brief Get our default cppcheck settings and read project file.
Expand Down
23 changes: 20 additions & 3 deletions gui/resultstree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ bool ResultsTree::addErrorItem(const ErrorItem& errorItem)
const QString s = errorItem.toString();
if (mErrorList.contains(s))
return false;
mErrorList.append(s);
mErrorList.insert(s);

QSharedPointer<ErrorItem> errorItemPtr{new ErrorItem(errorItem)};

Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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<const ResultItem*>(fileItem->child(i, COLUMN_FILE));
if (errorItem && errorItem->errorItem)
mErrorList.remove(errorItem->errorItem->toString());
}
}

void ResultsTree::updateFromOldReport(const QString &filename)
{
showColumn(COLUMN_SINCE_DATE);
Expand Down Expand Up @@ -1194,7 +1203,15 @@ void ResultsTree::updateSettings(bool showFullPath,

void ResultsTree::setCheckDirectory(const QString &dir)
{
if (mCheckPath == dir)
return;

mCheckPath = dir;

// A partial recheck keeps results whose displayed paths are relative to the check path, so
// they are recalculated. Not without a check path, QDir("") would turn absolute paths into "../..".
if (!mCheckPath.isEmpty())
refreshFilePaths();
}

const QString& ResultsTree::getCheckDirectory() const
Expand Down
Loading
Loading