GUI: Better settings for extended safe checks

This commit is contained in:
Daniel Marjamäki 2019-07-23 11:54:38 +02:00
parent 8f9912d245
commit 681bd0a911
9 changed files with 322 additions and 82 deletions

View File

@ -895,7 +895,10 @@ Settings MainWindow::getCppcheckSettings()
result.maxCtuDepth = mProjectFile->getMaxCtuDepth(); result.maxCtuDepth = mProjectFile->getMaxCtuDepth();
result.checkHeaders = mProjectFile->getCheckHeaders(); result.checkHeaders = mProjectFile->getCheckHeaders();
result.checkUnusedTemplates = mProjectFile->getCheckUnusedTemplates(); result.checkUnusedTemplates = mProjectFile->getCheckUnusedTemplates();
result.allFunctionsAreSafe = mProjectFile->getCheckAllFunctionParameterValues(); result.safeChecks.classes = mProjectFile->getSafeChecks().classes;
result.safeChecks.externalFunctions = mProjectFile->getSafeChecks().externalFunctions;
result.safeChecks.internalFunctions = mProjectFile->getSafeChecks().internalFunctions;
result.safeChecks.externalVariables = mProjectFile->getSafeChecks().externalVariables;
foreach (QString s, mProjectFile->getCheckUnknownFunctionReturn()) foreach (QString s, mProjectFile->getCheckUnknownFunctionReturn())
result.checkUnknownFunctionReturn.insert(s.toStdString()); result.checkUnknownFunctionReturn.insert(s.toStdString());
} }

View File

@ -26,6 +26,7 @@
#include "common.h" #include "common.h"
#include "path.h" #include "path.h"
#include "settings.h"
static const char ProjectElementName[] = "project"; static const char ProjectElementName[] = "project";
static const char ProjectVersionAttrib[] = "version"; static const char ProjectVersionAttrib[] = "version";
@ -67,7 +68,6 @@ static const char CheckHeadersElementName[] = "check-headers";
static const char CheckUnusedTemplatesElementName[] = "check-unused-templates"; static const char CheckUnusedTemplatesElementName[] = "check-unused-templates";
static const char MaxCtuDepthElementName[] = "max-ctu-depth"; static const char MaxCtuDepthElementName[] = "max-ctu-depth";
static const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; static const char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values";
static const char CheckAllFunctionParameterValues[] = "check-all-function-parameter-values";
static const char Name[] = "name"; static const char Name[] = "name";
ProjectFile::ProjectFile(QObject *parent) : ProjectFile::ProjectFile(QObject *parent) :
@ -104,8 +104,8 @@ void ProjectFile::clear()
mCheckHeaders = true; mCheckHeaders = true;
mCheckUnusedTemplates = false; mCheckUnusedTemplates = false;
mMaxCtuDepth = 10; mMaxCtuDepth = 10;
mCheckAllFunctionParameterValues = false;
mCheckUnknownFunctionReturn.clear(); mCheckUnknownFunctionReturn.clear();
mSafeChecks.clear();
} }
bool ProjectFile::read(const QString &filename) bool ProjectFile::read(const QString &filename)
@ -128,77 +128,81 @@ bool ProjectFile::read(const QString &filename)
if (xmlReader.name() == ProjectElementName) { if (xmlReader.name() == ProjectElementName) {
insideProject = true; insideProject = true;
projectTagFound = true; projectTagFound = true;
break;
} }
if (!insideProject)
break;
// Read root path from inside project element // Read root path from inside project element
if (insideProject && xmlReader.name() == RootPathName) if (xmlReader.name() == RootPathName)
readRootPath(xmlReader); readRootPath(xmlReader);
// Read root path from inside project element // Read root path from inside project element
if (insideProject && xmlReader.name() == BuildDirElementName) if (xmlReader.name() == BuildDirElementName)
readBuildDir(xmlReader); readBuildDir(xmlReader);
// Find paths to check from inside project element // Find paths to check from inside project element
if (insideProject && xmlReader.name() == PathsElementName) if (xmlReader.name() == PathsElementName)
readCheckPaths(xmlReader); readCheckPaths(xmlReader);
if (insideProject && xmlReader.name() == ImportProjectElementName) if (xmlReader.name() == ImportProjectElementName)
readImportProject(xmlReader); readImportProject(xmlReader);
if (insideProject && xmlReader.name() == AnalyzeAllVsConfigsElementName) if (xmlReader.name() == AnalyzeAllVsConfigsElementName)
mAnalyzeAllVsConfigs = readBool(xmlReader); mAnalyzeAllVsConfigs = readBool(xmlReader);
if (insideProject && xmlReader.name() == CheckHeadersElementName) if (xmlReader.name() == CheckHeadersElementName)
mCheckHeaders = readBool(xmlReader); mCheckHeaders = readBool(xmlReader);
if (insideProject && xmlReader.name() == CheckUnusedTemplatesElementName) if (xmlReader.name() == CheckUnusedTemplatesElementName)
mCheckUnusedTemplates = readBool(xmlReader); mCheckUnusedTemplates = readBool(xmlReader);
// Find include directory from inside project element // Find include directory from inside project element
if (insideProject && xmlReader.name() == IncludeDirElementName) if (xmlReader.name() == IncludeDirElementName)
readIncludeDirs(xmlReader); readIncludeDirs(xmlReader);
// Find preprocessor define from inside project element // Find preprocessor define from inside project element
if (insideProject && xmlReader.name() == DefinesElementName) if (xmlReader.name() == DefinesElementName)
readDefines(xmlReader); readDefines(xmlReader);
// Find preprocessor define from inside project element // Find preprocessor define from inside project element
if (insideProject && xmlReader.name() == UndefinesElementName) if (xmlReader.name() == UndefinesElementName)
readStringList(mUndefines, xmlReader, UndefineName); readStringList(mUndefines, xmlReader, UndefineName);
// Find exclude list from inside project element // Find exclude list from inside project element
if (insideProject && xmlReader.name() == ExcludeElementName) if (xmlReader.name() == ExcludeElementName)
readExcludes(xmlReader); readExcludes(xmlReader);
// Find ignore list from inside project element // Find ignore list from inside project element
// These are read for compatibility // These are read for compatibility
if (insideProject && xmlReader.name() == IgnoreElementName) if (xmlReader.name() == IgnoreElementName)
readExcludes(xmlReader); readExcludes(xmlReader);
// Find libraries list from inside project element // Find libraries list from inside project element
if (insideProject && xmlReader.name() == LibrariesElementName) if (xmlReader.name() == LibrariesElementName)
readStringList(mLibraries, xmlReader,LibraryElementName); readStringList(mLibraries, xmlReader,LibraryElementName);
if (insideProject && xmlReader.name() == PlatformElementName) if (xmlReader.name() == PlatformElementName)
readPlatform(xmlReader); readPlatform(xmlReader);
// Find suppressions list from inside project element // Find suppressions list from inside project element
if (insideProject && xmlReader.name() == SuppressionsElementName) if (xmlReader.name() == SuppressionsElementName)
readSuppressions(xmlReader); readSuppressions(xmlReader);
// Unknown function return values // Unknown function return values
if (insideProject && xmlReader.name() == CheckUnknownFunctionReturn) if (xmlReader.name() == CheckUnknownFunctionReturn)
readStringList(mCheckUnknownFunctionReturn, xmlReader, Name); readStringList(mCheckUnknownFunctionReturn, xmlReader, Name);
// check all function parameter values // check all function parameter values
if (insideProject && xmlReader.name() == CheckAllFunctionParameterValues) if (xmlReader.name() == Settings::SafeChecks::XmlRootName)
mCheckAllFunctionParameterValues = true; mSafeChecks.loadFromXml(xmlReader);
// Addons // Addons
if (insideProject && xmlReader.name() == AddonsElementName) if (xmlReader.name() == AddonsElementName)
readStringList(mAddons, xmlReader, AddonElementName); readStringList(mAddons, xmlReader, AddonElementName);
// Tools // Tools
if (insideProject && xmlReader.name() == ToolsElementName) { if (xmlReader.name() == ToolsElementName) {
QStringList tools; QStringList tools;
readStringList(tools, xmlReader, ToolElementName); readStringList(tools, xmlReader, ToolElementName);
mClangAnalyzer = tools.contains(CLANG_ANALYZER); mClangAnalyzer = tools.contains(CLANG_ANALYZER);
@ -796,10 +800,7 @@ bool ProjectFile::write(const QString &filename)
CheckUnknownFunctionReturn, CheckUnknownFunctionReturn,
Name); Name);
if (mCheckAllFunctionParameterValues) { mSafeChecks.saveToXml(xmlWriter);
xmlWriter.writeStartElement(CheckAllFunctionParameterValues);
xmlWriter.writeEndElement();
}
writeStringList(xmlWriter, writeStringList(xmlWriter,
mAddons, mAddons,
@ -854,3 +855,67 @@ QStringList ProjectFile::getAddonsAndTools() const
ret << CLANG_TIDY; ret << CLANG_TIDY;
return ret; return ret;
} }
void ProjectFile::SafeChecks::loadFromXml(QXmlStreamReader &xmlReader)
{
classes = externalFunctions = internalFunctions = externalVariables = false;
int level = 0;
do {
const QXmlStreamReader::TokenType type = xmlReader.readNext();
switch (type) {
case QXmlStreamReader::StartElement:
++level;
if (xmlReader.name() == Settings::SafeChecks::XmlClasses)
classes = true;
else if (xmlReader.name() == Settings::SafeChecks::XmlExternalFunctions)
externalFunctions = true;
else if (xmlReader.name() == Settings::SafeChecks::XmlInternalFunctions)
internalFunctions = true;
else if (xmlReader.name() == Settings::SafeChecks::XmlExternalVariables)
externalVariables = true;
break;
case QXmlStreamReader::EndElement:
if (level <= 0)
return;
level--;
break;
// Not handled
case QXmlStreamReader::Characters:
case QXmlStreamReader::NoToken:
case QXmlStreamReader::Invalid:
case QXmlStreamReader::StartDocument:
case QXmlStreamReader::EndDocument:
case QXmlStreamReader::Comment:
case QXmlStreamReader::DTD:
case QXmlStreamReader::EntityReference:
case QXmlStreamReader::ProcessingInstruction:
break;
}
} while (1);
}
void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const
{
if (!classes && !externalFunctions && !internalFunctions && !externalVariables)
return;
xmlWriter.writeStartElement(Settings::SafeChecks::XmlRootName);
if (classes) {
xmlWriter.writeStartElement(Settings::SafeChecks::XmlClasses);
xmlWriter.writeEndElement();
}
if (externalFunctions) {
xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalFunctions);
xmlWriter.writeEndElement();
}
if (internalFunctions) {
xmlWriter.writeStartElement(Settings::SafeChecks::XmlInternalFunctions);
xmlWriter.writeEndElement();
}
if (externalVariables) {
xmlWriter.writeStartElement(Settings::SafeChecks::XmlExternalVariables);
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}

View File

@ -300,12 +300,52 @@ public:
mFilename = filename; mFilename = filename;
} }
/** Experimental: checking all function parameter values */ /** Do not only check how interface is used. Also check that interface is safe. */
bool getCheckAllFunctionParameterValues() const { class SafeChecks {
return mCheckAllFunctionParameterValues; public:
SafeChecks() : classes(false), externalFunctions(false), internalFunctions(false), externalVariables(false) {}
void clear() {
classes = externalFunctions = internalFunctions = externalVariables = false;
}
void loadFromXml(QXmlStreamReader &xmlReader);
void saveToXml(QXmlStreamWriter &xmlWriter) const;
/**
* Public interface of classes
* - public function parameters can have any value
* - public functions can be called in any order
* - public variables can have any value
*/
bool classes;
/**
* External functions
* - external functions can be called in any order
* - function parameters can have any values
*/
bool externalFunctions;
/**
* Experimental: assume that internal functions can be used in any way
* This is only available in the GUI.
*/
bool internalFunctions;
/**
* Global variables that can be modified outside the TU.
* - Such variable can have "any" value
*/
bool externalVariables;
};
/** Safe checks */
SafeChecks getSafeChecks() const {
return mSafeChecks;
} }
void setCheckAllFunctionParameterValues(bool b) { void setSafeChecks(SafeChecks safeChecks) {
mCheckAllFunctionParameterValues = b; mSafeChecks = safeChecks;
} }
/** Check unknown function return values */ /** Check unknown function return values */
@ -489,7 +529,7 @@ private:
/** Max CTU depth */ /** Max CTU depth */
int mMaxCtuDepth; int mMaxCtuDepth;
bool mCheckAllFunctionParameterValues; SafeChecks mSafeChecks;
QStringList mCheckUnknownFunctionReturn; QStringList mCheckUnknownFunctionReturn;

View File

@ -283,7 +283,10 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
const bool unknownValues = projectFile->getCheckUnknownFunctionReturn().contains(item->text()); const bool unknownValues = projectFile->getCheckUnknownFunctionReturn().contains(item->text());
item->setCheckState(unknownValues ? Qt::Checked : Qt::Unchecked); // AND initialize check state item->setCheckState(unknownValues ? Qt::Checked : Qt::Unchecked); // AND initialize check state
} }
mUI.mAllFunctionsAreSafe->setChecked(projectFile->getCheckAllFunctionParameterValues()); mUI.mCheckSafeClasses->setChecked(projectFile->getSafeChecks().classes);
mUI.mCheckSafeExternalFunctions->setChecked(projectFile->getSafeChecks().externalFunctions);
mUI.mCheckSafeInternalFunctions->setChecked(projectFile->getSafeChecks().internalFunctions);
mUI.mCheckSafeExternalVariables->setChecked(projectFile->getSafeChecks().externalVariables);
// Addons.. // Addons..
QSettings settings; QSettings settings;
@ -346,7 +349,12 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
unknownReturnValues << item->text(); unknownReturnValues << item->text();
} }
projectFile->setCheckUnknownFunctionReturn(unknownReturnValues); projectFile->setCheckUnknownFunctionReturn(unknownReturnValues);
projectFile->setCheckAllFunctionParameterValues(mUI.mAllFunctionsAreSafe->isChecked()); ProjectFile::SafeChecks safeChecks;
safeChecks.classes = mUI.mCheckSafeClasses->isChecked();
safeChecks.externalFunctions = mUI.mCheckSafeExternalFunctions->isChecked();
safeChecks.internalFunctions = mUI.mCheckSafeInternalFunctions->isChecked();
safeChecks.externalVariables = mUI.mCheckSafeExternalVariables->isChecked();
projectFile->setSafeChecks(safeChecks);
// Addons // Addons
QStringList list; QStringList list;
if (mUI.mAddonThreadSafety->isChecked()) if (mUI.mAddonThreadSafety->isChecked())

View File

@ -417,9 +417,105 @@
</widget> </widget>
<widget class="QWidget" name="mTabHumanKnownledge"> <widget class="QWidget" name="mTabHumanKnownledge">
<attribute name="title"> <attribute name="title">
<string>Human knowledge</string> <string>Extended checks</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_19"> <layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>&quot;Safe&quot; interface: Cppcheck does not only check how it is actually used, but analyze any possible usage.</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>840</width>
<height>273</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<widget class="QCheckBox" name="mCheckSafeClasses">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Classes - Public interface
- public function parameters can have any value
- public functions can be called in any order
- public variables can have any value</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mCheckSafeExternalFunctions">
<property name="text">
<string>External functions
- External functions can be called in any order
- Function parameters can have any value</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mCheckSafeExternalVariables">
<property name="text">
<string>External variables
- Variable can have any value</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mCheckSafeInternalFunctions">
<property name="text">
<string>Internal/private functions
- Function parameters can have any value</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox_13"> <widget class="QGroupBox" name="groupBox_13">
<property name="title"> <property name="title">
@ -468,48 +564,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>Possible values of function parameters</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_18">
<item>
<widget class="QCheckBox" name="mAllFunctionsAreSafe">
<property name="text">
<string>Experimental: All function parameters can have arbitrary values</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>All classes must have a &quot;safe&quot; public interface</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_11">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="mTabWarningOptions"> <widget class="QWidget" name="mTabWarningOptions">

View File

@ -1068,6 +1068,20 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0); temp.checkUnusedTemplates = (strcmp(node->GetText(), "true") == 0);
else if (strcmp(node->Name(), MaxCtuDepthElementName) == 0) else if (strcmp(node->Name(), MaxCtuDepthElementName) == 0)
temp.maxCtuDepth = std::atoi(node->GetText()); temp.maxCtuDepth = std::atoi(node->GetText());
else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) {
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0)
temp.safeChecks.classes = true;
else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0)
temp.safeChecks.externalFunctions = true;
else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0)
temp.safeChecks.internalFunctions = false; // This is not available in CLI
else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0)
temp.safeChecks.externalVariables = true;
else
return false;
}
}
else else
return false; return false;
} }

View File

@ -22,6 +22,12 @@
bool Settings::mTerminated; bool Settings::mTerminated;
const char Settings::SafeChecks::XmlRootName[] = "safe-checks";
const char Settings::SafeChecks::XmlClasses[] = "class-public";
const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions";
const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions";
const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables";
Settings::Settings() Settings::Settings()
: mEnabled(0), : mEnabled(0),
checkConfiguration(false), checkConfiguration(false),
@ -39,7 +45,6 @@ Settings::Settings()
experimental(false), experimental(false),
force(false), force(false),
inconclusive(false), inconclusive(false),
allFunctionsAreSafe(false),
inlineSuppressions(false), inlineSuppressions(false),
jobs(1), jobs(1),
jointSuppressionReport(false), jointSuppressionReport(false),

View File

@ -146,8 +146,46 @@ public:
/** @brief Inconclusive checks */ /** @brief Inconclusive checks */
bool inconclusive; bool inconclusive;
/** @brief Experimental flag that says all functions are "safe" */ /** Do not only check how interface is used. Also check that interface is safe. */
bool allFunctionsAreSafe; class SafeChecks {
public:
SafeChecks() : classes(false), externalFunctions(false), internalFunctions(false), externalVariables(false) {}
static const char XmlRootName[];
static const char XmlClasses[];
static const char XmlExternalFunctions[];
static const char XmlInternalFunctions[];
static const char XmlExternalVariables[];
/**
* Public interface of classes
* - public function parameters can have any value
* - public functions can be called in any order
* - public variables can have any value
*/
bool classes;
/**
* External functions
* - external functions can be called in any order
* - function parameters can have any values
*/
bool externalFunctions;
/**
* Experimental: assume that internal functions can be used in any way
* This is only available in the GUI.
*/
bool internalFunctions;
/**
* Global variables that can be modified outside the TU.
* - Such variable can have "any" value
*/
bool externalVariables;
};
SafeChecks safeChecks;
/** @brief check unknown function return values */ /** @brief check unknown function return values */
std::set<std::string> checkUnknownFunctionReturn; std::set<std::string> checkUnknownFunctionReturn;

View File

@ -2081,7 +2081,20 @@ const Token * Function::constructorMemberInitialization() const
bool Function::isSafe(const Settings *settings) const bool Function::isSafe(const Settings *settings) const
{ {
return settings->allFunctionsAreSafe; switch (access) {
case AccessControl::Local:
case AccessControl::Private:
case AccessControl::Protected:
return settings->safeChecks.internalFunctions;
case AccessControl::Public:
return settings->safeChecks.classes;
case AccessControl::Namespace:
case AccessControl::Global:
return settings->safeChecks.externalFunctions;
case AccessControl::Throw:
return false;
};
return false;
} }
Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart)