Skip to content
Draft
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
25 changes: 16 additions & 9 deletions gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -573,7 +575,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo
});
}

clearResults();
clearResults(recheckFiles);

mIsLogfileLoaded = false;
if (mProjectFile) {
Expand Down Expand Up @@ -619,7 +621,7 @@ void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLib, const bo
if (!checkSettings.buildDir.empty()) {
checkSettings.loadSummaries();
std::list<std::string> sourcefiles;
AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, p.fileSettings);
AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, p.fileSettings, !recheckFiles.isEmpty());
}

//mThread->SetanalyzeProject(true);
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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();

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 8 additions & 4 deletions gui/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -309,16 +312,17 @@ 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
*
* @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.
Expand Down
14 changes: 12 additions & 2 deletions gui/resultstree.cpp
Original file line number Diff line number Diff line change
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.removeAll(errorItem->errorItem->toString());
}
}

void ResultsTree::updateFromOldReport(const QString &filename)
{
showColumn(COLUMN_SINCE_DATE);
Expand Down Expand Up @@ -1195,6 +1204,7 @@ void ResultsTree::updateSettings(bool showFullPath,
void ResultsTree::setCheckDirectory(const QString &dir)
{
mCheckPath = dir;
refreshFilePaths();
}

const QString& ResultsTree::getCheckDirectory() const
Expand Down
6 changes: 6 additions & 0 deletions gui/resultstree.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
43 changes: 38 additions & 5 deletions lib/analyzerinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <exception>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <stdexcept>
#include <utility>
Expand All @@ -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<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings)
void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings, bool merge)
{
const std::string filesTxt(buildDir + "/files.txt");

std::string keptLines;
std::map<std::string, unsigned int> fileCount;

if (merge) {
std::set<std::string> 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<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings) {
std::string AnalyzerInformation::getFilesTxt(const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings, std::map<std::string, unsigned int> fileCount) {
std::ostringstream ret;

std::map<std::string, unsigned int> 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';
Expand Down
5 changes: 3 additions & 2 deletions lib/analyzerinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <fstream>
#include <functional>
#include <list>
#include <map>
#include <string>

class ErrorMessage;
Expand Down Expand Up @@ -57,7 +58,7 @@ class CPPCHECKLIB AnalyzerInformation {
public:
~AnalyzerInformation();

static void writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings);
static void writeFilesTxt(const std::string &buildDir, const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings, bool merge = false);

/** Close current TU.analyzerinfo file */
void close();
Expand Down Expand Up @@ -85,7 +86,7 @@ class CPPCHECKLIB AnalyzerInformation {
static std::string processFilesTxt(const std::string& buildDir, const std::function<void(const char* checkattr, const tinyxml2::XMLElement* e, const Info& filesTxtInfo)>& handler, bool debug = false);

protected:
static std::string getFilesTxt(const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings);
static std::string getFilesTxt(const std::list<std::string> &sourcefiles, const std::list<FileSettings> &fileSettings, std::map<std::string, unsigned int> fileCount = {});

static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg, size_t fsFileId);

Expand Down
22 changes: 14 additions & 8 deletions lib/cppcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1654,7 +1654,7 @@ void CppCheck::executeAddons(const std::vector<std::string>& files, const std::s
}
}

void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files, const std::list<FileSettings>& fileSettings, const std::string& ctuInfo)
void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails>& /*files*/, const std::list<FileSettings>& /*fileSettings*/, const std::string& ctuInfo)
{
if (mSettings.addons.empty())
return;
Expand All @@ -1671,13 +1671,19 @@ void CppCheck::executeAddonsWholeProgram(const std::list<FileWithDetails> &files
}

std::vector<std::string> ctuInfoFiles;
for (const auto &f: files) {
const std::string &dumpFileName = getDumpFileName(mSettings, f);
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}

for (const auto &fs: fileSettings) {
const std::string &dumpFileName = getDumpFileName(mSettings, fs.file);
std::set<std::pair<std::string, std::size_t>> seen;
std::ifstream filesTxt(mSettings.buildDir + "/files.txt");
std::string line;
while (std::getline(filesTxt, line)) {
AnalyzerInformation::Info info;
if (!info.parse(line) || info.sourceFile.empty())
continue;
if (!seen.emplace(info.sourceFile, info.fsFileId).second)
continue;
std::string dumpFileName = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, info.sourceFile, "", info.fsFileId);
if (info.fsFileId > 0)
dumpFileName += '.' + std::to_string(info.fsFileId);
dumpFileName += ".dump";
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}

Expand Down
53 changes: 50 additions & 3 deletions lib/errorlogger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@
#include <cstring>
#include <fstream>
#include <iomanip>
#include <ios>
#include <list>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "xml.h"

Expand Down Expand Up @@ -544,13 +547,57 @@
return printer.CStr();
}

// Byte offset of the start of each line, indexed by 1-based line number.
// Building this once per file keeps readCode() cheap. Without it every call
// rescans the file from the start, which is quadratic for a file that produces
// many findings - vendor/generated headers routinely produce tens of thousands.
static const std::vector<std::streamoff>* getLineOffsets(const std::string &file)
{
// thread_local: the thread executor calls this concurrently
static thread_local std::list<std::pair<std::string, std::vector<std::streamoff>>> cache;

for (auto it = cache.begin(); it != cache.end(); ++it) {
if (it->first == file) {
// most recently used goes first
cache.splice(cache.begin(), cache, it);
return &cache.front().second;
}
}

// Binary mode so the offsets match what the seek in readCode() expects.
// A trailing '\r' is stripped by the caller.
std::ifstream fin(file, std::ios::binary);
if (!fin.is_open())
return nullptr;

std::vector<std::streamoff> offsets{0, 0}; // index 0 is unused, line 1 starts at offset 0
std::array<char, 64 * 1024> buf;
std::streamoff pos = 0;
while (fin.read(buf.data(), buf.size()) || fin.gcount() > 0) {
const std::streamsize n = fin.gcount();
for (std::streamsize i = 0; i < n; ++i) {
if (buf[i] == '\n')
offsets.push_back(pos + i + 1);
}
pos += n;
}

constexpr std::size_t maxCachedFiles = 4;
if (cache.size() >= maxCachedFiles)
cache.pop_back();
cache.emplace_front(file, std::move(offsets));
return &cache.front().second;
}

// TODO: read info from some shared resource instead?
static std::string readCode(const std::string &file, int linenr, int column, const char endl[])
{
std::ifstream fin(file);
std::string line;
while (linenr > 0 && std::getline(fin,line)) {
linenr--;
const std::vector<std::streamoff>* const offsets = getLineOffsets(file);
if (offsets && linenr > 0 && linenr < static_cast<int>(offsets->size())) {

Check failure

Code scanning / Cppcheck Premium

Ensure that integer conversions do not result in lost or misinterpreted data Error

Ensure that integer conversions do not result in lost or misinterpreted data
std::ifstream fin(file, std::ios::binary);
fin.seekg((*offsets)[linenr]);
std::getline(fin, line);
}
const std::string::size_type endPos = line.find_last_not_of("\r\n\t ");
if (endPos + 1 < line.size())
Expand Down
Loading