Variable contracts

This commit is contained in:
Daniel Marjamäki 2020-08-22 11:37:44 +02:00
parent 02287d9d34
commit 6277eece67
19 changed files with 624 additions and 23 deletions

View File

@ -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 \

View File

@ -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();
}
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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
*/

View File

@ -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);
}

View File

@ -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;

View File

@ -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>

View File

@ -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();
}

View File

@ -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

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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";

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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());