Bug hunting: basic handling of contracts through GUI

This commit is contained in:
Daniel Marjamäki 2020-04-27 09:08:50 +02:00
parent 72e0a4be29
commit f7096a2232
18 changed files with 211 additions and 14 deletions

View File

@ -40,7 +40,9 @@ ErrorItem::ErrorItem()
} }
ErrorItem::ErrorItem(const ErrorLogger::ErrorMessage &errmsg) ErrorItem::ErrorItem(const ErrorLogger::ErrorMessage &errmsg)
: errorId(QString::fromStdString(errmsg.id)) : file0(QString::fromStdString(errmsg.file0))
, function(QString::fromStdString(errmsg.function))
, errorId(QString::fromStdString(errmsg.id))
, severity(errmsg.severity) , severity(errmsg.severity)
, inconclusive(errmsg.inconclusive) , inconclusive(errmsg.inconclusive)
, summary(QString::fromStdString(errmsg.shortMessage())) , summary(QString::fromStdString(errmsg.shortMessage()))

View File

@ -80,6 +80,7 @@ public:
QString tool() const; QString tool() const;
QString file0; QString file0;
QString function;
QString errorId; QString errorId;
Severity::SeverityType severity; Severity::SeverityType severity;
bool inconclusive; bool inconclusive;

View File

@ -142,6 +142,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
connect(mUI.mResults, &ResultsView::checkSelected, this, &MainWindow::performSelectedFilesCheck); connect(mUI.mResults, &ResultsView::checkSelected, this, &MainWindow::performSelectedFilesCheck);
connect(mUI.mResults, &ResultsView::tagged, this, &MainWindow::tagged); connect(mUI.mResults, &ResultsView::tagged, this, &MainWindow::tagged);
connect(mUI.mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds); connect(mUI.mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds);
connect(mUI.mResults, &ResultsView::addFunctionContract, this, &MainWindow::addFunctionContract);
connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu); connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu);
// File menu // File menu
@ -844,6 +845,8 @@ Settings MainWindow::getCppcheckSettings()
result.bugHunting = mProjectFile->bugHunting; result.bugHunting = mProjectFile->bugHunting;
result.bugHuntingReport = " "; result.bugHuntingReport = " ";
result.functionContracts = mProjectFile->getFunctionContracts();
const QStringList undefines = mProjectFile->getUndefines(); const QStringList undefines = mProjectFile->getUndefines();
foreach (QString undefine, undefines) foreach (QString undefine, undefines)
result.userUndefs.insert(undefine.toStdString()); result.userUndefs.insert(undefine.toStdString());
@ -1790,3 +1793,20 @@ void MainWindow::suppressIds(QStringList ids)
mProjectFile->setSuppressions(suppressions); mProjectFile->setSuppressions(suppressions);
mProjectFile->write(); mProjectFile->write();
} }
void MainWindow::addFunctionContract(QString function)
{
if (!mProjectFile)
return;
bool ok;
const QString expects = QInputDialog::getText(this,
tr("Add contract"),
"Function:" + function + "\nExpects:",
QLineEdit::Normal,
QString(),
&ok);
if (ok) {
mProjectFile->setFunctionContract(function, expects);
mProjectFile->write();
}
}

View File

@ -225,6 +225,8 @@ protected slots:
/** Suppress error ids */ /** Suppress error ids */
void suppressIds(QStringList ids); void suppressIds(QStringList ids);
/** Add contract for function */
void addFunctionContract(QString function);
private: private:
/** Get filename for last results */ /** Get filename for last results */

View File

@ -55,6 +55,7 @@ void ProjectFile::clear()
mUndefines.clear(); mUndefines.clear();
mPaths.clear(); mPaths.clear();
mExcludedPaths.clear(); mExcludedPaths.clear();
mFunctionContracts.clear();
mLibraries.clear(); mLibraries.clear();
mPlatform.clear(); mPlatform.clear();
mSuppressions.clear(); mSuppressions.clear();
@ -145,6 +146,10 @@ bool ProjectFile::read(const QString &filename)
if (xmlReader.name() == CppcheckXml::IgnoreElementName) if (xmlReader.name() == CppcheckXml::IgnoreElementName)
readExcludes(xmlReader); readExcludes(xmlReader);
// Function contracts
if (xmlReader.name() == CppcheckXml::FunctionContracts)
readFunctionContracts(xmlReader);
// Find libraries list from inside project element // Find libraries list from inside project element
if (xmlReader.name() == CppcheckXml::LibrariesElementName) if (xmlReader.name() == CppcheckXml::LibrariesElementName)
readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName); readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName);
@ -477,6 +482,43 @@ void ProjectFile::readExcludes(QXmlStreamReader &reader)
} while (!allRead); } while (!allRead);
} }
void ProjectFile::readFunctionContracts(QXmlStreamReader &reader)
{
QXmlStreamReader::TokenType type;
bool allRead = false;
do {
type = reader.readNext();
switch (type) {
case QXmlStreamReader::StartElement:
if (reader.name().toString() == CppcheckXml::FunctionContract) {
QXmlStreamAttributes attribs = reader.attributes();
QString function = attribs.value(QString(), CppcheckXml::ContractFunction).toString();
QString expects = attribs.value(QString(), CppcheckXml::ContractExpects).toString();
if (!function.isEmpty() && !expects.isEmpty())
mFunctionContracts[function.toStdString()] = expects.toStdString();
}
break;
case QXmlStreamReader::EndElement:
if (reader.name().toString() == CppcheckXml::FunctionContracts)
allRead = true;
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;
}
} while (!allRead);
}
void ProjectFile::readVsConfigurations(QXmlStreamReader &reader) void ProjectFile::readVsConfigurations(QXmlStreamReader &reader)
{ {
QXmlStreamReader::TokenType type; QXmlStreamReader::TokenType type;
@ -653,6 +695,11 @@ void ProjectFile::setLibraries(const QStringList &libraries)
mLibraries = libraries; mLibraries = libraries;
} }
void ProjectFile::setFunctionContract(QString function, QString expects)
{
mFunctionContracts[function.toStdString()] = expects.toStdString();
}
void ProjectFile::setPlatform(const QString &platform) void ProjectFile::setPlatform(const QString &platform)
{ {
mPlatform = platform; mPlatform = platform;
@ -796,6 +843,17 @@ bool ProjectFile::write(const QString &filename)
CppcheckXml::LibrariesElementName, CppcheckXml::LibrariesElementName,
CppcheckXml::LibraryElementName); CppcheckXml::LibraryElementName);
if (!mFunctionContracts.empty()) {
xmlWriter.writeStartElement(CppcheckXml::FunctionContracts);
for (const auto contract: mFunctionContracts) {
xmlWriter.writeStartElement(CppcheckXml::FunctionContract);
xmlWriter.writeAttribute(CppcheckXml::ContractFunction, QString::fromStdString(contract.first));
xmlWriter.writeAttribute(CppcheckXml::ContractExpects, QString::fromStdString(contract.second));
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
if (!mSuppressions.isEmpty()) { if (!mSuppressions.isEmpty()) {
xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName); xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName);
foreach (const Suppressions::Suppression &suppression, mSuppressions) { foreach (const Suppressions::Suppression &suppression, mSuppressions) {

View File

@ -19,6 +19,7 @@
#ifndef PROJECT_FILE_H #ifndef PROJECT_FILE_H
#define PROJECT_FILE_H #define PROJECT_FILE_H
#include <map>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -200,6 +201,10 @@ public:
mMaxCtuDepth = maxCtuDepth; mMaxCtuDepth = maxCtuDepth;
} }
const std::map<std::string,std::string> getFunctionContracts() const {
return mFunctionContracts;
}
/** /**
* @brief Get filename for the project file. * @brief Get filename for the project file.
* @return file name. * @return file name.
@ -264,6 +269,9 @@ public:
*/ */
void setLibraries(const QStringList &libraries); void setLibraries(const QStringList &libraries);
/** Set contract for a function */
void setFunctionContract(QString function, QString expects);
/** /**
* @brief Set platform. * @brief Set platform.
* @param platform platform. * @param platform platform.
@ -377,6 +385,12 @@ protected:
*/ */
void readExcludes(QXmlStreamReader &reader); void readExcludes(QXmlStreamReader &reader);
/**
* @brief Read function contracts.
* @param reader XML stream reader.
*/
void readFunctionContracts(QXmlStreamReader &reader);
/** /**
* @brief Read lists of Visual Studio configurations * @brief Read lists of Visual Studio configurations
* @param reader XML stream reader. * @param reader XML stream reader.
@ -486,6 +500,8 @@ private:
*/ */
QStringList mLibraries; QStringList mLibraries;
std::map<std::string, std::string> mFunctionContracts;
/** /**
* @brief Platform * @brief Platform
*/ */

View File

@ -191,6 +191,7 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
data["id"] = item.errorId; data["id"] = item.errorId;
data["inconclusive"] = item.inconclusive; data["inconclusive"] = item.inconclusive;
data["file0"] = stripPath(item.file0, true); data["file0"] = stripPath(item.file0, true);
data["function"] = item.function;
data["sinceDate"] = item.sinceDate; data["sinceDate"] = item.sinceDate;
data["tags"] = item.tags; data["tags"] = item.tags;
data["hide"] = hide; data["hide"] = hide;
@ -561,6 +562,7 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
QModelIndex index = indexAt(e->pos()); QModelIndex index = indexAt(e->pos());
if (index.isValid()) { if (index.isValid()) {
bool multipleSelection = false; bool multipleSelection = false;
mSelectionModel = selectionModel(); mSelectionModel = selectionModel();
if (mSelectionModel->selectedRows().count() > 1) if (mSelectionModel->selectedRows().count() > 1)
multipleSelection = true; multipleSelection = true;
@ -609,12 +611,20 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
menu.addSeparator(); menu.addSeparator();
} }
const bool bughunting = !multipleSelection && mContextItem->data().toMap().value("id").toString().startsWith("bughunting");
if (bughunting) {
QAction *addContract = new QAction(tr("Add contract.."), &menu);
connect(addContract, SIGNAL(triggered()), this, SLOT(addContract()));
menu.addAction(addContract);
menu.addSeparator();
}
//Create an action for the application //Create an action for the application
QAction *recheckSelectedFiles = new QAction(tr("Recheck"), &menu); QAction *recheckSelectedFiles = new QAction(tr("Recheck"), &menu);
QAction *copy = new QAction(tr("Copy"), &menu); QAction *copy = new QAction(tr("Copy"), &menu);
QAction *hide = new QAction(tr("Hide"), &menu); QAction *hide = new QAction(tr("Hide"), &menu);
QAction *hideallid = new QAction(tr("Hide all with id"), &menu); QAction *hideallid = new QAction(tr("Hide all with id"), &menu);
QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
QAction *opencontainingfolder = new QAction(tr("Open containing folder"), &menu); QAction *opencontainingfolder = new QAction(tr("Open containing folder"), &menu);
if (multipleSelection) { if (multipleSelection) {
@ -632,7 +642,11 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
menu.addSeparator(); menu.addSeparator();
menu.addAction(hide); menu.addAction(hide);
menu.addAction(hideallid); menu.addAction(hideallid);
if (!bughunting) {
QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
menu.addAction(suppress); menu.addAction(suppress);
connect(suppress, SIGNAL(triggered()), this, SLOT(suppressSelectedIds()));
}
menu.addSeparator(); menu.addSeparator();
menu.addAction(opencontainingfolder); menu.addAction(opencontainingfolder);
@ -640,7 +654,6 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
connect(copy, SIGNAL(triggered()), this, SLOT(copy())); connect(copy, SIGNAL(triggered()), this, SLOT(copy()));
connect(hide, SIGNAL(triggered()), this, SLOT(hideResult())); connect(hide, SIGNAL(triggered()), this, SLOT(hideResult()));
connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult())); connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult()));
connect(suppress, SIGNAL(triggered()), this, SLOT(suppressSelectedIds()));
connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder())); connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
if (!mTags.isEmpty()) { if (!mTags.isEmpty()) {
@ -1020,6 +1033,12 @@ void ResultsTree::openContainingFolder()
} }
} }
void ResultsTree::addContract()
{
QString function = mContextItem->data().toMap().value("function").toString();
emit addFunctionContract(function);
}
void ResultsTree::tagSelectedItems(const QString &tag) void ResultsTree::tagSelectedItems(const QString &tag)
{ {
if (!mSelectionModel) if (!mSelectionModel)

View File

@ -208,6 +208,8 @@ signals:
/** Suppress Ids */ /** Suppress Ids */
void suppressIds(QStringList ids); void suppressIds(QStringList ids);
/** Add contract for function */
void addFunctionContract(QString function);
public slots: public slots:
/** /**
@ -282,6 +284,11 @@ protected slots:
*/ */
void openContainingFolder(); void openContainingFolder();
/**
* @brief Allow user to add contract to fix bughunting warning
*/
void addContract();
/** /**
* @brief Slot for selection change in the results tree. * @brief Slot for selection change in the results tree.
* *

View File

@ -55,6 +55,7 @@ ResultsView::ResultsView(QWidget * parent) :
connect(mUI.mTree, &ResultsTree::treeSelectionChanged, this, &ResultsView::updateDetails); connect(mUI.mTree, &ResultsTree::treeSelectionChanged, this, &ResultsView::updateDetails);
connect(mUI.mTree, &ResultsTree::tagged, this, &ResultsView::tagged); connect(mUI.mTree, &ResultsTree::tagged, this, &ResultsView::tagged);
connect(mUI.mTree, &ResultsTree::suppressIds, this, &ResultsView::suppressIds); connect(mUI.mTree, &ResultsTree::suppressIds, this, &ResultsView::suppressIds);
connect(mUI.mTree, &ResultsTree::addFunctionContract, this, &ResultsView::addFunctionContract);
connect(this, &ResultsView::showResults, mUI.mTree, &ResultsTree::showResults); connect(this, &ResultsView::showResults, mUI.mTree, &ResultsTree::showResults);
connect(this, &ResultsView::showCppcheckResults, mUI.mTree, &ResultsTree::showCppcheckResults); connect(this, &ResultsView::showCppcheckResults, mUI.mTree, &ResultsTree::showCppcheckResults);
connect(this, &ResultsView::showClangResults, mUI.mTree, &ResultsTree::showClangResults); connect(this, &ResultsView::showClangResults, mUI.mTree, &ResultsTree::showClangResults);

View File

@ -228,6 +228,9 @@ signals:
/** Suppress Ids */ /** Suppress Ids */
void suppressIds(QStringList ids); void suppressIds(QStringList ids);
/** Add contract for function */
void addFunctionContract(QString function);
/** /**
* @brief Show/hide certain type of errors * @brief Show/hide certain type of errors
* Refreshes the tree. * Refreshes the tree.

View File

@ -314,8 +314,10 @@ public:
std::list<FileLocation> callStack; std::list<FileLocation> callStack;
std::string id; std::string id;
/** source file (not header) */ /** For GUI rechecking; source file (not header) */
std::string file0; std::string file0;
/** For GUI bug hunting; function name */
std::string function;
Severity::SeverityType severity; Severity::SeverityType severity;
CWE cwe; CWE cwe;

View File

@ -158,8 +158,8 @@ namespace {
class Data : public ExprEngine::DataBase { class Data : public ExprEngine::DataBase {
public: public:
Data(int *symbolValueIndex, const Tokenizer *tokenizer, const Settings *settings, const std::vector<ExprEngine::Callback> &callbacks, TrackExecution *trackExecution) Data(int *symbolValueIndex, const Tokenizer *tokenizer, const Settings *settings, const std::string &currentFunction, const std::vector<ExprEngine::Callback> &callbacks, TrackExecution *trackExecution)
: DataBase(settings) : DataBase(currentFunction, settings)
, symbolValueIndex(symbolValueIndex) , symbolValueIndex(symbolValueIndex)
, tokenizer(tokenizer) , tokenizer(tokenizer)
, callbacks(callbacks) , callbacks(callbacks)
@ -172,6 +172,28 @@ namespace {
const std::vector<ExprEngine::Callback> &callbacks; const std::vector<ExprEngine::Callback> &callbacks;
std::vector<ExprEngine::ValuePtr> constraints; std::vector<ExprEngine::ValuePtr> constraints;
void contractConstraints(const Function *function, ExprEngine::ValuePtr (*executeExpression)(const Token*, Data&)) {
const auto it = settings->functionContracts.find(currentFunction);
if (it == settings->functionContracts.end())
return;
const std::string &expects = it->second;
TokenList tokenList(settings);
std::istringstream istr(expects);
tokenList.createTokens(istr);
tokenList.createAst();
SymbolDatabase *symbolDatabase = const_cast<SymbolDatabase*>(tokenizer->getSymbolDatabase());
for (Token *tok = tokenList.front(); tok; tok = tok->next()) {
for (const Variable &arg: function->argumentList) {
if (arg.name() == tok->str()) {
tok->variable(&arg);
tok->varId(arg.declarationId());
}
}
}
symbolDatabase->setValueTypeInTokenList(false, tokenList.front());
constraints.push_back(executeExpression(tokenList.front()->astTop(), *this));
}
void addError(int linenr) OVERRIDE { void addError(int linenr) OVERRIDE {
mTrackExecution->addError(linenr); mTrackExecution->addError(linenr);
} }
@ -1756,13 +1778,17 @@ void ExprEngine::executeFunction(const Scope *functionScope, const Tokenizer *to
// TODO.. what about functions in headers? // TODO.. what about functions in headers?
return; return;
const std::string currentFunction = function->fullName();
int symbolValueIndex = 0; int symbolValueIndex = 0;
TrackExecution trackExecution; TrackExecution trackExecution;
Data data(&symbolValueIndex, tokenizer, settings, callbacks, &trackExecution); Data data(&symbolValueIndex, tokenizer, settings, currentFunction, callbacks, &trackExecution);
for (const Variable &arg : function->argumentList) for (const Variable &arg : function->argumentList)
data.assignValue(functionScope->bodyStart, arg.declarationId(), createVariableValue(arg, data)); data.assignValue(functionScope->bodyStart, arg.declarationId(), createVariableValue(arg, data));
data.contractConstraints(function, executeExpression1);
try { try {
execute(functionScope->bodyStart, functionScope->bodyEnd, data); execute(functionScope->bodyStart, functionScope->bodyEnd, data);
} catch (VerifyException &e) { } catch (VerifyException &e) {
@ -1829,6 +1855,7 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer,
std::list<const Token*> callstack{settings->clang ? tok : tok->astParent()}; std::list<const Token*> callstack{settings->clang ? tok : tok->astParent()};
const char * const id = (tok->valueType() && tok->valueType()->isFloat()) ? "bughuntingDivByZeroFloat" : "bughuntingDivByZero"; const char * const id = (tok->valueType() && tok->valueType()->isFloat()) ? "bughuntingDivByZeroFloat" : "bughuntingDivByZero";
ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, id, "There is division, cannot determine that there can't be a division by zero.", CWE(369), false); ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, id, "There is division, cannot determine that there can't be a division by zero.", CWE(369), false);
errmsg.function = dataBase->currentFunction;
errorLogger->reportErr(errmsg); errorLogger->reportErr(errmsg);
} }
}; };

View File

@ -71,8 +71,12 @@ namespace ExprEngine {
class DataBase { class DataBase {
public: public:
explicit DataBase(const Settings *settings) : settings(settings) {} explicit DataBase(const std::string &currentFunction, const Settings *settings)
: currentFunction(currentFunction)
, settings(settings) {
}
virtual std::string getNewSymbolName() = 0; virtual std::string getNewSymbolName() = 0;
const std::string currentFunction;
const Settings * const settings; const Settings * const settings;
virtual void addError(int linenr) { virtual void addError(int linenr) {
(void)linenr; (void)linenr;

View File

@ -1018,7 +1018,9 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0 && node->Attribute(CppcheckXml::RootPathNameAttrib)) { if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0 && node->Attribute(CppcheckXml::RootPathNameAttrib)) {
temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib))); temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib)));
temp.relativePaths = true; temp.relativePaths = true;
} else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0) } else if (strcmp(node->Name(), CppcheckXml::BugHunting) == 0)
temp.bugHunting = true;
else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0)
temp.buildDir = joinRelativePath(path, node->GetText() ? node->GetText() : ""); temp.buildDir = joinRelativePath(path, node->GetText() ? node->GetText() : "");
else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0) else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0)
temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib); temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib);
@ -1033,6 +1035,16 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib); paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib);
else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0) else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0)
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib);
else if (strcmp(node->Name(), CppcheckXml::FunctionContracts) == 0) {
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
if (strcmp(child->Name(), CppcheckXml::FunctionContract) == 0) {
const char *function = child->Attribute(CppcheckXml::ContractFunction);
const char *expects = child->Attribute(CppcheckXml::ContractExpects);
if (function && expects)
temp.functionContracts[function] = expects;
}
}
}
else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0) else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0)
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib);
else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0) else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0)
@ -1099,6 +1111,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
settings->checkUnusedTemplates = temp.checkUnusedTemplates; settings->checkUnusedTemplates = temp.checkUnusedTemplates;
settings->maxCtuDepth = temp.maxCtuDepth; settings->maxCtuDepth = temp.maxCtuDepth;
settings->safeChecks = temp.safeChecks; settings->safeChecks = temp.safeChecks;
settings->bugHunting = temp.bugHunting;
settings->functionContracts = temp.functionContracts;
return true; return true;
} }

View File

@ -146,6 +146,10 @@ namespace CppcheckXml {
const char ExcludeElementName[] = "exclude"; const char ExcludeElementName[] = "exclude";
const char ExcludePathName[] = "path"; const char ExcludePathName[] = "path";
const char ExcludePathNameAttrib[] = "name"; const char ExcludePathNameAttrib[] = "name";
const char FunctionContracts[] = "function-contracts";
const char FunctionContract[] = "contract";
const char ContractFunction[] = "function";
const char ContractExpects[] = "expects";
const char LibrariesElementName[] = "libraries"; const char LibrariesElementName[] = "libraries";
const char LibraryElementName[] = "library"; const char LibraryElementName[] = "library";
const char PlatformElementName[] = "platform"; const char PlatformElementName[] = "platform";

View File

@ -173,6 +173,8 @@ public:
/** @brief Force checking the files with "too many" configurations (--force). */ /** @brief Force checking the files with "too many" configurations (--force). */
bool force; bool force;
std::map<std::string, std::string> functionContracts;
/** @brief List of include paths, e.g. "my/includes/" which should be used /** @brief List of include paths, e.g. "my/includes/" which should be used
for finding include files inside source files. (-I) */ for finding include files inside source files. (-I) */
std::list<std::string> includePaths; std::list<std::string> includePaths;

View File

@ -2157,6 +2157,18 @@ Function::Function(const Token *tokenDef)
{ {
} }
std::string Function::fullName() const
{
std::string ret = name();
for (const Scope *s = nestedIn; s; s = s->nestedIn) {
if (!s->className.empty())
ret = s->className + "::" + ret;
}
ret += "(";
for (const Variable &arg : argumentList)
ret += (arg.index() == 0 ? "" : ",") + arg.name();
return ret + ")";
}
static std::string qualifiedName(const Scope *scope) static std::string qualifiedName(const Scope *scope)
{ {
@ -5763,9 +5775,10 @@ static const Function *getOperatorFunction(const Token * const tok)
return nullptr; return nullptr;
} }
void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings) void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens)
{ {
Token * tokens = const_cast<Tokenizer *>(mTokenizer)->list.front(); if (!tokens)
tokens = const_cast<Tokenizer *>(mTokenizer)->list.front();
for (Token *tok = tokens; tok; tok = tok->next()) for (Token *tok = tokens; tok; tok = tok->next())
tok->setValueType(nullptr); tok->setValueType(nullptr);

View File

@ -743,6 +743,8 @@ public:
return tokenDef->str(); return tokenDef->str();
} }
std::string fullName() const;
nonneg int argCount() const { nonneg int argCount() const {
return argumentList.size(); return argumentList.size();
} }
@ -1310,7 +1312,7 @@ public:
void validateVariables() const; void validateVariables() const;
/** Set valuetype in provided tokenlist */ /** Set valuetype in provided tokenlist */
void setValueTypeInTokenList(bool reportDebugWarnings); void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr);
/** /**
* Calculates sizeof value for given type. * Calculates sizeof value for given type.