Variable contracts
This commit is contained in:
parent
02287d9d34
commit
6277eece67
|
@ -77,7 +77,8 @@ FORMS = about.ui \
|
|||
librarydialog.ui \
|
||||
libraryaddfunctiondialog.ui \
|
||||
libraryeditargdialog.ui \
|
||||
newsuppressiondialog.ui
|
||||
newsuppressiondialog.ui \
|
||||
variablecontractsdialog.ui
|
||||
|
||||
TRANSLATIONS = cppcheck_de.ts \
|
||||
cppcheck_es.ts \
|
||||
|
@ -134,6 +135,7 @@ HEADERS += aboutdialog.h \
|
|||
threadresult.h \
|
||||
translationhandler.h \
|
||||
txtreport.h \
|
||||
variablecontractsdialog.h \
|
||||
xmlreport.h \
|
||||
xmlreportv2.h \
|
||||
librarydialog.h \
|
||||
|
@ -176,6 +178,7 @@ SOURCES += aboutdialog.cpp \
|
|||
threadresult.cpp \
|
||||
translationhandler.cpp \
|
||||
txtreport.cpp \
|
||||
variablecontractsdialog.cpp \
|
||||
xmlreport.cpp \
|
||||
xmlreportv2.cpp \
|
||||
librarydialog.cpp \
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "threadhandler.h"
|
||||
#include "threadresult.h"
|
||||
#include "translationhandler.h"
|
||||
#include "variablecontractsdialog.h"
|
||||
|
||||
static const QString OnlineHelpURL("http://cppcheck.net/manual.html");
|
||||
static const QString compile_commands_json("compile_commands.json");
|
||||
|
@ -144,6 +145,9 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
|
|||
connect(mUI.mResults, &ResultsView::checkSelected, this, &MainWindow::performSelectedFilesCheck);
|
||||
connect(mUI.mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds);
|
||||
connect(mUI.mResults, &ResultsView::editFunctionContract, this, &MainWindow::editFunctionContract);
|
||||
connect(mUI.mResults, &ResultsView::editVariableContract, this, &MainWindow::editVariableContract);
|
||||
connect(mUI.mResults, &ResultsView::deleteFunctionContract, this, &MainWindow::deleteFunctionContract);
|
||||
connect(mUI.mResults, &ResultsView::deleteVariableContract, this, &MainWindow::deleteVariableContract);
|
||||
connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu);
|
||||
|
||||
// File menu
|
||||
|
@ -351,7 +355,8 @@ void MainWindow::loadSettings()
|
|||
}
|
||||
}
|
||||
|
||||
updateContractsTab();
|
||||
updateFunctionContractsTab();
|
||||
updateVariableContractsTab();
|
||||
}
|
||||
|
||||
void MainWindow::saveSettings() const
|
||||
|
@ -611,7 +616,7 @@ QStringList MainWindow::selectFilesToAnalyze(QFileDialog::FileMode mode)
|
|||
return selected;
|
||||
}
|
||||
|
||||
void MainWindow::updateContractsTab()
|
||||
void MainWindow::updateFunctionContractsTab()
|
||||
{
|
||||
QStringList addedContracts;
|
||||
if (mProjectFile) {
|
||||
|
@ -619,7 +624,23 @@ void MainWindow::updateContractsTab()
|
|||
addedContracts << QString::fromStdString(it.first);
|
||||
}
|
||||
}
|
||||
mUI.mResults->setAddedContracts(addedContracts);
|
||||
mUI.mResults->setAddedFunctionContracts(addedContracts);
|
||||
}
|
||||
|
||||
void MainWindow::updateVariableContractsTab()
|
||||
{
|
||||
QStringList added;
|
||||
if (mProjectFile) {
|
||||
for (auto vc: mProjectFile->getVariableContracts()) {
|
||||
QString line = vc.first;
|
||||
if (!vc.second.minValue.empty())
|
||||
line += " min:" + QString::fromStdString(vc.second.minValue);
|
||||
if (!vc.second.maxValue.empty())
|
||||
line += " max:" + QString::fromStdString(vc.second.maxValue);
|
||||
added << line;
|
||||
}
|
||||
}
|
||||
mUI.mResults->setAddedVariableContracts(added);
|
||||
}
|
||||
|
||||
void MainWindow::analyzeFiles()
|
||||
|
@ -865,6 +886,9 @@ Settings MainWindow::getCppcheckSettings()
|
|||
|
||||
result.functionContracts = mProjectFile->getFunctionContracts();
|
||||
|
||||
for (const auto vc: mProjectFile->getVariableContracts())
|
||||
result.variableContracts[vc.first.toStdString()] = vc.second;
|
||||
|
||||
const QStringList undefines = mProjectFile->getUndefines();
|
||||
foreach (QString undefine, undefines)
|
||||
result.userUndefs.insert(undefine.toStdString());
|
||||
|
@ -1513,7 +1537,9 @@ void MainWindow::loadProjectFile(const QString &filePath)
|
|||
delete mProjectFile;
|
||||
mProjectFile = new ProjectFile(filePath, this);
|
||||
mProjectFile->setActiveProject();
|
||||
updateContractsTab();
|
||||
mUI.mResults->showContracts(mProjectFile->bugHunting);
|
||||
updateFunctionContractsTab();
|
||||
updateVariableContractsTab();
|
||||
if (!loadLastResults())
|
||||
analyzeProject(mProjectFile);
|
||||
}
|
||||
|
@ -1639,12 +1665,14 @@ void MainWindow::newProjectFile()
|
|||
ProjectFileDialog dlg(mProjectFile, this);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
addProjectMRU(filepath);
|
||||
mUI.mResults->showContracts(mProjectFile->bugHunting);
|
||||
analyzeProject(mProjectFile);
|
||||
} else {
|
||||
closeProjectFile();
|
||||
}
|
||||
|
||||
updateContractsTab();
|
||||
updateFunctionContractsTab();
|
||||
updateVariableContractsTab();
|
||||
}
|
||||
|
||||
void MainWindow::closeProjectFile()
|
||||
|
@ -1652,6 +1680,8 @@ void MainWindow::closeProjectFile()
|
|||
delete mProjectFile;
|
||||
mProjectFile = nullptr;
|
||||
mUI.mResults->clear(true);
|
||||
mUI.mResults->clearContracts();
|
||||
mUI.mResults->showContracts(false);
|
||||
enableProjectActions(false);
|
||||
enableProjectOpenActions(true);
|
||||
formatAndSetTitle();
|
||||
|
@ -1672,6 +1702,7 @@ void MainWindow::editProjectFile()
|
|||
ProjectFileDialog dlg(mProjectFile, this);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
mProjectFile->write();
|
||||
mUI.mResults->showContracts(mProjectFile->bugHunting);
|
||||
analyzeProject(mProjectFile);
|
||||
}
|
||||
}
|
||||
|
@ -1858,5 +1889,35 @@ void MainWindow::editFunctionContract(QString function)
|
|||
mProjectFile->write();
|
||||
}
|
||||
|
||||
updateContractsTab();
|
||||
updateFunctionContractsTab();
|
||||
}
|
||||
|
||||
void MainWindow::editVariableContract(QString var)
|
||||
{
|
||||
if (!mProjectFile)
|
||||
return;
|
||||
|
||||
VariableContractsDialog dlg(nullptr, var);
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
mProjectFile->setVariableContracts(dlg.getVarname(), dlg.getMin(), dlg.getMax());
|
||||
mProjectFile->write();
|
||||
}
|
||||
|
||||
updateVariableContractsTab();
|
||||
}
|
||||
|
||||
void MainWindow::deleteFunctionContract(QString function)
|
||||
{
|
||||
if (mProjectFile) {
|
||||
mProjectFile->deleteFunctionContract(function);
|
||||
mProjectFile->write();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::deleteVariableContract(QString var)
|
||||
{
|
||||
if (mProjectFile) {
|
||||
mProjectFile->deleteVariableContract(var);
|
||||
mProjectFile->write();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,11 @@ public:
|
|||
|
||||
public slots:
|
||||
|
||||
/** Update "Contracts" tab */
|
||||
void updateContractsTab();
|
||||
/** Update "Functions" tab */
|
||||
void updateFunctionContractsTab();
|
||||
|
||||
/** Update "Variables" tab */
|
||||
void updateVariableContractsTab();
|
||||
|
||||
/** @brief Slot for analyze files menu item */
|
||||
void analyzeFiles();
|
||||
|
@ -227,6 +230,16 @@ protected slots:
|
|||
|
||||
/** Edit contract for function */
|
||||
void editFunctionContract(QString function);
|
||||
|
||||
/** Edit constraints for variable */
|
||||
void editVariableContract(QString var);
|
||||
|
||||
/** Delete contract for function */
|
||||
void deleteFunctionContract(QString function);
|
||||
|
||||
/** Edit constraints for variable */
|
||||
void deleteVariableContract(QString var);
|
||||
|
||||
private:
|
||||
|
||||
/** Get filename for last results */
|
||||
|
|
|
@ -59,6 +59,7 @@ void ProjectFile::clear()
|
|||
mPaths.clear();
|
||||
mExcludedPaths.clear();
|
||||
mFunctionContracts.clear();
|
||||
mVariableContracts.clear();
|
||||
mLibraries.clear();
|
||||
mPlatform.clear();
|
||||
mSuppressions.clear();
|
||||
|
@ -156,6 +157,10 @@ bool ProjectFile::read(const QString &filename)
|
|||
if (xmlReader.name() == CppcheckXml::FunctionContracts)
|
||||
readFunctionContracts(xmlReader);
|
||||
|
||||
// Variable constraints
|
||||
if (xmlReader.name() == CppcheckXml::VariableContractsElementName)
|
||||
readVariableContracts(xmlReader);
|
||||
|
||||
// Find libraries list from inside project element
|
||||
if (xmlReader.name() == CppcheckXml::LibrariesElementName)
|
||||
readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName);
|
||||
|
@ -531,6 +536,42 @@ void ProjectFile::readFunctionContracts(QXmlStreamReader &reader)
|
|||
} while (!allRead);
|
||||
}
|
||||
|
||||
void ProjectFile::readVariableContracts(QXmlStreamReader &reader)
|
||||
{
|
||||
QXmlStreamReader::TokenType type;
|
||||
while (true) {
|
||||
type = reader.readNext();
|
||||
switch (type) {
|
||||
case QXmlStreamReader::StartElement:
|
||||
if (reader.name().toString() == CppcheckXml::VariableContractItemElementName) {
|
||||
QXmlStreamAttributes attribs = reader.attributes();
|
||||
QString varname = attribs.value(QString(), CppcheckXml::VariableContractVarName).toString();
|
||||
QString minValue = attribs.value(QString(), CppcheckXml::VariableContractMin).toString();
|
||||
QString maxValue = attribs.value(QString(), CppcheckXml::VariableContractMax).toString();
|
||||
setVariableContracts(varname, minValue, maxValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case QXmlStreamReader::EndElement:
|
||||
if (reader.name().toString() == CppcheckXml::VariableContractsElementName)
|
||||
return;
|
||||
break;
|
||||
|
||||
// Not handled
|
||||
case QXmlStreamReader::NoToken:
|
||||
case QXmlStreamReader::Invalid:
|
||||
case QXmlStreamReader::StartDocument:
|
||||
case QXmlStreamReader::EndDocument:
|
||||
case QXmlStreamReader::Characters:
|
||||
case QXmlStreamReader::Comment:
|
||||
case QXmlStreamReader::DTD:
|
||||
case QXmlStreamReader::EntityReference:
|
||||
case QXmlStreamReader::ProcessingInstruction:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectFile::readVsConfigurations(QXmlStreamReader &reader)
|
||||
{
|
||||
QXmlStreamReader::TokenType type;
|
||||
|
@ -926,6 +967,20 @@ bool ProjectFile::write(const QString &filename)
|
|||
xmlWriter.writeEndElement();
|
||||
}
|
||||
|
||||
if (!mVariableContracts.empty()) {
|
||||
xmlWriter.writeStartElement(CppcheckXml::VariableContractsElementName);
|
||||
|
||||
for (auto vc: mVariableContracts) {
|
||||
xmlWriter.writeStartElement(CppcheckXml::VariableContractItemElementName);
|
||||
xmlWriter.writeAttribute(CppcheckXml::VariableContractVarName, vc.first);
|
||||
xmlWriter.writeAttribute(CppcheckXml::VariableContractMin, QString::fromStdString(vc.second.minValue));
|
||||
xmlWriter.writeAttribute(CppcheckXml::VariableContractMax, QString::fromStdString(vc.second.maxValue));
|
||||
xmlWriter.writeEndElement();
|
||||
}
|
||||
|
||||
xmlWriter.writeEndElement();
|
||||
}
|
||||
|
||||
if (!mSuppressions.isEmpty()) {
|
||||
xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName);
|
||||
foreach (const Suppressions::Suppression &suppression, mSuppressions) {
|
||||
|
|
|
@ -230,6 +230,24 @@ public:
|
|||
return mFunctionContracts;
|
||||
}
|
||||
|
||||
const std::map<QString, Settings::VariableContracts>& getVariableContracts() const {
|
||||
return mVariableContracts;
|
||||
}
|
||||
|
||||
void setVariableContracts(QString var, QString min, QString max) {
|
||||
mVariableContracts[var] = Settings::VariableContracts{min.toStdString(), max.toStdString()};
|
||||
}
|
||||
|
||||
void deleteFunctionContract(QString function)
|
||||
{
|
||||
mFunctionContracts.erase(function.toStdString());
|
||||
}
|
||||
|
||||
void deleteVariableContract(QString var)
|
||||
{
|
||||
mVariableContracts.erase(var);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get filename for the project file.
|
||||
* @return file name.
|
||||
|
@ -425,6 +443,12 @@ protected:
|
|||
*/
|
||||
void readFunctionContracts(QXmlStreamReader &reader);
|
||||
|
||||
/**
|
||||
* @brief Read variable constraints.
|
||||
* @param reader XML stream reader.
|
||||
*/
|
||||
void readVariableContracts(QXmlStreamReader &reader);
|
||||
|
||||
/**
|
||||
* @brief Read lists of Visual Studio configurations
|
||||
* @param reader XML stream reader.
|
||||
|
@ -542,6 +566,8 @@ private:
|
|||
|
||||
std::map<std::string, std::string> mFunctionContracts;
|
||||
|
||||
std::map<QString, Settings::VariableContracts> mVariableContracts;
|
||||
|
||||
/**
|
||||
* @brief Platform
|
||||
*/
|
||||
|
|
|
@ -61,8 +61,17 @@ ResultsView::ResultsView(QWidget * parent) :
|
|||
connect(this, &ResultsView::collapseAllResults, mUI.mTree, &ResultsTree::collapseAll);
|
||||
connect(this, &ResultsView::expandAllResults, mUI.mTree, &ResultsTree::expandAll);
|
||||
connect(this, &ResultsView::showHiddenResults, mUI.mTree, &ResultsTree::showHiddenResults);
|
||||
|
||||
// Function contracts
|
||||
connect(mUI.mListAddedContracts, &QListWidget::itemDoubleClicked, this, &ResultsView::contractDoubleClicked);
|
||||
connect(mUI.mListMissingContracts, &QListWidget::itemDoubleClicked, this, &ResultsView::contractDoubleClicked);
|
||||
mUI.mListAddedContracts->installEventFilter(this);
|
||||
|
||||
// Variable contracts
|
||||
connect(mUI.mListAddedVariables, &QListWidget::itemDoubleClicked, this, &ResultsView::variableDoubleClicked);
|
||||
connect(mUI.mListMissingVariables, &QListWidget::itemDoubleClicked, this, &ResultsView::variableDoubleClicked);
|
||||
connect(mUI.mEditVariablesFilter, &QLineEdit::textChanged, this, &ResultsView::editVariablesFilter);
|
||||
mUI.mListAddedVariables->installEventFilter(this);
|
||||
|
||||
mUI.mListLog->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
|
@ -90,7 +99,7 @@ ResultsView::~ResultsView()
|
|||
//dtor
|
||||
}
|
||||
|
||||
void ResultsView::setAddedContracts(const QStringList &addedContracts)
|
||||
void ResultsView::setAddedFunctionContracts(const QStringList &addedContracts)
|
||||
{
|
||||
mUI.mListAddedContracts->clear();
|
||||
mUI.mListAddedContracts->addItems(addedContracts);
|
||||
|
@ -101,6 +110,17 @@ void ResultsView::setAddedContracts(const QStringList &addedContracts)
|
|||
}
|
||||
}
|
||||
|
||||
void ResultsView::setAddedVariableContracts(const QStringList &added)
|
||||
{
|
||||
mUI.mListAddedVariables->clear();
|
||||
mUI.mListAddedVariables->addItems(added);
|
||||
for (const QString var: added) {
|
||||
for (auto item: mUI.mListMissingVariables->findItems(var, Qt::MatchExactly))
|
||||
delete item;
|
||||
mVariableContracts.insert(var);
|
||||
}
|
||||
}
|
||||
|
||||
void ResultsView::clear(bool results)
|
||||
{
|
||||
if (results) {
|
||||
|
@ -127,6 +147,22 @@ void ResultsView::clearRecheckFile(const QString &filename)
|
|||
mUI.mTree->clearRecheckFile(filename);
|
||||
}
|
||||
|
||||
void ResultsView::clearContracts()
|
||||
{
|
||||
mUI.mListAddedContracts->clear();
|
||||
mUI.mListAddedVariables->clear();
|
||||
mUI.mListMissingContracts->clear();
|
||||
mUI.mListMissingVariables->clear();
|
||||
mFunctionContracts.clear();
|
||||
mVariableContracts.clear();
|
||||
}
|
||||
|
||||
void ResultsView::showContracts(bool visible)
|
||||
{
|
||||
mUI.mTabFunctionContracts->setVisible(visible);
|
||||
mUI.mTabVariableContracts->setVisible(visible);
|
||||
}
|
||||
|
||||
void ResultsView::progress(int value, const QString& description)
|
||||
{
|
||||
mUI.mProgress->setValue(value);
|
||||
|
@ -458,12 +494,16 @@ void ResultsView::debugError(const ErrorItem &item)
|
|||
void ResultsView::bughuntingReportLine(const QString& line)
|
||||
{
|
||||
for (const QString& s: line.split("\n")) {
|
||||
if (s.isEmpty())
|
||||
continue;
|
||||
if (s.startsWith("[missing contract] ")) {
|
||||
if (s.startsWith("[intvar] ")) {
|
||||
const QString varname = s.mid(9);
|
||||
if (!mVariableContracts.contains(varname)) {
|
||||
mVariableContracts.insert(varname);
|
||||
mUI.mListMissingVariables->addItem(varname);
|
||||
}
|
||||
} else if (s.startsWith("[missing contract] ")) {
|
||||
const QString functionName = s.mid(19);
|
||||
if (!mContracts.contains(functionName)) {
|
||||
mContracts.insert(functionName);
|
||||
if (!mFunctionContracts.contains(functionName)) {
|
||||
mFunctionContracts.insert(functionName);
|
||||
mUI.mListMissingContracts->addItem(functionName);
|
||||
}
|
||||
}
|
||||
|
@ -502,6 +542,21 @@ void ResultsView::contractDoubleClicked(QListWidgetItem* item)
|
|||
emit editFunctionContract(item->text());
|
||||
}
|
||||
|
||||
void ResultsView::variableDoubleClicked(QListWidgetItem* item)
|
||||
{
|
||||
emit editVariableContract(item->text());
|
||||
}
|
||||
|
||||
void ResultsView::editVariablesFilter(const QString &text)
|
||||
{
|
||||
for (auto item: mUI.mListAddedVariables->findItems(".*", Qt::MatchRegExp)) {
|
||||
QString varname = item->text().mid(0, item->text().indexOf(" "));
|
||||
item->setHidden(!varname.contains(text));
|
||||
}
|
||||
for (auto item: mUI.mListMissingVariables->findItems(".*", Qt::MatchRegExp))
|
||||
item->setHidden(!item->text().contains(text));
|
||||
}
|
||||
|
||||
void ResultsView::on_mListLog_customContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
if (mUI.mListLog->count() <= 0)
|
||||
|
@ -516,3 +571,32 @@ void ResultsView::on_mListLog_customContextMenuRequested(const QPoint &pos)
|
|||
|
||||
contextMenu.exec(globalPos);
|
||||
}
|
||||
|
||||
bool ResultsView::eventFilter(QObject *target, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
if (target == mUI.mListAddedVariables) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Delete) {
|
||||
for (auto i: mUI.mListAddedVariables->selectedItems()) {
|
||||
emit deleteVariableContract(i->text().mid(0, i->text().indexOf(" ")));
|
||||
delete i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == mUI.mListAddedContracts) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Delete) {
|
||||
for (auto i: mUI.mListAddedContracts->selectedItems()) {
|
||||
emit deleteFunctionContract(i->text());
|
||||
delete i;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(target, event);
|
||||
}
|
||||
|
|
|
@ -50,7 +50,8 @@ public:
|
|||
virtual ~ResultsView();
|
||||
ResultsView &operator=(const ResultsView &) = delete;
|
||||
|
||||
void setAddedContracts(const QStringList &addedContracts);
|
||||
void setAddedFunctionContracts(const QStringList &addedContracts);
|
||||
void setAddedVariableContracts(const QStringList &added);
|
||||
|
||||
/**
|
||||
* @brief Clear results and statistics and reset progressinfo.
|
||||
|
@ -68,6 +69,9 @@ public:
|
|||
*/
|
||||
void clearRecheckFile(const QString &filename);
|
||||
|
||||
/** Clear the contracts */
|
||||
void clearContracts();
|
||||
|
||||
/**
|
||||
* @brief Write statistics in file
|
||||
*
|
||||
|
@ -196,6 +200,9 @@ public:
|
|||
return &mUI.mTree->mShowSeverities;
|
||||
}
|
||||
|
||||
/** Show/hide the contract tabs */
|
||||
void showContracts(bool visible);
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
|
@ -224,6 +231,15 @@ signals:
|
|||
/** Edit contract for function */
|
||||
void editFunctionContract(QString function);
|
||||
|
||||
/** Delete contract for function */
|
||||
void deleteFunctionContract(QString function);
|
||||
|
||||
/** Edit contract for variable */
|
||||
void editVariableContract(QString var);
|
||||
|
||||
/** Delete variable contract */
|
||||
void deleteVariableContract(QString var);
|
||||
|
||||
/**
|
||||
* @brief Show/hide certain type of errors
|
||||
* Refreshes the tree.
|
||||
|
@ -342,6 +358,11 @@ public slots:
|
|||
/** \brief Contract was double clicked => edit it */
|
||||
void contractDoubleClicked(QListWidgetItem* item);
|
||||
|
||||
/** \brief Variable was double clicked => edit it */
|
||||
void variableDoubleClicked(QListWidgetItem* item);
|
||||
|
||||
void editVariablesFilter(const QString &text);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Should we show a "No errors found dialog" every time no errors were found?
|
||||
|
@ -351,6 +372,8 @@ protected:
|
|||
Ui::ResultsView mUI;
|
||||
|
||||
CheckStatistics *mStatistics;
|
||||
|
||||
bool eventFilter(QObject *target, QEvent *event);
|
||||
private slots:
|
||||
/**
|
||||
* @brief Custom context menu for Analysis Log
|
||||
|
@ -358,7 +381,8 @@ private slots:
|
|||
*/
|
||||
void on_mListLog_customContextMenuRequested(const QPoint &pos);
|
||||
private:
|
||||
QSet<QString> mContracts;
|
||||
QSet<QString> mFunctionContracts;
|
||||
QSet<QString> mVariableContracts;
|
||||
|
||||
/** Current file shown in the code editor */
|
||||
QString mCurrentFileName;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>459</width>
|
||||
<height>358</height>
|
||||
<height>391</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -153,9 +153,9 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="mTabContracts">
|
||||
<widget class="QWidget" name="mTabFunctionContracts">
|
||||
<attribute name="title">
|
||||
<string>Contracts</string>
|
||||
<string>Functions</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
|
@ -191,6 +191,71 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="mTabVariableContracts">
|
||||
<attribute name="title">
|
||||
<string>Variables</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Only show variable names that contain text:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mEditVariablesFilter"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Configured contracts:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="mListAddedVariables">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Missing contracts:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="mListMissingVariables">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#include "variablecontractsdialog.h"
|
||||
#include "ui_variablecontractsdialog.h"
|
||||
|
||||
#include <QRegExpValidator>
|
||||
|
||||
VariableContractsDialog::VariableContractsDialog(QWidget *parent, QString var) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::VariableContractsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
mVarName = var.indexOf(" ") < 0 ? var : var.mid(0, var.indexOf(" "));
|
||||
|
||||
this->setWindowTitle(mVarName);
|
||||
|
||||
auto getMinMax = [](QString var, QString minmax) {
|
||||
if (var.indexOf(" " + minmax + ":") < 0)
|
||||
return QString();
|
||||
int pos1 = var.indexOf(" " + minmax + ":") + 2 + minmax.length();
|
||||
int pos2 = var.indexOf(" ", pos1);
|
||||
if (pos2 < 0)
|
||||
return var.mid(pos1);
|
||||
return var.mid(pos1, pos2-pos1);
|
||||
};
|
||||
|
||||
ui->mMinValue->setText(getMinMax(var, "min"));
|
||||
ui->mMaxValue->setText(getMinMax(var, "max"));
|
||||
|
||||
ui->mMinValue->setValidator(new QRegExpValidator(QRegExp("-?[0-9]*")));
|
||||
ui->mMaxValue->setValidator(new QRegExpValidator(QRegExp("-?[0-9]*")));
|
||||
}
|
||||
|
||||
VariableContractsDialog::~VariableContractsDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString VariableContractsDialog::getVarname() const
|
||||
{
|
||||
return mVarName;
|
||||
}
|
||||
QString VariableContractsDialog::getMin() const
|
||||
{
|
||||
return ui->mMinValue->text();
|
||||
}
|
||||
QString VariableContractsDialog::getMax() const
|
||||
{
|
||||
return ui->mMaxValue->text();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef VARIABLECONSTRAINTSDIALOG_H
|
||||
#define VARIABLECONSTRAINTSDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class VariableContractsDialog;
|
||||
}
|
||||
|
||||
class VariableContractsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VariableContractsDialog(QWidget *parent, QString var);
|
||||
~VariableContractsDialog();
|
||||
|
||||
QString getVarname() const;
|
||||
QString getMin() const;
|
||||
QString getMax() const;
|
||||
|
||||
private:
|
||||
Ui::VariableContractsDialog *ui;
|
||||
QString mVarName;
|
||||
};
|
||||
|
||||
#endif // VARIABLECONSTRAINTSDIALOG_H
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VariableContractsDialog</class>
|
||||
<widget class="QDialog" name="VariableContractsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>172</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>You can specify min and max value for the variable here</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Min</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="mMinValue"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Max</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="mMaxValue"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>25</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>VariableContractsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>VariableContractsDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -505,14 +505,44 @@ static void checkAssignment(const Token *tok, const ExprEngine::Value &value, Ex
|
|||
|
||||
const Token *vartok = lhs->variable()->nameToken();
|
||||
|
||||
bool executable = false;
|
||||
std::string fullName = lhs->variable()->name();
|
||||
for (const Scope *s = lhs->variable()->nameToken()->scope(); s->type != Scope::ScopeType::eGlobal; s = s->nestedIn) {
|
||||
if (s->isExecutable()) {
|
||||
executable = true;
|
||||
break;
|
||||
}
|
||||
fullName = s->className + "::" + fullName;
|
||||
}
|
||||
|
||||
auto getMinMaxValue = [=](TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *val) {
|
||||
if (vartok->getCppcheckAttribute(type, val))
|
||||
return true;
|
||||
if (!executable) {
|
||||
const auto it = dataBase->settings->variableContracts.find(fullName);
|
||||
if (it != dataBase->settings->variableContracts.end()) {
|
||||
const std::string *str;
|
||||
if (type == TokenImpl::CppcheckAttributes::Type::LOW)
|
||||
str = &it->second.minValue;
|
||||
else if (type == TokenImpl::CppcheckAttributes::Type::HIGH)
|
||||
str = &it->second.maxValue;
|
||||
else
|
||||
return false;
|
||||
*val = MathLib::toLongNumber(*str);
|
||||
return !str->empty();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
MathLib::bigint low;
|
||||
if (vartok->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low)) {
|
||||
if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::LOW, &low)) {
|
||||
if (value.isLessThan(dataBase, low))
|
||||
dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is greater or equal with " + std::to_string(low), CWE_INCORRECT_CALCULATION, false);
|
||||
}
|
||||
|
||||
MathLib::bigint high;
|
||||
if (vartok->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) {
|
||||
if (getMinMaxValue(TokenImpl::CppcheckAttributes::Type::HIGH, &high)) {
|
||||
if (value.isGreaterThan(dataBase, high))
|
||||
dataBase->reportError(tok, Severity::SeverityType::error, "bughuntingAssign", "There is assignment, cannot determine that value is lower or equal with " + std::to_string(high), CWE_INCORRECT_CALCULATION, false);
|
||||
}
|
||||
|
|
|
@ -2641,6 +2641,28 @@ void ExprEngine::executeFunction(const Scope *functionScope, ErrorLogger *errorL
|
|||
|
||||
// Write a report
|
||||
if (bugHuntingReport) {
|
||||
std::set<std::string> intvars;
|
||||
for (const Scope &scope: tokenizer->getSymbolDatabase()->scopeList) {
|
||||
if (scope.isExecutable())
|
||||
continue;
|
||||
std::string path;
|
||||
bool valid = true;
|
||||
for (const Scope *s = &scope; s->type != Scope::ScopeType::eGlobal; s = s->nestedIn) {
|
||||
if (s->isExecutable()) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
path = s->className + "::" + path;
|
||||
}
|
||||
if (!valid)
|
||||
continue;
|
||||
for (const Variable &var: scope.varlist) {
|
||||
if (var.nameToken() && !var.nameToken()->hasCppcheckAttributes() && var.valueType() && var.valueType()->pointer == 0 && var.valueType()->constness == 0 && var.valueType()->isIntegral())
|
||||
intvars.insert(path + var.name());
|
||||
}
|
||||
}
|
||||
for (const std::string &v: intvars)
|
||||
report << "[intvar] " << v << std::endl;
|
||||
for (const std::string &f: trackExecution.getMissingContracts())
|
||||
report << "[missing contract] " << f << std::endl;
|
||||
}
|
||||
|
|
|
@ -1068,6 +1068,16 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
temp.functionContracts[function] = expects;
|
||||
}
|
||||
}
|
||||
} else if (strcmp(node->Name(), CppcheckXml::VariableContractsElementName) == 0) {
|
||||
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
|
||||
if (strcmp(child->Name(), CppcheckXml::VariableContractItemElementName) == 0) {
|
||||
const char *name = child->Attribute(CppcheckXml::VariableContractVarName);
|
||||
const char *min = child->Attribute(CppcheckXml::VariableContractMin);
|
||||
const char *max = child->Attribute(CppcheckXml::VariableContractMax);
|
||||
if (name)
|
||||
temp.variableContracts[name] = Settings::VariableContracts{min?min:"", max?max:""};
|
||||
}
|
||||
}
|
||||
} else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0)
|
||||
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib);
|
||||
else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0)
|
||||
|
|
|
@ -150,6 +150,11 @@ namespace CppcheckXml {
|
|||
const char FunctionContract[] = "contract";
|
||||
const char ContractFunction[] = "function";
|
||||
const char ContractExpects[] = "expects";
|
||||
const char VariableContractsElementName[] = "variable-contracts";
|
||||
const char VariableContractItemElementName[] = "var";
|
||||
const char VariableContractVarName[] = "name";
|
||||
const char VariableContractMin[] = "min";
|
||||
const char VariableContractMax[] = "max";
|
||||
const char LibrariesElementName[] = "libraries";
|
||||
const char LibraryElementName[] = "library";
|
||||
const char PlatformElementName[] = "platform";
|
||||
|
|
|
@ -176,6 +176,12 @@ public:
|
|||
|
||||
std::map<std::string, std::string> functionContracts;
|
||||
|
||||
struct VariableContracts {
|
||||
std::string minValue;
|
||||
std::string maxValue;
|
||||
};
|
||||
std::map<std::string, VariableContracts> variableContracts;
|
||||
|
||||
/** @brief List of include paths, e.g. "my/includes/" which should be used
|
||||
for finding include files inside source files. (-I) */
|
||||
std::list<std::string> includePaths;
|
||||
|
|
|
@ -561,6 +561,9 @@ public:
|
|||
bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const {
|
||||
return mImpl->getCppcheckAttribute(type, value);
|
||||
}
|
||||
bool hasCppcheckAttributes() const {
|
||||
return nullptr != mImpl->mCppcheckAttributes;
|
||||
}
|
||||
bool isControlFlowKeyword() const {
|
||||
return getFlag(fIsControlFlowKeyword);
|
||||
}
|
||||
|
|
|
@ -927,7 +927,7 @@ Cppcheck will warn:
|
|||
|
||||
There are two ways:
|
||||
|
||||
- Open the "Contracts" tab at the bottom of the screen. Find the function in the listbox and double click on it.
|
||||
- Open the "Functions" or "Variables" tab at the bottom of the screen. Find the function or variable in the listbox and double click on it.
|
||||
- Right click on a warning and click on "Edit contract.." in the popup menu. This popup menu item is only available if the warning is not inconclusive.
|
||||
|
||||
## Incomplete analysis
|
||||
|
|
|
@ -35,6 +35,7 @@ private:
|
|||
#ifdef USE_Z3
|
||||
settings.inconclusive = true;
|
||||
LOAD_LIB_2(settings.library, "std.cfg");
|
||||
TEST_CASE(checkAssignment);
|
||||
TEST_CASE(uninit);
|
||||
TEST_CASE(uninit_array);
|
||||
TEST_CASE(uninit_function_par);
|
||||
|
@ -53,6 +54,15 @@ private:
|
|||
ExprEngine::runChecks(this, &tokenizer, &settings);
|
||||
}
|
||||
|
||||
void checkAssignment() {
|
||||
check("void foo(int any) { __cppcheck_low__(0) int x; x = any; }");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) There is assignment, cannot determine that value is greater or equal with 0\n", errout.str());
|
||||
|
||||
check("struct S { __cppcheck_low__(0) int x; };\n"
|
||||
"void foo(S *s, int any) { s->x = any; }");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (error) There is assignment, cannot determine that value is greater or equal with 0\n", errout.str());
|
||||
}
|
||||
|
||||
void uninit() {
|
||||
check("void foo() { int x; x = x + 1; }");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str());
|
||||
|
|
Loading…
Reference in New Issue