Merge branch 'main' into fp-invalid-container-pointer
This commit is contained in:
commit
abeea7b32b
|
@ -50,7 +50,7 @@ jobs:
|
||||||
- name: Install Qt
|
- name: Install Qt
|
||||||
uses: jurplel/install-qt-action@v2
|
uses: jurplel/install-qt-action@v2
|
||||||
with:
|
with:
|
||||||
modules: 'qtcharts'
|
modules: 'qtcharts qthelp'
|
||||||
|
|
||||||
- name: Create .qm
|
- name: Create .qm
|
||||||
run: |
|
run: |
|
||||||
|
@ -58,6 +58,11 @@ jobs:
|
||||||
lupdate gui.pro
|
lupdate gui.pro
|
||||||
lrelease gui.pro -removeidentical
|
lrelease gui.pro -removeidentical
|
||||||
|
|
||||||
|
- name: Create online-help
|
||||||
|
run: |
|
||||||
|
cd gui\help
|
||||||
|
qhelpgenerator online-help.qhcp -o online-help.qhc
|
||||||
|
|
||||||
- name: Matchcompiler
|
- name: Matchcompiler
|
||||||
run: python tools\matchcompiler.py --write-dir lib
|
run: python tools\matchcompiler.py --write-dir lib
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ env:
|
||||||
before_install:
|
before_install:
|
||||||
# install needed deps
|
# install needed deps
|
||||||
- travis_retry sudo apt-get update -qq
|
- travis_retry sudo apt-get update -qq
|
||||||
- travis_retry sudo apt-get install -qq python3-pip qt5-default qt5-qmake qtbase5-dev qtcreator libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet python3-dev liblua5.3-dev libcurl3 libcairo2-dev libsigc++-2.0-dev tidy libopencv-dev libz3-dev
|
- travis_retry sudo apt-get install -qq python3-pip qt5-default qt5-qmake qtbase5-dev qtcreator qttools5-dev qttools5-dev-tools libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet python3-dev liblua5.3-dev libcurl3 libcairo2-dev libsigc++-2.0-dev tidy libopencv-dev libz3-dev
|
||||||
# Python 2 modules
|
# Python 2 modules
|
||||||
- travis_retry python2 -m pip install --user pytest==4.6.4
|
- travis_retry python2 -m pip install --user pytest==4.6.4
|
||||||
- travis_retry python2 -m pip install --user pylint
|
- travis_retry python2 -m pip install --user pylint
|
||||||
|
|
|
@ -1059,6 +1059,8 @@ class MisraChecker:
|
||||||
|
|
||||||
self.severity = None
|
self.severity = None
|
||||||
|
|
||||||
|
self.existing_violations = set()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
||||||
"ruleTexts", "suppressedRules", "dumpfileSuppressions",
|
"ruleTexts", "suppressedRules", "dumpfileSuppressions",
|
||||||
|
@ -2726,11 +2728,17 @@ class MisraChecker:
|
||||||
if self.severity:
|
if self.severity:
|
||||||
cppcheck_severity = self.severity
|
cppcheck_severity = self.severity
|
||||||
|
|
||||||
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity)
|
this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum)
|
||||||
|
|
||||||
if misra_severity not in self.violations:
|
# If this is new violation then record it and show it. If not then
|
||||||
self.violations[misra_severity] = []
|
# skip it since it has already been displayed.
|
||||||
self.violations[misra_severity].append('misra-' + errorId)
|
if not this_violation in self.existing_violations:
|
||||||
|
self.existing_violations.add(this_violation)
|
||||||
|
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity)
|
||||||
|
|
||||||
|
if misra_severity not in self.violations:
|
||||||
|
self.violations[misra_severity] = []
|
||||||
|
self.violations[misra_severity].append('misra-' + errorId)
|
||||||
|
|
||||||
def loadRuleTexts(self, filename):
|
def loadRuleTexts(self, filename):
|
||||||
num1 = 0
|
num1 = 0
|
||||||
|
|
72
build.bat
72
build.bat
|
@ -1,72 +0,0 @@
|
||||||
@echo off
|
|
||||||
REM A simple script to build different cppcheck targets from project root
|
|
||||||
REM folder. This script can be run from VS prompt or Qt prompt.
|
|
||||||
REM
|
|
||||||
REM Usage: build <target> [release|debug]
|
|
||||||
REM where <target> is any of cppcheck/gui/tests/all
|
|
||||||
REM release or debug is the configuration
|
|
||||||
REM all-target builds both cppcheck and gui.
|
|
||||||
REM
|
|
||||||
REM Run the command before build.bat to enable rules using pcre:
|
|
||||||
REM set HAVE_RULES=yes
|
|
||||||
REM
|
|
||||||
REM TODO:
|
|
||||||
REM - run tests too
|
|
||||||
|
|
||||||
pushd %~dp0
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
REM Qt prompt sets QMAKESPEC
|
|
||||||
if "%QMAKESPEC%" == "" (
|
|
||||||
REM parse qmakespec to see if it's some msvc
|
|
||||||
if "%QMAKESPEC:~6,4%" == "msvc" (
|
|
||||||
set MAKE=nmake
|
|
||||||
) else (
|
|
||||||
set MAKE=mingw32-make
|
|
||||||
)
|
|
||||||
) else (
|
|
||||||
set MAKE=nmake
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%2" == "" set TARGET=release
|
|
||||||
if "%2" == "debug" set TARGET=debug
|
|
||||||
if "%2" == "release" set TARGET=release
|
|
||||||
|
|
||||||
if "%1" == "all" goto cppcheck
|
|
||||||
if "%1" == "cppcheck" goto cppcheck
|
|
||||||
if "%1" == "gui" goto gui
|
|
||||||
if "%1" == "tests" goto tests
|
|
||||||
goto help
|
|
||||||
|
|
||||||
:cppcheck
|
|
||||||
pushd cli
|
|
||||||
qmake -config %TARGET% HAVE_RULES=%HAVE_RULES%
|
|
||||||
%MAKE%
|
|
||||||
popd
|
|
||||||
if "%1" == "all" goto gui
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:gui
|
|
||||||
pushd gui
|
|
||||||
qmake -config %TARGET% HAVE_RULES=%HAVE_RULES%
|
|
||||||
%MAKE%
|
|
||||||
lupdate -no-obsolete gui.pro
|
|
||||||
lrelease gui.pro
|
|
||||||
popd
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:tests
|
|
||||||
pushd test
|
|
||||||
qmake -config %TARGET% HAVE_RULES=%HAVE_RULES%
|
|
||||||
%MAKE%
|
|
||||||
popd
|
|
||||||
goto end
|
|
||||||
|
|
||||||
:help
|
|
||||||
echo Syntax: build ^<target^> [debug^|release]
|
|
||||||
echo where ^<target^> is any of cppcheck/gui/tests/all
|
|
||||||
echo debug or release define used configuration
|
|
||||||
echo all- target builds both cppcheck and gui.
|
|
||||||
|
|
||||||
:end
|
|
|
@ -13428,6 +13428,13 @@ wxItemKind kind = wxITEM_NORMAL) -->
|
||||||
<not-null/>
|
<not-null/>
|
||||||
</arg>
|
</arg>
|
||||||
</function>
|
</function>
|
||||||
|
<!-- virtual void wxGridCellEnumRenderer::SetParameters (const wxString ¶ms)-->
|
||||||
|
<function name="wxGridCellEnumRenderer::SetParameters">
|
||||||
|
<noreturn>false</noreturn>
|
||||||
|
<returnValue type="void"/>
|
||||||
|
<leak-ignore/>
|
||||||
|
<arg nr="1" direction="in"/>
|
||||||
|
</function>
|
||||||
<!-- bool wxGrid::SetTable(wxGridTableBase * table, bool takeOwnership = false, wxGridSelectionModes selmode = wxGridSelectCells) -->
|
<!-- bool wxGrid::SetTable(wxGridTableBase * table, bool takeOwnership = false, wxGridSelectionModes selmode = wxGridSelectCells) -->
|
||||||
<function name="wxGrid::SetTable">
|
<function name="wxGrid::SetTable">
|
||||||
<noreturn>false</noreturn>
|
<noreturn>false</noreturn>
|
||||||
|
|
|
@ -24,6 +24,13 @@
|
||||||
</data>
|
</data>
|
||||||
</attribute>
|
</attribute>
|
||||||
</optional>
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<attribute name="hash">
|
||||||
|
<data type="integer">
|
||||||
|
<param name="minExclusive">1</param>
|
||||||
|
</data>
|
||||||
|
</attribute>
|
||||||
|
</optional>
|
||||||
<attribute name="id">
|
<attribute name="id">
|
||||||
<data type="NCName"/>
|
<data type="NCName"/>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
|
|
@ -257,6 +257,19 @@ void CodeEditor::setError(const QString &code, int errorLine, const QStringList
|
||||||
highlightErrorLine();
|
highlightErrorLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CodeEditor::setError(int errorLine, const QStringList &symbols)
|
||||||
|
{
|
||||||
|
mHighlighter->setSymbols(symbols);
|
||||||
|
|
||||||
|
mErrorPosition = getPos(toPlainText(), errorLine);
|
||||||
|
QTextCursor tc = textCursor();
|
||||||
|
tc.setPosition(mErrorPosition);
|
||||||
|
setTextCursor(tc);
|
||||||
|
centerCursor();
|
||||||
|
|
||||||
|
highlightErrorLine();
|
||||||
|
}
|
||||||
|
|
||||||
int CodeEditor::lineNumberAreaWidth()
|
int CodeEditor::lineNumberAreaWidth()
|
||||||
{
|
{
|
||||||
int digits = 1;
|
int digits = 1;
|
||||||
|
|
|
@ -77,6 +77,26 @@ public:
|
||||||
*/
|
*/
|
||||||
void setError(const QString &code, int errorLine, const QStringList &symbols);
|
void setError(const QString &code, int errorLine, const QStringList &symbols);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goto another error in existing source file
|
||||||
|
* \param errorLine line number
|
||||||
|
* \param symbols the related symbols, these are marked
|
||||||
|
*/
|
||||||
|
void setError(int errorLine, const QStringList &symbols);
|
||||||
|
|
||||||
|
void setFileName(const QString &fileName) {
|
||||||
|
mFileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getFileName() const {
|
||||||
|
return mFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
mFileName.clear();
|
||||||
|
setPlainText(QString());
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
|
@ -93,6 +113,7 @@ private:
|
||||||
Highlighter *mHighlighter;
|
Highlighter *mHighlighter;
|
||||||
CodeEditorStyle *mWidgetStyle;
|
CodeEditorStyle *mWidgetStyle;
|
||||||
int mErrorPosition;
|
int mErrorPosition;
|
||||||
|
QString mFileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ ErrorItem::ErrorItem()
|
||||||
, incomplete(false)
|
, incomplete(false)
|
||||||
, inconclusive(false)
|
, inconclusive(false)
|
||||||
, cwe(-1)
|
, cwe(-1)
|
||||||
|
, hash(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ ErrorItem::ErrorItem(const ErrorMessage &errmsg)
|
||||||
, summary(QString::fromStdString(errmsg.shortMessage()))
|
, summary(QString::fromStdString(errmsg.shortMessage()))
|
||||||
, message(QString::fromStdString(errmsg.verboseMessage()))
|
, message(QString::fromStdString(errmsg.verboseMessage()))
|
||||||
, cwe(errmsg.cwe.id)
|
, cwe(errmsg.cwe.id)
|
||||||
|
, hash(errmsg.hash)
|
||||||
, symbolNames(QString::fromStdString(errmsg.symbolNames()))
|
, symbolNames(QString::fromStdString(errmsg.symbolNames()))
|
||||||
{
|
{
|
||||||
for (std::list<ErrorMessage::FileLocation>::const_iterator loc = errmsg.callStack.begin();
|
for (std::list<ErrorMessage::FileLocation>::const_iterator loc = errmsg.callStack.begin();
|
||||||
|
@ -70,7 +72,7 @@ QString ErrorItem::tool() const
|
||||||
return "cppcheck";
|
return "cppcheck";
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ErrorItem::ToString() const
|
QString ErrorItem::toString() const
|
||||||
{
|
{
|
||||||
QString str = errorPath.back().file + " - " + errorId + " - ";
|
QString str = errorPath.back().file + " - " + errorId + " - ";
|
||||||
if (inconclusive)
|
if (inconclusive)
|
||||||
|
@ -86,7 +88,10 @@ QString ErrorItem::ToString() const
|
||||||
|
|
||||||
bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2)
|
bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2)
|
||||||
{
|
{
|
||||||
// TODO: Implement some better CID calculation
|
if (errorItem1.hash || errorItem2.hash)
|
||||||
|
return errorItem1.hash == errorItem2.hash;
|
||||||
|
|
||||||
|
// fallback
|
||||||
return errorItem1.errorId == errorItem2.errorId &&
|
return errorItem1.errorId == errorItem2.errorId &&
|
||||||
errorItem1.errorPath == errorItem2.errorPath &&
|
errorItem1.errorPath == errorItem2.errorPath &&
|
||||||
errorItem1.file0 == errorItem2.file0 &&
|
errorItem1.file0 == errorItem2.file0 &&
|
||||||
|
|
|
@ -76,7 +76,7 @@ public:
|
||||||
* @brief Convert error item to string.
|
* @brief Convert error item to string.
|
||||||
* @return Error item as string.
|
* @return Error item as string.
|
||||||
*/
|
*/
|
||||||
QString ToString() const;
|
QString toString() const;
|
||||||
QString tool() const;
|
QString tool() const;
|
||||||
|
|
||||||
QString file0;
|
QString file0;
|
||||||
|
@ -88,6 +88,7 @@ public:
|
||||||
QString summary;
|
QString summary;
|
||||||
QString message;
|
QString message;
|
||||||
int cwe;
|
int cwe;
|
||||||
|
unsigned long long hash;
|
||||||
QList<QErrorPathItem> errorPath;
|
QList<QErrorPathItem> errorPath;
|
||||||
QString symbolNames;
|
QString symbolNames;
|
||||||
|
|
||||||
|
@ -114,6 +115,7 @@ public:
|
||||||
QString errorId;
|
QString errorId;
|
||||||
bool incomplete;
|
bool incomplete;
|
||||||
int cwe;
|
int cwe;
|
||||||
|
unsigned long long hash;
|
||||||
bool inconclusive;
|
bool inconclusive;
|
||||||
Severity::SeverityType severity;
|
Severity::SeverityType severity;
|
||||||
QString summary;
|
QString summary;
|
||||||
|
|
10
gui/gui.pro
10
gui/gui.pro
|
@ -10,6 +10,13 @@ INCLUDEPATH += . \
|
||||||
../externals/z3/include
|
../externals/z3/include
|
||||||
QT += widgets
|
QT += widgets
|
||||||
QT += printsupport
|
QT += printsupport
|
||||||
|
QT += help
|
||||||
|
|
||||||
|
# Build online help
|
||||||
|
#onlinehelp.target = online-help.qhc
|
||||||
|
#onlinehelp.commands = qhelpgenerator $$PWD/help/online-help.qhp -o online-help.qch ; qhelpgenerator $$PWD/help/online-help.qhcp -o online-help.qhc
|
||||||
|
#QMAKE_EXTRA_TARGETS += onlinehelp
|
||||||
|
#PRE_TARGETDEPS += online-help.qhc
|
||||||
|
|
||||||
contains(LINKCORE, [yY][eE][sS]) {
|
contains(LINKCORE, [yY][eE][sS]) {
|
||||||
LIBS += -l../bin/cppcheck-core
|
LIBS += -l../bin/cppcheck-core
|
||||||
|
@ -61,6 +68,7 @@ FORMS = about.ui \
|
||||||
application.ui \
|
application.ui \
|
||||||
file.ui \
|
file.ui \
|
||||||
functioncontractdialog.ui \
|
functioncontractdialog.ui \
|
||||||
|
helpdialog.ui \
|
||||||
mainwindow.ui \
|
mainwindow.ui \
|
||||||
projectfiledialog.ui \
|
projectfiledialog.ui \
|
||||||
resultsview.ui \
|
resultsview.ui \
|
||||||
|
@ -110,6 +118,7 @@ HEADERS += aboutdialog.h \
|
||||||
filelist.h \
|
filelist.h \
|
||||||
fileviewdialog.h \
|
fileviewdialog.h \
|
||||||
functioncontractdialog.h \
|
functioncontractdialog.h \
|
||||||
|
helpdialog.h \
|
||||||
mainwindow.h \
|
mainwindow.h \
|
||||||
platforms.h \
|
platforms.h \
|
||||||
printablereport.h \
|
printablereport.h \
|
||||||
|
@ -150,6 +159,7 @@ SOURCES += aboutdialog.cpp \
|
||||||
filelist.cpp \
|
filelist.cpp \
|
||||||
fileviewdialog.cpp \
|
fileviewdialog.cpp \
|
||||||
functioncontractdialog.cpp \
|
functioncontractdialog.cpp \
|
||||||
|
helpdialog.cpp \
|
||||||
main.cpp \
|
main.cpp \
|
||||||
mainwindow.cpp\
|
mainwindow.cpp\
|
||||||
platforms.cpp \
|
platforms.cpp \
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
@echo off
|
|
||||||
|
|
||||||
pushd %~dp0
|
|
||||||
|
|
||||||
if exist online-help.qhc del online-help.qhc
|
|
||||||
if exist online-help.qch del online-help.qch
|
|
||||||
|
|
||||||
qcollectiongenerator online-help.qhcp -o online-help.qhc
|
|
||||||
|
|
||||||
popd
|
|
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,13 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Cppcheck GUI</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Cppcheck GUI</h1>
|
||||||
|
|
||||||
|
<p>With the Cppcheck GUI you can analyze your code.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Investigating warnings</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Investigating warnings</h1>
|
||||||
|
|
||||||
|
<p>When you have run the analysis it is time to look at the results.</p>
|
||||||
|
|
||||||
|
<p>If you click on a warning then the corresponding code will be shown in the
|
||||||
|
"Warning details" at the bottom.</p>
|
||||||
|
|
||||||
|
<p>You can right click warnings to get options. The difference of
|
||||||
|
"hiding" a warning and "suppressing" a warning is that
|
||||||
|
the suppression is permanent and hiding the warning is only temporary. When
|
||||||
|
suppressing warning(s), that is saved in the project file.
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -4,11 +4,33 @@
|
||||||
<virtualFolder>doc</virtualFolder>
|
<virtualFolder>doc</virtualFolder>
|
||||||
<filterSection>
|
<filterSection>
|
||||||
<toc>
|
<toc>
|
||||||
<section title="Manual" ref="manual.html"/>
|
<section title="Cppcheck GUI help" ref="index.html">
|
||||||
|
<section title="Investigating warnings" ref="./investigating-warnings.html"/>
|
||||||
|
<section title="Preferences" ref="./preferences.html"/>
|
||||||
|
<section title="Project file dialog" ref="./projectfiledialog.html"/>
|
||||||
|
<section title="Standalone analysis" ref="./standalone-analysis.html"/>
|
||||||
|
<section title="Tagging" ref="./tagging.html"/>
|
||||||
|
<section title="Quick walk through" ref="./walkthrough.html"/>
|
||||||
|
</section>
|
||||||
</toc>
|
</toc>
|
||||||
<keywords/>
|
<keywords>
|
||||||
|
<keyword name="Project" ref="./projectfiledialog.html"/>
|
||||||
|
<keyword name="Tag" ref="./tagging.html"/>
|
||||||
|
</keywords>
|
||||||
<files>
|
<files>
|
||||||
<file>manual.html</file>
|
<file>index.html</file>
|
||||||
|
<file>investigating-warnings.html</file>
|
||||||
|
<file>preferences.html</file>
|
||||||
|
<file>projectfiledialog.html</file>
|
||||||
|
<file>standalone-analysis.html</file>
|
||||||
|
<file>tagging.html</file>
|
||||||
|
<file>walkthrough.html</file>
|
||||||
|
<file>images/walkthrough-analysis.png</file>
|
||||||
|
<file>images/walkthrough-library.png</file>
|
||||||
|
<file>images/walkthrough-toolbar-severities.png</file>
|
||||||
|
<file>images/walkthrough-import-project.png</file>
|
||||||
|
<file>images/walkthrough-new-project.png</file>
|
||||||
|
<file>images/walkthrough-warning-menu.png</file>
|
||||||
</files>
|
</files>
|
||||||
</filterSection>
|
</filterSection>
|
||||||
</QtHelpProject>
|
</QtHelpProject>
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Preferences</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Preferences</h1>
|
||||||
|
|
||||||
|
<p><b>Number of threads</b><br>Number of threads to use in analysis. Each
|
||||||
|
thread checks its own source file.</p>
|
||||||
|
|
||||||
|
<p><b>Force checking of all #ifdef configurations</b><br>Cppcheck try to check
|
||||||
|
all code and will therefore guess different preprocessor configurations. The
|
||||||
|
maximum number of configurations that is checked is 14 by default.</p>
|
||||||
|
|
||||||
|
<p><b>Show full path of files</b><br>Show the full paths in the results.</p>
|
||||||
|
|
||||||
|
<p><b>Show "No errors found" message when no errors found</b><br>
|
||||||
|
If you want to get a message box about this.</p>
|
||||||
|
|
||||||
|
<p><b>Display error id column "Id"</b><br>Show error id in results</p>
|
||||||
|
|
||||||
|
<p><b>Enable inline suppressions</b><br>You can suppress warnings with comments.
|
||||||
|
See the Cppcheck manual (http://cppcheck.sf.net/manual.pdf) for more information
|
||||||
|
about inline suppressions.</p>
|
||||||
|
|
||||||
|
<p><b>Check for inconclusive errors also</b><br>When full analysis of the code
|
||||||
|
can not determine if there should be a warning or not, it is inconclusive.
|
||||||
|
Normally Cppcheck does not warn then.</p>
|
||||||
|
|
||||||
|
<p><b>Show statistics on check completion</b><br>Show statistics in a window
|
||||||
|
when analysis finish.</p>
|
||||||
|
|
||||||
|
<p><b>Show internal warnings in log</b><br>Internal warnings (for debugging) is
|
||||||
|
shown in the <i>Analysis log</i>.</p>
|
||||||
|
|
||||||
|
<p><b>Applications</b><br>Configure external editor to open from context menu
|
||||||
|
when you right click on a warning.</p>
|
||||||
|
|
||||||
|
<p><b>Save all errors when creating report</b><br>If hidden warnings should be
|
||||||
|
saved or not.</p>
|
||||||
|
|
||||||
|
<p><b>Save full path to files in report</b><br>If you use <i>Root path</i> the
|
||||||
|
warnings on the screen will not have the full path.</p>
|
||||||
|
|
||||||
|
<p><b>Language</b><br>Configure language to use for GUI.</p>
|
||||||
|
|
||||||
|
<p><b>Python binary</b><br>To be able to execute addons, Cppcheck needs to know
|
||||||
|
where python is. Unless you configure something, Cppcheck will try to execute
|
||||||
|
python in your PATH.</p>
|
||||||
|
|
||||||
|
<p><b>Misra rule texts</b><br>Only needed if you want to use the Misra addon.
|
||||||
|
Cppcheck is not legally allowed to distribute the Misra rule texts and these
|
||||||
|
must be provided by users. The Misra rule texts are proprietary. An example
|
||||||
|
rule text file:
|
||||||
|
<blockquote><pre>Appendix A Summary of guidelines
|
||||||
|
Rule 1.1
|
||||||
|
Text of rule 1.1
|
||||||
|
Rule 1.2
|
||||||
|
Text of rule 1.2</pre></blockquote>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><b>Clang path</b><br>The path to <i>clang</i> binary. If no path is provided
|
||||||
|
then system PATH is used.</p>
|
||||||
|
|
||||||
|
<p><b>Visual studio headers</b><br>If you want to use the Visual Studio headers
|
||||||
|
in the analysis you can provide the path(s) here. Hint: Open a visual studio
|
||||||
|
command prompt and type <i>SET INCLUDE</i>. Then copy/paste the paths.</p>
|
||||||
|
|
||||||
|
<p><b>Code editor style</b><br>The visual theme to use for the code editor that
|
||||||
|
is used when you investigate results.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,181 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Project File Dialog</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Project File Dialog</h1>
|
||||||
|
|
||||||
|
<p>The <i>Project file</i> dialog contains 4 tabs:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Paths and defines; paths to check and basic preprocessor settings.
|
||||||
|
<li>Types and Functions; configuration of platform and 3rd party libraries
|
||||||
|
<li>Analysis; analysis options
|
||||||
|
<li>Warning options; formatting warnings, suppressing warnings, etc
|
||||||
|
<li>Addons; extra analysis with addons
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Paths and defines</h2>
|
||||||
|
|
||||||
|
<p>It is recommended to import a project file.</p>
|
||||||
|
|
||||||
|
<h3> Import project</h3>
|
||||||
|
|
||||||
|
Project to import. Cppcheck will get:
|
||||||
|
<ul>
|
||||||
|
<li>What files to check
|
||||||
|
<li>Preprocessor defines
|
||||||
|
<li>Preprocessor include paths
|
||||||
|
<li>Language standard if set
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Paths (If you do not import project)</h3>
|
||||||
|
|
||||||
|
<p>What paths to check.</p>
|
||||||
|
|
||||||
|
<h3>Defines (If you do not import project)</h3>
|
||||||
|
|
||||||
|
<p>Cppcheck automatically checks the code with different preprocessor
|
||||||
|
configurations.</p>
|
||||||
|
|
||||||
|
<blockquote><pre>#ifdef A
|
||||||
|
code1
|
||||||
|
#endif
|
||||||
|
#ifdef B
|
||||||
|
code2
|
||||||
|
#endif</pre></blockquote>
|
||||||
|
|
||||||
|
<p>Cppcheck will automatically perform analysis both when A is defined and B is
|
||||||
|
defined. So any bugs in both code1 and code2 will be detected.</p>
|
||||||
|
|
||||||
|
<p>If you want to configure that A will always be defined in Cppcheck analysis
|
||||||
|
you can do that here.</p>
|
||||||
|
|
||||||
|
<p>Defines are separated by semicolon. So you can for instance write:</p>
|
||||||
|
|
||||||
|
<blockquote><pre>A;B=3;C</pre></blockquote>
|
||||||
|
|
||||||
|
<h3>Undefines (If you do not import project)</h3>
|
||||||
|
|
||||||
|
<p>Cppcheck automatically checks the code with different preprocessor
|
||||||
|
configurations.</p>
|
||||||
|
|
||||||
|
<blockquote><pre>#ifdef A
|
||||||
|
code1
|
||||||
|
#endif
|
||||||
|
#ifdef B
|
||||||
|
code2
|
||||||
|
#endif</pre></blockquote>
|
||||||
|
|
||||||
|
<p>Cppcheck will automatically perform analysis both when A is defined and B is
|
||||||
|
defined. So any bugs in both code1 and code2 will be detected.</p>
|
||||||
|
|
||||||
|
<p>If you want to configure that A is never defined in Cppcheck analysis you
|
||||||
|
can do that here.</p>
|
||||||
|
|
||||||
|
<p>Undefines are separated by semicolon. So you can for instance write:</p>
|
||||||
|
|
||||||
|
<blockquote><pre>A;C</pre></blockquote>
|
||||||
|
|
||||||
|
<h3>Include paths (If you do not import project)</h3>
|
||||||
|
|
||||||
|
<p>Specify include paths.</p>
|
||||||
|
|
||||||
|
<h2>Types and Functions</h2>
|
||||||
|
|
||||||
|
<p>Cppcheck uses the <i>Platform</i> setting to determine size of
|
||||||
|
short/int/long/pointer/etc.</p>
|
||||||
|
|
||||||
|
<p>Check the libraries that you use in the <i>Libraries</i> listbox.</p>
|
||||||
|
|
||||||
|
<h2>Analysis</h2>
|
||||||
|
|
||||||
|
<h3>Cppcheck build dir</h3>
|
||||||
|
|
||||||
|
<p>This is a work-folder that Cppcheck uses. Each Cppcheck project should have
|
||||||
|
a separate build dir. It is used for:</p>
|
||||||
|
<ul>
|
||||||
|
<li>whole program analysis
|
||||||
|
<li>debug output
|
||||||
|
<li>faster analysis (if a source file has changed check it, if source file is
|
||||||
|
not changed then reuse old results)
|
||||||
|
<li>statistics
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Parser</h3>
|
||||||
|
|
||||||
|
<p>It is in general recommended to use Cppcheck parser. However you can choose
|
||||||
|
to use Clang parser; Clang will be executed with a command line flag that tells
|
||||||
|
it to dump its AST and Cppcheck will read that AST and convert it into a
|
||||||
|
corresponding Cppcheck AST and use that.</p>
|
||||||
|
|
||||||
|
<h3>Analysis</h3>
|
||||||
|
|
||||||
|
<p>Configure what kind of analysis you want.</p>
|
||||||
|
|
||||||
|
<p>The <i>Normal analysis</i> is recommended for most use cases. Especially if
|
||||||
|
you use Cppcheck in CI.</p>
|
||||||
|
|
||||||
|
<p>The <i>Bug hunting</i> can be used if you really want to find a bug in your
|
||||||
|
code and can invest time looking at bad results and providing extra
|
||||||
|
configuration.</p>
|
||||||
|
|
||||||
|
<h3>Limit analysis</h3>
|
||||||
|
|
||||||
|
<p>You can turn off checking of headers. That could be interesting if Cppcheck
|
||||||
|
is very slow. But normally, you should check the code in headers.</p>
|
||||||
|
|
||||||
|
<p>It is possible to check the code in unused templates. However the Cppcheck
|
||||||
|
AST will be incomplete/wrong. The recommendation is that you do not check
|
||||||
|
unused templates to avoid wrong warnings. The templates will be checked
|
||||||
|
properly when you do use them.</p>
|
||||||
|
|
||||||
|
<p>Max CTU depth: How deep should the whole program analysis be. The risk with
|
||||||
|
a "too high" value is that Cppcheck will be slow.</p>
|
||||||
|
|
||||||
|
<p>Max recursion in template instantiation: Max recursion when Cppcheck
|
||||||
|
instantiates templates. The risk with a "too high" value is that
|
||||||
|
Cppcheck will be slow and can require much memory.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Warning options</h2>
|
||||||
|
|
||||||
|
<h3>Root path</h3>
|
||||||
|
|
||||||
|
<p>The root path for warnings. Cppcheck will strip away this part of the path
|
||||||
|
from warnings. For instance if there is a warning in
|
||||||
|
<pre>../myproject/foo/bar/file.cpp</pre> and the root path is
|
||||||
|
<pre>../myproject/foo</pre> then the path for the warning will be
|
||||||
|
<pre>bar/file.cpp</pre>.</p>
|
||||||
|
|
||||||
|
<h3>Warning Tags</h3>
|
||||||
|
|
||||||
|
<p>Tags allow you to manually categorize warnings.</p>
|
||||||
|
|
||||||
|
<h3>Exclude source files</h3>
|
||||||
|
|
||||||
|
<p>Excluded source files will not be analyzed by Cppcheck.</p>
|
||||||
|
|
||||||
|
<h3>Suppressions</h3>
|
||||||
|
|
||||||
|
<p>List of suppressions. These warnings will not be shown.</p>
|
||||||
|
|
||||||
|
<h2>Addons</h2>
|
||||||
|
|
||||||
|
<p><b>Y2038</b><br>32-bit timers that count number of seconds since 1970 will
|
||||||
|
overflow in year 2038. Check that the code does not use such timers.</p>
|
||||||
|
|
||||||
|
<p><b>Thread safety</b><br>Check that the code is thread safe</p>
|
||||||
|
|
||||||
|
<p><b>Cert</b><br>Ensure that the Cert coding standard is followed</p>
|
||||||
|
|
||||||
|
<p><b>Misra</b><br>Ensure that the Misra coding standard is followed. Please
|
||||||
|
note you need to have a textfile with the misra rule texts to get proper
|
||||||
|
warning messages. Cppcheck is not legally allowed to distribute the misra
|
||||||
|
rule texts.</p>
|
||||||
|
|
||||||
|
<p><b>Clang-tidy</b><br>Run Clang-tidy</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Standalone analysis</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Standalone analysis</h1>
|
||||||
|
|
||||||
|
<p>It is possible to quickly analyze files. Open the <i>Analyze</i> menu and click on
|
||||||
|
either <i>Files...</i> or <i>Directory...</i>.</p>
|
||||||
|
|
||||||
|
<p>It is recommended that you create a project for analysis. A properly configured
|
||||||
|
project will give you better analysis.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Tagging warnings</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Tagging warnings</h1>
|
||||||
|
|
||||||
|
<p>You can manually categorize warnings.</p>
|
||||||
|
|
||||||
|
<p>You choose the names of the categories yourself in the project file
|
||||||
|
dialog.</p>
|
||||||
|
|
||||||
|
<p>If tag names are configured, then you can tag the warnings by
|
||||||
|
right-clicking on them and selecting the proper tag in the
|
||||||
|
context menu.</p>
|
||||||
|
|
||||||
|
<p>Tags are saved in the project file and will be permanent.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Walk through</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Quick walk through</h1>
|
||||||
|
|
||||||
|
This is a quick and short walk through to get you started.
|
||||||
|
|
||||||
|
<h2>Step 1: Create a project.</h2>
|
||||||
|
|
||||||
|
<p>Create a new project:</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-new-project.png"></blockquote>
|
||||||
|
|
||||||
|
<p>In the <i>Paths and Defines</i> tab, it is recommended that you import your
|
||||||
|
project file at the top.</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-import-project.png"></blockquote>
|
||||||
|
|
||||||
|
<p>In the <i>Types and Functions</i> tab, try to activate all 3rd party
|
||||||
|
libraries you use (windows, posix, ...).</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-library.png"></blockquote>
|
||||||
|
|
||||||
|
<p>In the <i>Analysis</i> tab, leave the default settings to start with.</p>
|
||||||
|
|
||||||
|
<p>In the <i>Warnings options</i> tab, leave the default settings to start
|
||||||
|
with.</p>
|
||||||
|
|
||||||
|
<p>In the <i>Addons</i> tab, leave the default settings to start with.</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Step 2: Analyze code.</h2>
|
||||||
|
|
||||||
|
<p>When the project file has been created, the analysis will start
|
||||||
|
automatically.</p>
|
||||||
|
|
||||||
|
<p>While analysis is performed in the background, you can investigate the
|
||||||
|
results.</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-analysis.png"></blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
<h2>Step 3: Investigate warnings.</h2>
|
||||||
|
|
||||||
|
<p>In the toolbar you choose what types of warnings you want to see
|
||||||
|
(error/warning/style/performance/portability/information).</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-toolbar-severities.png"></blockquote>
|
||||||
|
|
||||||
|
|
||||||
|
<p>All warnings are shown in a list. If you select a warning in the list, then
|
||||||
|
details about that warning is shown.</p>
|
||||||
|
|
||||||
|
<p>If you right click on warning(s) then you get a context menu.</p>
|
||||||
|
|
||||||
|
<blockquote><img src="images/walkthrough-warning-menu.png"></blockquote>
|
||||||
|
|
||||||
|
<p>The difference of "Hide" and "Suppress" is that
|
||||||
|
suppressions are saved in the project file. The suppressed warnings will not be
|
||||||
|
shown again unless you remove the suppression. When you hide a warning then
|
||||||
|
they will be temporarily hidden; the next time you analyze your code these
|
||||||
|
warnings will be shown again.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "helpdialog.h"
|
||||||
|
#include "ui_helpdialog.h"
|
||||||
|
|
||||||
|
#include <QHelpEngine>
|
||||||
|
#include <QHelpContentWidget>
|
||||||
|
#include <QHelpIndexWidget>
|
||||||
|
|
||||||
|
void HelpBrowser::setHelpEngine(QHelpEngine *helpEngine)
|
||||||
|
{
|
||||||
|
mHelpEngine = helpEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant HelpBrowser::loadResource(int type, const QUrl &name)
|
||||||
|
{
|
||||||
|
if (name.scheme() == "qthelp") {
|
||||||
|
QString url(name.toString());
|
||||||
|
while (url.indexOf("/./") > 0)
|
||||||
|
url.remove(url.indexOf("/./"), 2);
|
||||||
|
return QVariant(mHelpEngine->fileData(QUrl(url)));
|
||||||
|
}
|
||||||
|
return QTextBrowser::loadResource(type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
HelpDialog::HelpDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
mUi(new Ui::HelpDialog)
|
||||||
|
{
|
||||||
|
mUi->setupUi(this);
|
||||||
|
|
||||||
|
mHelpEngine = new QHelpEngine(QApplication::applicationDirPath() + "/online-help.qhc");
|
||||||
|
mHelpEngine->setupData();
|
||||||
|
|
||||||
|
mUi->contents->addWidget(mHelpEngine->contentWidget());
|
||||||
|
mUi->index->addWidget(mHelpEngine->indexWidget());
|
||||||
|
|
||||||
|
mUi->textBrowser->setHelpEngine(mHelpEngine);
|
||||||
|
|
||||||
|
mUi->textBrowser->setSource(QUrl("qthelp://cppcheck.sourceforge.net/doc/index.html"));
|
||||||
|
connect(mHelpEngine->contentWidget(),
|
||||||
|
SIGNAL(linkActivated(QUrl)),
|
||||||
|
mUi->textBrowser,
|
||||||
|
SLOT(setSource(QUrl)));
|
||||||
|
|
||||||
|
connect(mHelpEngine->indexWidget(),
|
||||||
|
SIGNAL(linkActivated(QUrl, QString)),
|
||||||
|
mUi->textBrowser,
|
||||||
|
SLOT(setSource(QUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
HelpDialog::~HelpDialog()
|
||||||
|
{
|
||||||
|
delete mUi;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef HELPDIALOG_H
|
||||||
|
#define HELPDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QTextBrowser>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class HelpDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class QHelpEngine;
|
||||||
|
|
||||||
|
class HelpBrowser : public QTextBrowser {
|
||||||
|
public:
|
||||||
|
HelpBrowser(QWidget* parent = 0) : QTextBrowser(parent), mHelpEngine(nullptr) {}
|
||||||
|
void setHelpEngine(QHelpEngine *helpEngine);
|
||||||
|
QVariant loadResource(int type, const QUrl& name);
|
||||||
|
private:
|
||||||
|
QHelpEngine* mHelpEngine;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HelpDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit HelpDialog(QWidget *parent = nullptr);
|
||||||
|
~HelpDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::HelpDialog *mUi;
|
||||||
|
QHelpEngine* mHelpEngine;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HELPDIALOG_H
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>HelpDialog</class>
|
||||||
|
<widget class="QDialog" name="HelpDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>635</width>
|
||||||
|
<height>446</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Cppcheck GUI help</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<widget class="QTabWidget" name="tabs">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>200</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tabContents">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Contents</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="contents"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tabIndex">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Index</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="index"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="HelpBrowser" name="textBrowser"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>HelpBrowser</class>
|
||||||
|
<extends>QTextBrowser</extends>
|
||||||
|
<header>helpdialog.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -34,20 +34,21 @@
|
||||||
#include "applicationlist.h"
|
#include "applicationlist.h"
|
||||||
#include "aboutdialog.h"
|
#include "aboutdialog.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "threadhandler.h"
|
#include "filelist.h"
|
||||||
#include "fileviewdialog.h"
|
#include "fileviewdialog.h"
|
||||||
#include "functioncontractdialog.h"
|
#include "functioncontractdialog.h"
|
||||||
|
#include "helpdialog.h"
|
||||||
|
#include "librarydialog.h"
|
||||||
#include "projectfile.h"
|
#include "projectfile.h"
|
||||||
#include "projectfiledialog.h"
|
#include "projectfiledialog.h"
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
#include "scratchpad.h"
|
#include "scratchpad.h"
|
||||||
|
#include "showtypes.h"
|
||||||
#include "statsdialog.h"
|
#include "statsdialog.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
|
#include "threadhandler.h"
|
||||||
#include "threadresult.h"
|
#include "threadresult.h"
|
||||||
#include "translationhandler.h"
|
#include "translationhandler.h"
|
||||||
#include "filelist.h"
|
|
||||||
#include "showtypes.h"
|
|
||||||
#include "librarydialog.h"
|
|
||||||
|
|
||||||
static const QString OnlineHelpURL("http://cppcheck.net/manual.html");
|
static const QString OnlineHelpURL("http://cppcheck.net/manual.html");
|
||||||
static const QString compile_commands_json("compile_commands.json");
|
static const QString compile_commands_json("compile_commands.json");
|
||||||
|
@ -141,7 +142,6 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
|
||||||
connect(mUI.mResults, &ResultsView::gotResults, this, &MainWindow::resultsAdded);
|
connect(mUI.mResults, &ResultsView::gotResults, this, &MainWindow::resultsAdded);
|
||||||
connect(mUI.mResults, &ResultsView::resultsHidden, mUI.mActionShowHidden, &QAction::setEnabled);
|
connect(mUI.mResults, &ResultsView::resultsHidden, mUI.mActionShowHidden, &QAction::setEnabled);
|
||||||
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::suppressIds, this, &MainWindow::suppressIds);
|
connect(mUI.mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds);
|
||||||
connect(mUI.mResults, &ResultsView::editFunctionContract, this, &MainWindow::editFunctionContract);
|
connect(mUI.mResults, &ResultsView::editFunctionContract, this, &MainWindow::editFunctionContract);
|
||||||
connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu);
|
connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu);
|
||||||
|
@ -341,6 +341,7 @@ void MainWindow::loadSettings()
|
||||||
if (inf.exists() && inf.isReadable()) {
|
if (inf.exists() && inf.isReadable()) {
|
||||||
setPath(SETTINGS_LAST_PROJECT_PATH, projectFile);
|
setPath(SETTINGS_LAST_PROJECT_PATH, projectFile);
|
||||||
mProjectFile = new ProjectFile(this);
|
mProjectFile = new ProjectFile(this);
|
||||||
|
mProjectFile->setActiveProject();
|
||||||
mProjectFile->read(projectFile);
|
mProjectFile->read(projectFile);
|
||||||
loadLastResults();
|
loadLastResults();
|
||||||
QDir::setCurrent(inf.absolutePath());
|
QDir::setCurrent(inf.absolutePath());
|
||||||
|
@ -1465,7 +1466,8 @@ void MainWindow::openHelpContents()
|
||||||
|
|
||||||
void MainWindow::openOnlineHelp()
|
void MainWindow::openOnlineHelp()
|
||||||
{
|
{
|
||||||
QDesktopServices::openUrl(QUrl(OnlineHelpURL));
|
HelpDialog *helpDialog = new HelpDialog;
|
||||||
|
helpDialog->showMaximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::openProjectFile()
|
void MainWindow::openProjectFile()
|
||||||
|
@ -1508,6 +1510,7 @@ void MainWindow::loadProjectFile(const QString &filePath)
|
||||||
mUI.mActionEditProjectFile->setEnabled(true);
|
mUI.mActionEditProjectFile->setEnabled(true);
|
||||||
delete mProjectFile;
|
delete mProjectFile;
|
||||||
mProjectFile = new ProjectFile(filePath, this);
|
mProjectFile = new ProjectFile(filePath, this);
|
||||||
|
mProjectFile->setActiveProject();
|
||||||
updateContractsTab();
|
updateContractsTab();
|
||||||
if (!loadLastResults())
|
if (!loadLastResults())
|
||||||
analyzeProject(mProjectFile);
|
analyzeProject(mProjectFile);
|
||||||
|
@ -1522,8 +1525,6 @@ QString MainWindow::getLastResults() const
|
||||||
|
|
||||||
bool MainWindow::loadLastResults()
|
bool MainWindow::loadLastResults()
|
||||||
{
|
{
|
||||||
if (mProjectFile)
|
|
||||||
mUI.mResults->setTags(mProjectFile->getTags());
|
|
||||||
const QString &lastResults = getLastResults();
|
const QString &lastResults = getLastResults();
|
||||||
if (lastResults.isEmpty())
|
if (lastResults.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
@ -1546,7 +1547,6 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const bool check
|
||||||
QDir::setCurrent(inf.absolutePath());
|
QDir::setCurrent(inf.absolutePath());
|
||||||
|
|
||||||
mThread->setAddonsAndTools(projectFile->getAddonsAndTools());
|
mThread->setAddonsAndTools(projectFile->getAddonsAndTools());
|
||||||
mUI.mResults->setTags(projectFile->getTags());
|
|
||||||
|
|
||||||
// If the root path is not given or is not "current dir", use project
|
// If the root path is not given or is not "current dir", use project
|
||||||
// file's location directory as root path
|
// file's location directory as root path
|
||||||
|
@ -1630,6 +1630,7 @@ void MainWindow::newProjectFile()
|
||||||
|
|
||||||
delete mProjectFile;
|
delete mProjectFile;
|
||||||
mProjectFile = new ProjectFile(this);
|
mProjectFile = new ProjectFile(this);
|
||||||
|
mProjectFile->setActiveProject();
|
||||||
mProjectFile->setFilename(filepath);
|
mProjectFile->setFilename(filepath);
|
||||||
mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir");
|
mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir");
|
||||||
|
|
||||||
|
@ -1649,7 +1650,6 @@ void MainWindow::closeProjectFile()
|
||||||
delete mProjectFile;
|
delete mProjectFile;
|
||||||
mProjectFile = nullptr;
|
mProjectFile = nullptr;
|
||||||
mUI.mResults->clear(true);
|
mUI.mResults->clear(true);
|
||||||
mUI.mResults->setTags(QStringList());
|
|
||||||
enableProjectActions(false);
|
enableProjectActions(false);
|
||||||
enableProjectOpenActions(true);
|
enableProjectOpenActions(true);
|
||||||
formatAndSetTitle();
|
formatAndSetTitle();
|
||||||
|
@ -1811,13 +1811,6 @@ void MainWindow::selectPlatform()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::tagged()
|
|
||||||
{
|
|
||||||
const QString &lastResults = getLastResults();
|
|
||||||
if (!lastResults.isEmpty())
|
|
||||||
mUI.mResults->save(lastResults, Report::XMLV2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::suppressIds(QStringList ids)
|
void MainWindow::suppressIds(QStringList ids)
|
||||||
{
|
{
|
||||||
if (!mProjectFile)
|
if (!mProjectFile)
|
||||||
|
|
|
@ -221,9 +221,6 @@ protected slots:
|
||||||
/** @brief Selects the platform as analyzed platform. */
|
/** @brief Selects the platform as analyzed platform. */
|
||||||
void selectPlatform();
|
void selectPlatform();
|
||||||
|
|
||||||
/** Some results were tagged */
|
|
||||||
void tagged();
|
|
||||||
|
|
||||||
/** Suppress error ids */
|
/** Suppress error ids */
|
||||||
void suppressIds(QStringList ids);
|
void suppressIds(QStringList ids);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
ProjectFile *ProjectFile::mActiveProject;
|
||||||
|
|
||||||
ProjectFile::ProjectFile(QObject *parent) :
|
ProjectFile::ProjectFile(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
|
@ -70,6 +72,8 @@ void ProjectFile::clear()
|
||||||
mCheckUnknownFunctionReturn.clear();
|
mCheckUnknownFunctionReturn.clear();
|
||||||
safeChecks.clear();
|
safeChecks.clear();
|
||||||
mVsConfigurations.clear();
|
mVsConfigurations.clear();
|
||||||
|
mTags.clear();
|
||||||
|
mWarningTags.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectFile::read(const QString &filename)
|
bool ProjectFile::read(const QString &filename)
|
||||||
|
@ -186,6 +190,9 @@ bool ProjectFile::read(const QString &filename)
|
||||||
if (xmlReader.name() == CppcheckXml::TagsElementName)
|
if (xmlReader.name() == CppcheckXml::TagsElementName)
|
||||||
readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
|
readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
|
||||||
|
|
||||||
|
if (xmlReader.name() == CppcheckXml::TagWarningsElementName)
|
||||||
|
readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString());
|
||||||
|
|
||||||
if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName)
|
if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName)
|
||||||
mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
|
mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
|
||||||
|
|
||||||
|
@ -603,6 +610,8 @@ void ProjectFile::readSuppressions(QXmlStreamReader &reader)
|
||||||
suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
|
suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
|
||||||
if (reader.attributes().hasAttribute(QString(),"symbolName"))
|
if (reader.attributes().hasAttribute(QString(),"symbolName"))
|
||||||
suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString();
|
suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString();
|
||||||
|
if (reader.attributes().hasAttribute(QString(),"hash"))
|
||||||
|
suppression.hash = reader.attributes().value(QString(),"hash").toULongLong();
|
||||||
type = reader.readNext();
|
type = reader.readNext();
|
||||||
if (type == QXmlStreamReader::Characters) {
|
if (type == QXmlStreamReader::Characters) {
|
||||||
suppression.errorId = reader.text().toString().toStdString();
|
suppression.errorId = reader.text().toString().toStdString();
|
||||||
|
@ -632,6 +641,41 @@ void ProjectFile::readSuppressions(QXmlStreamReader &reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ProjectFile::readTagWarnings(QXmlStreamReader &reader, const QString &tag)
|
||||||
|
{
|
||||||
|
QXmlStreamReader::TokenType type;
|
||||||
|
do {
|
||||||
|
type = reader.readNext();
|
||||||
|
switch (type) {
|
||||||
|
case QXmlStreamReader::StartElement:
|
||||||
|
// Read library-elements
|
||||||
|
if (reader.name().toString() == CppcheckXml::WarningElementName) {
|
||||||
|
std::size_t hash = reader.attributes().value(QString(), CppcheckXml::HashAttributeName).toULongLong();
|
||||||
|
mWarningTags[hash] = tag;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QXmlStreamReader::EndElement:
|
||||||
|
if (reader.name().toString() != CppcheckXml::WarningElementName)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[])
|
void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[])
|
||||||
{
|
{
|
||||||
QXmlStreamReader::TokenType type;
|
QXmlStreamReader::TokenType type;
|
||||||
|
@ -715,6 +759,11 @@ void ProjectFile::setSuppressions(const QList<Suppressions::Suppression> &suppre
|
||||||
mSuppressions = suppressions;
|
mSuppressions = suppressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectFile::addSuppression(const Suppressions::Suppression &suppression)
|
||||||
|
{
|
||||||
|
mSuppressions.append(suppression);
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectFile::setAddons(const QStringList &addons)
|
void ProjectFile::setAddons(const QStringList &addons)
|
||||||
{
|
{
|
||||||
mAddons = addons;
|
mAddons = addons;
|
||||||
|
@ -725,6 +774,20 @@ void ProjectFile::setVSConfigurations(const QStringList &vsConfigs)
|
||||||
mVsConfigurations = vsConfigs;
|
mVsConfigurations = vsConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectFile::setWarningTags(std::size_t hash, QString tag)
|
||||||
|
{
|
||||||
|
if (tag.isEmpty())
|
||||||
|
mWarningTags.erase(hash);
|
||||||
|
else if (hash > 0)
|
||||||
|
mWarningTags[hash] = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProjectFile::getWarningTags(std::size_t hash) const
|
||||||
|
{
|
||||||
|
auto it = mWarningTags.find(hash);
|
||||||
|
return (it != mWarningTags.end()) ? it->second : QString();
|
||||||
|
}
|
||||||
|
|
||||||
bool ProjectFile::write(const QString &filename)
|
bool ProjectFile::write(const QString &filename)
|
||||||
{
|
{
|
||||||
if (!filename.isEmpty())
|
if (!filename.isEmpty())
|
||||||
|
@ -873,6 +936,8 @@ bool ProjectFile::write(const QString &filename)
|
||||||
xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
|
xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
|
||||||
if (!suppression.symbolName.empty())
|
if (!suppression.symbolName.empty())
|
||||||
xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
|
xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
|
||||||
|
if (suppression.hash > 0)
|
||||||
|
xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash));
|
||||||
if (!suppression.errorId.empty())
|
if (!suppression.errorId.empty())
|
||||||
xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
|
xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
|
||||||
xmlWriter.writeEndElement();
|
xmlWriter.writeEndElement();
|
||||||
|
@ -903,6 +968,26 @@ bool ProjectFile::write(const QString &filename)
|
||||||
CppcheckXml::ToolElementName);
|
CppcheckXml::ToolElementName);
|
||||||
|
|
||||||
writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName);
|
writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName);
|
||||||
|
if (!mWarningTags.empty()) {
|
||||||
|
QStringList tags;
|
||||||
|
for (const auto wt: mWarningTags) {
|
||||||
|
if (!tags.contains(wt.second))
|
||||||
|
tags.append(wt.second);
|
||||||
|
}
|
||||||
|
for (const QString &tag: tags) {
|
||||||
|
xmlWriter.writeStartElement(CppcheckXml::TagWarningsElementName);
|
||||||
|
xmlWriter.writeAttribute(CppcheckXml::TagAttributeName, tag);
|
||||||
|
QStringList warnings;
|
||||||
|
for (const auto wt: mWarningTags) {
|
||||||
|
if (wt.second == tag) {
|
||||||
|
xmlWriter.writeStartElement(CppcheckXml::WarningElementName);
|
||||||
|
xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(wt.first));
|
||||||
|
xmlWriter.writeEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlWriter.writeEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
xmlWriter.writeEndDocument();
|
xmlWriter.writeEndDocument();
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -1021,4 +1106,3 @@ QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon)
|
||||||
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,16 @@ class ProjectFile : public QObject {
|
||||||
public:
|
public:
|
||||||
explicit ProjectFile(QObject *parent = nullptr);
|
explicit ProjectFile(QObject *parent = nullptr);
|
||||||
explicit ProjectFile(const QString &filename, QObject *parent = nullptr);
|
explicit ProjectFile(const QString &filename, QObject *parent = nullptr);
|
||||||
|
~ProjectFile() {
|
||||||
|
if (this == mActiveProject) mActiveProject = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProjectFile* getActiveProject() {
|
||||||
|
return mActiveProject;
|
||||||
|
}
|
||||||
|
void setActiveProject() {
|
||||||
|
mActiveProject = this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read the project file.
|
* @brief Read the project file.
|
||||||
|
@ -299,6 +309,9 @@ public:
|
||||||
*/
|
*/
|
||||||
void setSuppressions(const QList<Suppressions::Suppression> &suppressions);
|
void setSuppressions(const QList<Suppressions::Suppression> &suppressions);
|
||||||
|
|
||||||
|
/** Add suppression */
|
||||||
|
void addSuppression(const Suppressions::Suppression &suppression);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set list of addons.
|
* @brief Set list of addons.
|
||||||
* @param addons List of addons.
|
* @param addons List of addons.
|
||||||
|
@ -318,6 +331,12 @@ public:
|
||||||
mTags = tags;
|
mTags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set tags for a warning */
|
||||||
|
void setWarningTags(std::size_t hash, QString tags);
|
||||||
|
|
||||||
|
/** Get tags for a warning */
|
||||||
|
QString getWarningTags(std::size_t hash) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write project file (to disk).
|
* @brief Write project file (to disk).
|
||||||
* @param filename Filename to use.
|
* @param filename Filename to use.
|
||||||
|
@ -424,6 +443,12 @@ protected:
|
||||||
*/
|
*/
|
||||||
void readSuppressions(QXmlStreamReader &reader);
|
void readSuppressions(QXmlStreamReader &reader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read tag warnings, what warnings are tagged with a specific tag
|
||||||
|
* @param reader XML stream reader.
|
||||||
|
*/
|
||||||
|
void readTagWarnings(QXmlStreamReader &reader, const QString &tag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read string list
|
* @brief Read string list
|
||||||
* @param stringlist destination string list
|
* @param stringlist destination string list
|
||||||
|
@ -539,10 +564,15 @@ private:
|
||||||
bool mClangTidy;
|
bool mClangTidy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Warning tags
|
* @brief Tags
|
||||||
*/
|
*/
|
||||||
QStringList mTags;
|
QStringList mTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Warning tags
|
||||||
|
*/
|
||||||
|
std::map<std::size_t, QString> mWarningTags;
|
||||||
|
|
||||||
/** Max CTU depth */
|
/** Max CTU depth */
|
||||||
int mMaxCtuDepth;
|
int mMaxCtuDepth;
|
||||||
|
|
||||||
|
@ -551,6 +581,7 @@ private:
|
||||||
|
|
||||||
QStringList mCheckUnknownFunctionReturn;
|
QStringList mCheckUnknownFunctionReturn;
|
||||||
|
|
||||||
|
static ProjectFile *mActiveProject;
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
#endif // PROJECT_FILE_H
|
#endif // PROJECT_FILE_H
|
||||||
|
|
|
@ -62,6 +62,8 @@ static const int numberOfBuiltinPlatforms = sizeof(builtinPlatforms) / sizeof(bu
|
||||||
|
|
||||||
QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName)
|
QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName)
|
||||||
{
|
{
|
||||||
|
if (!fileName.endsWith(".sln") && !fileName.endsWith(".vcxproj"))
|
||||||
|
return QStringList();
|
||||||
QStringList ret;
|
QStringList ret;
|
||||||
ImportProject importer;
|
ImportProject importer;
|
||||||
Settings projSettings;
|
Settings projSettings;
|
||||||
|
@ -199,6 +201,7 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent)
|
||||||
connect(mUI.mBtnEditInclude, &QPushButton::clicked, this, &ProjectFileDialog::editIncludeDir);
|
connect(mUI.mBtnEditInclude, &QPushButton::clicked, this, &ProjectFileDialog::editIncludeDir);
|
||||||
connect(mUI.mBtnRemoveInclude, &QPushButton::clicked, this, &ProjectFileDialog::removeIncludeDir);
|
connect(mUI.mBtnRemoveInclude, &QPushButton::clicked, this, &ProjectFileDialog::removeIncludeDir);
|
||||||
connect(mUI.mBtnAddIgnorePath, SIGNAL(clicked()), this, SLOT(addExcludePath()));
|
connect(mUI.mBtnAddIgnorePath, SIGNAL(clicked()), this, SLOT(addExcludePath()));
|
||||||
|
connect(mUI.mBtnAddIgnoreFile, SIGNAL(clicked()), this, SLOT(addExcludeFile()));
|
||||||
connect(mUI.mBtnEditIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::editExcludePath);
|
connect(mUI.mBtnEditIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::editExcludePath);
|
||||||
connect(mUI.mBtnRemoveIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::removeExcludePath);
|
connect(mUI.mBtnRemoveIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::removeExcludePath);
|
||||||
connect(mUI.mBtnIncludeUp, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathUp);
|
connect(mUI.mBtnIncludeUp, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathUp);
|
||||||
|
@ -208,6 +211,7 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent)
|
||||||
connect(mUI.mListSuppressions, &QListWidget::doubleClicked, this, &ProjectFileDialog::editSuppression);
|
connect(mUI.mListSuppressions, &QListWidget::doubleClicked, this, &ProjectFileDialog::editSuppression);
|
||||||
connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile);
|
connect(mUI.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile);
|
||||||
connect(mUI.mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs);
|
connect(mUI.mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs);
|
||||||
|
connect(mUI.mBtnNormalAnalysis, &QCheckBox::toggled, mUI.mBtnSafeClasses, &QCheckBox::setEnabled);
|
||||||
loadFromProjectFile(projectFile);
|
loadFromProjectFile(projectFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,8 +246,12 @@ static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, c
|
||||||
|
|
||||||
void ProjectFileDialog::checkAllVSConfigs()
|
void ProjectFileDialog::checkAllVSConfigs()
|
||||||
{
|
{
|
||||||
if (mUI.mChkAllVsConfigs->isChecked())
|
if (mUI.mChkAllVsConfigs->isChecked()) {
|
||||||
mUI.mListVsConfigs->selectAll();
|
for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) {
|
||||||
|
QListWidgetItem *item = mUI.mListVsConfigs->item(row);
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
mUI.mListVsConfigs->setEnabled(!mUI.mChkAllVsConfigs->isChecked());
|
mUI.mListVsConfigs->setEnabled(!mUI.mChkAllVsConfigs->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,6 +265,14 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
||||||
setCheckPaths(projectFile->getCheckPaths());
|
setCheckPaths(projectFile->getCheckPaths());
|
||||||
setImportProject(projectFile->getImportProject());
|
setImportProject(projectFile->getImportProject());
|
||||||
mUI.mChkAllVsConfigs->setChecked(projectFile->getAnalyzeAllVsConfigs());
|
mUI.mChkAllVsConfigs->setChecked(projectFile->getAnalyzeAllVsConfigs());
|
||||||
|
setProjectConfigurations(getProjectConfigs(mUI.mEditImportProject->text()));
|
||||||
|
for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) {
|
||||||
|
QListWidgetItem *item = mUI.mListVsConfigs->item(row);
|
||||||
|
if (projectFile->getAnalyzeAllVsConfigs() || projectFile->getVsConfigurations().contains(item->text()))
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
else
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
mUI.mCheckHeaders->setChecked(projectFile->getCheckHeaders());
|
mUI.mCheckHeaders->setChecked(projectFile->getCheckHeaders());
|
||||||
mUI.mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates());
|
mUI.mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates());
|
||||||
mUI.mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth());
|
mUI.mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth());
|
||||||
|
@ -266,7 +282,7 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
||||||
else
|
else
|
||||||
mUI.mBtnCppcheckParser->setChecked(true);
|
mUI.mBtnCppcheckParser->setChecked(true);
|
||||||
mUI.mBtnSafeClasses->setChecked(projectFile->safeChecks.classes);
|
mUI.mBtnSafeClasses->setChecked(projectFile->safeChecks.classes);
|
||||||
mUI.mBugHunting->setChecked(projectFile->bugHunting);
|
mUI.mBtnBugHunting->setChecked(projectFile->bugHunting);
|
||||||
setExcludedPaths(projectFile->getExcludedPaths());
|
setExcludedPaths(projectFile->getExcludedPaths());
|
||||||
setLibraries(projectFile->getLibraries());
|
setLibraries(projectFile->getLibraries());
|
||||||
const QString platform = projectFile->getPlatform();
|
const QString platform = projectFile->getPlatform();
|
||||||
|
@ -340,17 +356,6 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
||||||
}
|
}
|
||||||
mUI.mEditTags->setText(projectFile->getTags().join(';'));
|
mUI.mEditTags->setText(projectFile->getTags().join(';'));
|
||||||
updatePathsAndDefines();
|
updatePathsAndDefines();
|
||||||
if (mUI.mEditImportProject->text().endsWith(".sln") || mUI.mEditImportProject->text().endsWith(".vcxproj")) {
|
|
||||||
setVsConfigurations(getProjectConfigs(mUI.mEditImportProject->text()));
|
|
||||||
foreach (const QString &cfg, projectFile->getVsConfigurations()) {
|
|
||||||
QList<QListWidgetItem*> items = mUI.mListVsConfigs->findItems(cfg, Qt::MatchFlag::MatchExactly);
|
|
||||||
items[0]->setSelected(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mUI.mListVsConfigs->clear();
|
|
||||||
mUI.mListVsConfigs->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
||||||
|
@ -359,6 +364,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
||||||
projectFile->setBuildDir(getBuildDir());
|
projectFile->setBuildDir(getBuildDir());
|
||||||
projectFile->setImportProject(getImportProject());
|
projectFile->setImportProject(getImportProject());
|
||||||
projectFile->setAnalyzeAllVsConfigs(mUI.mChkAllVsConfigs->isChecked());
|
projectFile->setAnalyzeAllVsConfigs(mUI.mChkAllVsConfigs->isChecked());
|
||||||
|
projectFile->setVSConfigurations(getProjectConfigurations());
|
||||||
projectFile->setCheckHeaders(mUI.mCheckHeaders->isChecked());
|
projectFile->setCheckHeaders(mUI.mCheckHeaders->isChecked());
|
||||||
projectFile->setCheckUnusedTemplates(mUI.mCheckUnusedTemplates->isChecked());
|
projectFile->setCheckUnusedTemplates(mUI.mCheckUnusedTemplates->isChecked());
|
||||||
projectFile->setMaxCtuDepth(mUI.mMaxCtuDepth->value());
|
projectFile->setMaxCtuDepth(mUI.mMaxCtuDepth->value());
|
||||||
|
@ -371,7 +377,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
||||||
projectFile->setLibraries(getLibraries());
|
projectFile->setLibraries(getLibraries());
|
||||||
projectFile->clangParser = mUI.mBtnClangParser->isChecked();
|
projectFile->clangParser = mUI.mBtnClangParser->isChecked();
|
||||||
projectFile->safeChecks.classes = mUI.mBtnSafeClasses->isChecked();
|
projectFile->safeChecks.classes = mUI.mBtnSafeClasses->isChecked();
|
||||||
projectFile->bugHunting = mUI.mBugHunting->isChecked();
|
projectFile->bugHunting = mUI.mBtnBugHunting->isChecked();
|
||||||
if (mUI.mComboBoxPlatform->currentText().endsWith(".xml"))
|
if (mUI.mComboBoxPlatform->currentText().endsWith(".xml"))
|
||||||
projectFile->setPlatform(mUI.mComboBoxPlatform->currentText());
|
projectFile->setPlatform(mUI.mComboBoxPlatform->currentText());
|
||||||
else {
|
else {
|
||||||
|
@ -412,7 +418,6 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
||||||
projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked());
|
projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked());
|
||||||
projectFile->setClangTidy(mUI.mToolClangTidy->isChecked());
|
projectFile->setClangTidy(mUI.mToolClangTidy->isChecked());
|
||||||
projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts));
|
projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts));
|
||||||
projectFile->setVSConfigurations(getVsConfigurations());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFileDialog::ok()
|
void ProjectFileDialog::ok()
|
||||||
|
@ -458,6 +463,7 @@ void ProjectFileDialog::updatePathsAndDefines()
|
||||||
{
|
{
|
||||||
const QString &fileName = mUI.mEditImportProject->text();
|
const QString &fileName = mUI.mEditImportProject->text();
|
||||||
bool importProject = !fileName.isEmpty();
|
bool importProject = !fileName.isEmpty();
|
||||||
|
bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".vcxproj");
|
||||||
mUI.mBtnClearImportProject->setEnabled(importProject);
|
mUI.mBtnClearImportProject->setEnabled(importProject);
|
||||||
mUI.mListCheckPaths->setEnabled(!importProject);
|
mUI.mListCheckPaths->setEnabled(!importProject);
|
||||||
mUI.mListIncludeDirs->setEnabled(!importProject);
|
mUI.mListIncludeDirs->setEnabled(!importProject);
|
||||||
|
@ -471,9 +477,9 @@ void ProjectFileDialog::updatePathsAndDefines()
|
||||||
mUI.mBtnRemoveInclude->setEnabled(!importProject);
|
mUI.mBtnRemoveInclude->setEnabled(!importProject);
|
||||||
mUI.mBtnIncludeUp->setEnabled(!importProject);
|
mUI.mBtnIncludeUp->setEnabled(!importProject);
|
||||||
mUI.mBtnIncludeDown->setEnabled(!importProject);
|
mUI.mBtnIncludeDown->setEnabled(!importProject);
|
||||||
mUI.mChkAllVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"));
|
mUI.mChkAllVsConfigs->setEnabled(hasConfigs);
|
||||||
mUI.mListVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"));
|
mUI.mListVsConfigs->setEnabled(hasConfigs && !mUI.mChkAllVsConfigs->isChecked());
|
||||||
if (!mUI.mListVsConfigs->isEnabled())
|
if (!hasConfigs)
|
||||||
mUI.mListVsConfigs->clear();
|
mUI.mListVsConfigs->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,24 +503,34 @@ void ProjectFileDialog::browseImportProject()
|
||||||
if (!fileName.isEmpty()) {
|
if (!fileName.isEmpty()) {
|
||||||
mUI.mEditImportProject->setText(dir.relativeFilePath(fileName));
|
mUI.mEditImportProject->setText(dir.relativeFilePath(fileName));
|
||||||
updatePathsAndDefines();
|
updatePathsAndDefines();
|
||||||
setVsConfigurations(getProjectConfigs(fileName));
|
setProjectConfigurations(getProjectConfigs(fileName));
|
||||||
mUI.mListVsConfigs->selectAll();
|
for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) {
|
||||||
|
QListWidgetItem *item = mUI.mListVsConfigs->item(row);
|
||||||
|
item->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ProjectFileDialog::getVsConfigurations() const
|
QStringList ProjectFileDialog::getProjectConfigurations() const
|
||||||
{
|
{
|
||||||
QStringList configs;
|
QStringList configs;
|
||||||
foreach (QListWidgetItem *item, mUI.mListVsConfigs->selectedItems())
|
for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) {
|
||||||
configs << item->text();
|
QListWidgetItem *item = mUI.mListVsConfigs->item(row);
|
||||||
|
if (item->checkState() == Qt::Checked)
|
||||||
|
configs << item->text();
|
||||||
|
}
|
||||||
return configs;
|
return configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFileDialog::setVsConfigurations(const QStringList &configs)
|
void ProjectFileDialog::setProjectConfigurations(const QStringList &configs)
|
||||||
{
|
{
|
||||||
mUI.mListVsConfigs->clear();
|
mUI.mListVsConfigs->clear();
|
||||||
mUI.mListVsConfigs->addItems(configs);
|
mUI.mListVsConfigs->setEnabled(!configs.isEmpty() && !mUI.mChkAllVsConfigs->isChecked());
|
||||||
|
foreach (const QString &cfg, configs) {
|
||||||
|
QListWidgetItem* item = new QListWidgetItem(cfg, mUI.mListVsConfigs);
|
||||||
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
|
||||||
|
item->setCheckState(Qt::Unchecked);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ProjectFileDialog::getImportProject() const
|
QString ProjectFileDialog::getImportProject() const
|
||||||
|
@ -717,9 +733,17 @@ void ProjectFileDialog::editIncludeDir()
|
||||||
|
|
||||||
void ProjectFileDialog::addExcludePath()
|
void ProjectFileDialog::addExcludePath()
|
||||||
{
|
{
|
||||||
QString dir = getExistingDirectory(tr("Select directory to ignore"), true);
|
addExcludePath(getExistingDirectory(tr("Select directory to ignore"), true));
|
||||||
if (!dir.isEmpty())
|
}
|
||||||
addExcludePath(dir);
|
|
||||||
|
void ProjectFileDialog::addExcludeFile()
|
||||||
|
{
|
||||||
|
const QFileInfo inf(mProjectFile->getFilename());
|
||||||
|
const QDir &dir = inf.absoluteDir();
|
||||||
|
QMap<QString,QString> filters;
|
||||||
|
filters[tr("Source files")] = "*.c *.cpp";
|
||||||
|
filters[tr("All files")] = "*.*";
|
||||||
|
addExcludePath(QFileDialog::getOpenFileName(this, tr("Exclude file"), dir.canonicalPath(), toFilterString(filters)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectFileDialog::editExcludePath()
|
void ProjectFileDialog::editExcludePath()
|
||||||
|
@ -802,7 +826,10 @@ int ProjectFileDialog::getSuppressionIndex(const QString &shortText) const
|
||||||
|
|
||||||
void ProjectFileDialog::browseMisraFile()
|
void ProjectFileDialog::browseMisraFile()
|
||||||
{
|
{
|
||||||
const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA rule texts file"), QDir::homePath(), tr("Misra rule texts file (%1)").arg("*.txt"));
|
const QString fileName = QFileDialog::getOpenFileName(this,
|
||||||
|
tr("Select MISRA rule texts file"),
|
||||||
|
QDir::homePath(),
|
||||||
|
tr("Misra rule texts file (%1)").arg("*.txt"));
|
||||||
if (!fileName.isEmpty()) {
|
if (!fileName.isEmpty()) {
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
mUI.mEditMisraFile->setText(fileName);
|
mUI.mEditMisraFile->setText(fileName);
|
||||||
|
|
|
@ -58,8 +58,8 @@ private:
|
||||||
*/
|
*/
|
||||||
QString getRootPath() const;
|
QString getRootPath() const;
|
||||||
|
|
||||||
QStringList getVsConfigurations() const;
|
QStringList getProjectConfigurations() const;
|
||||||
void setVsConfigurations(const QStringList &configs);
|
void setProjectConfigurations(const QStringList &configs);
|
||||||
|
|
||||||
QString getImportProject() const;
|
QString getImportProject() const;
|
||||||
|
|
||||||
|
@ -215,10 +215,15 @@ protected slots:
|
||||||
void editIncludeDir();
|
void editIncludeDir();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add new path to exclude.
|
* @brief Add new path to exclude list.
|
||||||
*/
|
*/
|
||||||
void addExcludePath();
|
void addExcludePath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add new file to exclude list.
|
||||||
|
*/
|
||||||
|
void addExcludeFile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Edit excluded path in the list.
|
* @brief Edit excluded path in the list.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -130,17 +130,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="mListVsConfigs">
|
<widget class="QListWidget" name="mListVsConfigs"/>
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>100</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="selectionMode">
|
|
||||||
<enum>QAbstractItemView::MultiSelection</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
@ -420,7 +410,11 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="mEditBuildDir"/>
|
<widget class="QLineEdit" name="mEditBuildDir">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>This is a workfolder that Cppcheck will use for various purposes.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="mBtnBrowseBuildDir">
|
<widget class="QPushButton" name="mBtnBrowseBuildDir">
|
||||||
|
@ -461,18 +455,31 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_11">
|
<widget class="QGroupBox" name="groupBox_11">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Check that code is safe</string>
|
<string>Analysis</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="mBugHunting">
|
<widget class="QRadioButton" name="mBtnNormalAnalysis">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Bug hunting -- Detect all bugs. Generates mostly noise.</string>
|
<string>Normal analysis -- Avoid false positives.</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="mBtnBugHunting">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bug hunting -- Generates mostly noise. The goal is to be "soundy" and detect most bugs.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="mBtnSafeClasses">
|
<widget class="QCheckBox" name="mBtnSafeClasses">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value.</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Check that each class has a safe public interface</string>
|
<string>Check that each class has a safe public interface</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -605,7 +612,11 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="mEditProjectRoot"/>
|
<widget class="QLineEdit" name="mEditProjectRoot">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Filepaths in warnings will be relative to this path</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -617,7 +628,11 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="mEditTags"/>
|
<widget class="QLineEdit" name="mEditTags">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -625,7 +640,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_7">
|
<widget class="QGroupBox" name="groupBox_7">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Exclude source files in paths</string>
|
<string>Exclude source files</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
|
@ -636,7 +651,14 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="mBtnAddIgnorePath">
|
<widget class="QPushButton" name="mBtnAddIgnorePath">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add...</string>
|
<string>Exclude folder...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="mBtnAddIgnoreFile">
|
||||||
|
<property name="text">
|
||||||
|
<string>Exclude file...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -43,14 +43,33 @@
|
||||||
#include "resultstree.h"
|
#include "resultstree.h"
|
||||||
#include "report.h"
|
#include "report.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
|
#include "projectfile.h"
|
||||||
#include "showtypes.h"
|
#include "showtypes.h"
|
||||||
#include "threadhandler.h"
|
#include "threadhandler.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "xmlreportv2.h"
|
#include "xmlreportv2.h"
|
||||||
|
|
||||||
|
static const char COLUMN[] = "column";
|
||||||
|
static const char CWE[] = "cwe";
|
||||||
|
static const char ERRORID[] = "id";
|
||||||
|
static const char FILENAME[] = "file";
|
||||||
|
static const char FILE0[] = "file0";
|
||||||
|
static const char FUNCTION[] = "function";
|
||||||
|
static const char HASH[] = "hash";
|
||||||
|
static const char HIDE[] = "hide";
|
||||||
|
static const char INCOMPLETE[] = "incomplete";
|
||||||
|
static const char INCONCLUSIVE[] = "inconclusive";
|
||||||
|
static const char LINE[] = "line";
|
||||||
|
static const char MESSAGE[] = "message";
|
||||||
|
static const char SEVERITY[] = "severity";
|
||||||
|
static const char SINCEDATE[] = "sinceDate";
|
||||||
|
static const char SYMBOLNAMES[] = "symbolNames";
|
||||||
|
static const char SUMMARY[] = "summary";
|
||||||
|
static const char TAGS[] = "tags";
|
||||||
|
|
||||||
// These must match column headers given in ResultsTree::translate()
|
// These must match column headers given in ResultsTree::translate()
|
||||||
static const unsigned int COLUMN_SINCE_DATE = 6;
|
static const int COLUMN_SINCE_DATE = 6;
|
||||||
static const unsigned int COLUMN_TAGS = 7;
|
static const int COLUMN_TAGS = 7;
|
||||||
|
|
||||||
static QString getFunction(QStandardItem *item)
|
static QString getFunction(QStandardItem *item)
|
||||||
{
|
{
|
||||||
|
@ -168,12 +187,15 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
||||||
line.errorId = item.errorId;
|
line.errorId = item.errorId;
|
||||||
line.incomplete = item.incomplete;
|
line.incomplete = item.incomplete;
|
||||||
line.cwe = item.cwe;
|
line.cwe = item.cwe;
|
||||||
|
line.hash = item.hash;
|
||||||
line.inconclusive = item.inconclusive;
|
line.inconclusive = item.inconclusive;
|
||||||
line.summary = item.summary;
|
line.summary = item.summary;
|
||||||
line.message = item.message;
|
line.message = item.message;
|
||||||
line.severity = item.severity;
|
line.severity = item.severity;
|
||||||
line.sinceDate = item.sinceDate;
|
line.sinceDate = item.sinceDate;
|
||||||
line.tags = item.tags;
|
if (const ProjectFile *activeProject = ProjectFile::getActiveProject()) {
|
||||||
|
line.tags = activeProject->getWarningTags(item.hash);
|
||||||
|
}
|
||||||
//Create the base item for the error and ensure it has a proper
|
//Create the base item for the error and ensure it has a proper
|
||||||
//file item as a parent
|
//file item as a parent
|
||||||
QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide);
|
QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide);
|
||||||
|
@ -188,21 +210,23 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
||||||
|
|
||||||
//Add user data to that item
|
//Add user data to that item
|
||||||
QMap<QString, QVariant> data;
|
QMap<QString, QVariant> data;
|
||||||
data["severity"] = ShowTypes::SeverityToShowType(item.severity);
|
data[SEVERITY] = ShowTypes::SeverityToShowType(item.severity);
|
||||||
data["summary"] = item.summary;
|
data[SUMMARY] = item.summary;
|
||||||
data["message"] = item.message;
|
data[MESSAGE] = item.message;
|
||||||
data["file"] = loc.file;
|
data[FILENAME] = loc.file;
|
||||||
data["line"] = loc.line;
|
data[LINE] = loc.line;
|
||||||
data["column"] = loc.column;
|
data[COLUMN] = loc.column;
|
||||||
data["id"] = item.errorId;
|
data[ERRORID] = item.errorId;
|
||||||
data["incomplete"] = item.incomplete;
|
data[INCOMPLETE] = item.incomplete;
|
||||||
data["cwe"] = item.cwe;
|
data[CWE] = item.cwe;
|
||||||
data["inconclusive"] = item.inconclusive;
|
data[HASH] = item.hash;
|
||||||
data["file0"] = stripPath(item.file0, true);
|
data[INCONCLUSIVE] = item.inconclusive;
|
||||||
data["function"] = item.function;
|
data[FILE0] = stripPath(item.file0, true);
|
||||||
data["sinceDate"] = item.sinceDate;
|
data[FUNCTION] = item.function;
|
||||||
data["tags"] = item.tags;
|
data[SINCEDATE] = item.sinceDate;
|
||||||
data["hide"] = hide;
|
data[SYMBOLNAMES] = item.symbolNames;
|
||||||
|
data[TAGS] = line.tags;
|
||||||
|
data[HIDE] = hide;
|
||||||
stditem->setData(QVariant(data));
|
stditem->setData(QVariant(data));
|
||||||
|
|
||||||
//Add backtrace files as children
|
//Add backtrace files as children
|
||||||
|
@ -223,16 +247,18 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
||||||
|
|
||||||
// Add user data to that item
|
// Add user data to that item
|
||||||
QMap<QString, QVariant> child_data;
|
QMap<QString, QVariant> child_data;
|
||||||
child_data["severity"] = ShowTypes::SeverityToShowType(line.severity);
|
child_data[SEVERITY] = ShowTypes::SeverityToShowType(line.severity);
|
||||||
child_data["summary"] = line.summary;
|
child_data[SUMMARY] = line.summary;
|
||||||
child_data["message"] = line.message;
|
child_data[MESSAGE] = line.message;
|
||||||
child_data["file"] = e.file;
|
child_data[FILENAME] = e.file;
|
||||||
child_data["line"] = e.line;
|
child_data[LINE] = e.line;
|
||||||
child_data["column"] = e.column;
|
child_data[COLUMN] = e.column;
|
||||||
child_data["id"] = line.errorId;
|
child_data[ERRORID] = line.errorId;
|
||||||
child_data["incomplete"] = line.incomplete;
|
child_data[INCOMPLETE] = line.incomplete;
|
||||||
child_data["cwe"] = line.cwe;
|
child_data[CWE] = line.cwe;
|
||||||
child_data["inconclusive"] = line.inconclusive;
|
child_data[HASH] = line.hash;
|
||||||
|
child_data[INCONCLUSIVE] = line.inconclusive;
|
||||||
|
child_data[SYMBOLNAMES] = item.symbolNames;
|
||||||
child_item->setData(QVariant(child_data));
|
child_item->setData(QVariant(child_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,8 +385,8 @@ void ResultsTree::clear(const QString &filename)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QVariantMap data = fileItem->data().toMap();
|
QVariantMap data = fileItem->data().toMap();
|
||||||
if (stripped == data["file"].toString() ||
|
if (stripped == data[FILENAME].toString() ||
|
||||||
filename == data["file0"].toString()) {
|
filename == data[FILE0].toString()) {
|
||||||
mModel.removeRow(i);
|
mModel.removeRow(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +402,7 @@ void ResultsTree::clearRecheckFile(const QString &filename)
|
||||||
|
|
||||||
QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename);
|
QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename);
|
||||||
QVariantMap data = fileItem->data().toMap();
|
QVariantMap data = fileItem->data().toMap();
|
||||||
QString storedfile = data["file"].toString();
|
QString storedfile = data[FILENAME].toString();
|
||||||
storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile);
|
storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile);
|
||||||
if (actualfile == storedfile) {
|
if (actualfile == storedfile) {
|
||||||
mModel.removeRow(i);
|
mModel.removeRow(i);
|
||||||
|
@ -446,7 +472,7 @@ void ResultsTree::showHiddenResults()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QVariantMap data = fileItem->data().toMap();
|
QVariantMap data = fileItem->data().toMap();
|
||||||
data["hide"] = false;
|
data[HIDE] = false;
|
||||||
fileItem->setData(QVariant(data));
|
fileItem->setData(QVariant(data));
|
||||||
|
|
||||||
int errorcount = fileItem->rowCount();
|
int errorcount = fileItem->rowCount();
|
||||||
|
@ -454,7 +480,7 @@ void ResultsTree::showHiddenResults()
|
||||||
QStandardItem *child = fileItem->child(j, 0);
|
QStandardItem *child = fileItem->child(j, 0);
|
||||||
if (child) {
|
if (child) {
|
||||||
data = child->data().toMap();
|
data = child->data().toMap();
|
||||||
data["hide"] = false;
|
data[HIDE] = false;
|
||||||
child->setData(QVariant(data));
|
child->setData(QVariant(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -496,21 +522,21 @@ void ResultsTree::refreshTree()
|
||||||
QVariantMap data = userdata.toMap();
|
QVariantMap data = userdata.toMap();
|
||||||
|
|
||||||
//Check if this error should be hidden
|
//Check if this error should be hidden
|
||||||
bool hide = (data["hide"].toBool() || !mShowSeverities.isShown(ShowTypes::VariantToShowType(data["severity"])));
|
bool hide = (data[HIDE].toBool() || !mShowSeverities.isShown(ShowTypes::VariantToShowType(data[SEVERITY])));
|
||||||
|
|
||||||
//If specified, filter on summary, message, filename, and id
|
//If specified, filter on summary, message, filename, and id
|
||||||
if (!hide && !mFilter.isEmpty()) {
|
if (!hide && !mFilter.isEmpty()) {
|
||||||
if (!data["summary"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
if (!data[SUMMARY].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||||
!data["message"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
!data[MESSAGE].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||||
!data["file"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
!data[FILENAME].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||||
!data["id"].toString().contains(mFilter, Qt::CaseInsensitive)) {
|
!data[ERRORID].toString().contains(mFilter, Qt::CaseInsensitive)) {
|
||||||
hide = true;
|
hide = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tool filter
|
// Tool filter
|
||||||
if (!hide) {
|
if (!hide) {
|
||||||
if (data["id"].toString().startsWith("clang"))
|
if (data[ERRORID].toString().startsWith("clang"))
|
||||||
hide = !mShowClang;
|
hide = !mShowClang;
|
||||||
else
|
else
|
||||||
hide = !mShowCppcheck;
|
hide = !mShowCppcheck;
|
||||||
|
@ -557,8 +583,8 @@ QStandardItem *ResultsTree::ensureFileItem(const QString &fullpath, const QStrin
|
||||||
|
|
||||||
//Add user data to that item
|
//Add user data to that item
|
||||||
QMap<QString, QVariant> data;
|
QMap<QString, QVariant> data;
|
||||||
data["file"] = fullpath;
|
data[FILENAME] = fullpath;
|
||||||
data["file0"] = file0;
|
data[FILE0] = file0;
|
||||||
item->setData(QVariant(data));
|
item->setData(QVariant(data));
|
||||||
mModel.appendRow(item);
|
mModel.appendRow(item);
|
||||||
|
|
||||||
|
@ -652,10 +678,15 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
menu.addAction(hide);
|
menu.addAction(hide);
|
||||||
menu.addAction(hideallid);
|
menu.addAction(hideallid);
|
||||||
|
|
||||||
if (!bughunting) {
|
if (!bughunting) {
|
||||||
QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
|
QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
|
||||||
menu.addAction(suppress);
|
menu.addAction(suppress);
|
||||||
connect(suppress, SIGNAL(triggered()), this, SLOT(suppressSelectedIds()));
|
connect(suppress, &QAction::triggered, this, &ResultsTree::suppressSelectedIds);
|
||||||
|
} else {
|
||||||
|
QAction *suppress = new QAction(tr("Suppress"), &menu);
|
||||||
|
menu.addAction(suppress);
|
||||||
|
connect(suppress, &QAction::triggered, this, &ResultsTree::suppressHash);
|
||||||
}
|
}
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
menu.addAction(opencontainingfolder);
|
menu.addAction(opencontainingfolder);
|
||||||
|
@ -666,7 +697,8 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
||||||
connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult()));
|
connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult()));
|
||||||
connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
|
connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
|
||||||
|
|
||||||
if (!mTags.isEmpty()) {
|
const ProjectFile *currentProject = ProjectFile::getActiveProject();
|
||||||
|
if (currentProject && !currentProject->getTags().isEmpty()) {
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
QMenu *tagMenu = menu.addMenu(tr("Tag"));
|
QMenu *tagMenu = menu.addMenu(tr("Tag"));
|
||||||
{
|
{
|
||||||
|
@ -677,7 +709,7 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const QString tagstr, mTags) {
|
foreach (const QString tagstr, currentProject->getTags()) {
|
||||||
QAction *action = new QAction(tagstr, tagMenu);
|
QAction *action = new QAction(tagstr, tagMenu);
|
||||||
tagMenu->addAction(action);
|
tagMenu->addAction(action);
|
||||||
connect(action, &QAction::triggered, [=]() {
|
connect(action, &QAction::triggered, [=]() {
|
||||||
|
@ -745,7 +777,7 @@ void ResultsTree::startApplication(QStandardItem *target, int application)
|
||||||
QVariantMap data = target->data().toMap();
|
QVariantMap data = target->data().toMap();
|
||||||
|
|
||||||
//Replace (file) with filename
|
//Replace (file) with filename
|
||||||
QString file = data["file"].toString();
|
QString file = data[FILENAME].toString();
|
||||||
file = QDir::toNativeSeparators(file);
|
file = QDir::toNativeSeparators(file);
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
file.replace(QString("\\"), QString("\\\\"));
|
file.replace(QString("\\"), QString("\\\\"));
|
||||||
|
@ -781,11 +813,11 @@ void ResultsTree::startApplication(QStandardItem *target, int application)
|
||||||
QString params = app.getParameters();
|
QString params = app.getParameters();
|
||||||
params.replace("(file)", file, Qt::CaseInsensitive);
|
params.replace("(file)", file, Qt::CaseInsensitive);
|
||||||
|
|
||||||
QVariant line = data["line"];
|
QVariant line = data[LINE];
|
||||||
params.replace("(line)", QString("%1").arg(line.toInt()), Qt::CaseInsensitive);
|
params.replace("(line)", QString("%1").arg(line.toInt()), Qt::CaseInsensitive);
|
||||||
|
|
||||||
params.replace("(message)", data["message"].toString(), Qt::CaseInsensitive);
|
params.replace("(message)", data[MESSAGE].toString(), Qt::CaseInsensitive);
|
||||||
params.replace("(severity)", data["severity"].toString(), Qt::CaseInsensitive);
|
params.replace("(severity)", data[SEVERITY].toString(), Qt::CaseInsensitive);
|
||||||
|
|
||||||
QString program = app.getPath();
|
QString program = app.getPath();
|
||||||
|
|
||||||
|
@ -881,14 +913,14 @@ void ResultsTree::copy()
|
||||||
QVariantMap data = item->data().toMap();
|
QVariantMap data = item->data().toMap();
|
||||||
if (!data.contains("id"))
|
if (!data.contains("id"))
|
||||||
continue;
|
continue;
|
||||||
QString inconclusive = data["inconclusive"].toBool() ? ",inconclusive" : "";
|
QString inconclusive = data[INCONCLUSIVE].toBool() ? ",inconclusive" : "";
|
||||||
text += '[' + data["file"].toString() + ':' + QString::number(data["line"].toInt())
|
text += '[' + data[FILENAME].toString() + ':' + QString::number(data[LINE].toInt())
|
||||||
+ "] ("
|
+ "] ("
|
||||||
+ QString::fromStdString(Severity::toString(ShowTypes::ShowTypeToSeverity((ShowTypes::ShowType)data["severity"].toInt()))) + inconclusive
|
+ QString::fromStdString(Severity::toString(ShowTypes::ShowTypeToSeverity((ShowTypes::ShowType)data[SEVERITY].toInt()))) + inconclusive
|
||||||
+ ") "
|
+ ") "
|
||||||
+ data["message"].toString()
|
+ data[MESSAGE].toString()
|
||||||
+ " ["
|
+ " ["
|
||||||
+ data["id"].toString()
|
+ data[ERRORID].toString()
|
||||||
+ "]\n";
|
+ "]\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,7 +938,7 @@ void ResultsTree::hideResult()
|
||||||
QStandardItem *item = mModel.itemFromIndex(index);
|
QStandardItem *item = mModel.itemFromIndex(index);
|
||||||
//Set the "hide" flag for this item
|
//Set the "hide" flag for this item
|
||||||
QVariantMap data = item->data().toMap();
|
QVariantMap data = item->data().toMap();
|
||||||
data["hide"] = true;
|
data[HIDE] = true;
|
||||||
item->setData(QVariant(data));
|
item->setData(QVariant(data));
|
||||||
|
|
||||||
refreshTree();
|
refreshTree();
|
||||||
|
@ -926,7 +958,7 @@ void ResultsTree::recheckSelectedFiles()
|
||||||
while (item->parent())
|
while (item->parent())
|
||||||
item = item->parent();
|
item = item->parent();
|
||||||
QVariantMap data = item->data().toMap();
|
QVariantMap data = item->data().toMap();
|
||||||
QString currentFile = data["file"].toString();
|
QString currentFile = data[FILENAME].toString();
|
||||||
if (!currentFile.isEmpty()) {
|
if (!currentFile.isEmpty()) {
|
||||||
QString fileNameWithCheckPath;
|
QString fileNameWithCheckPath;
|
||||||
QFileInfo curfileInfo(currentFile);
|
QFileInfo curfileInfo(currentFile);
|
||||||
|
@ -940,8 +972,8 @@ void ResultsTree::recheckSelectedFiles()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Path::isHeader(currentFile.toStdString())) {
|
if (Path::isHeader(currentFile.toStdString())) {
|
||||||
if (!data["file0"].toString().isEmpty() && !selectedItems.contains(data["file0"].toString())) {
|
if (!data[FILE0].toString().isEmpty() && !selectedItems.contains(data[FILE0].toString())) {
|
||||||
selectedItems<<((!mCheckPath.isEmpty() && (data["file0"].toString().indexOf(mCheckPath) != 0)) ? (mCheckPath + "/" + data["file0"].toString()) : data["file0"].toString());
|
selectedItems<<((!mCheckPath.isEmpty() && (data[FILE0].toString().indexOf(mCheckPath) != 0)) ? (mCheckPath + "/" + data[FILE0].toString()) : data[FILE0].toString());
|
||||||
if (!selectedItems.contains(fileNameWithCheckPath))
|
if (!selectedItems.contains(fileNameWithCheckPath))
|
||||||
selectedItems<<fileNameWithCheckPath;
|
selectedItems<<fileNameWithCheckPath;
|
||||||
}
|
}
|
||||||
|
@ -962,7 +994,7 @@ void ResultsTree::hideAllIdResult()
|
||||||
mContextItem = mContextItem->parent()->child(mContextItem->row(), 0);
|
mContextItem = mContextItem->parent()->child(mContextItem->row(), 0);
|
||||||
QVariantMap data = mContextItem->data().toMap();
|
QVariantMap data = mContextItem->data().toMap();
|
||||||
|
|
||||||
QString messageId = data["id"].toString();
|
QString messageId = data[ERRORID].toString();
|
||||||
|
|
||||||
mHiddenMessageId.append(messageId);
|
mHiddenMessageId.append(messageId);
|
||||||
|
|
||||||
|
@ -986,8 +1018,8 @@ void ResultsTree::hideAllIdResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap userdata = child->data().toMap();
|
QVariantMap userdata = child->data().toMap();
|
||||||
if (userdata["id"].toString() == messageId) {
|
if (userdata[ERRORID].toString() == messageId) {
|
||||||
userdata["hide"] = true;
|
userdata[HIDE] = true;
|
||||||
child->setData(QVariant(userdata));
|
child->setData(QVariant(userdata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +1045,7 @@ void ResultsTree::suppressSelectedIds()
|
||||||
QVariantMap data = item->data().toMap();
|
QVariantMap data = item->data().toMap();
|
||||||
if (!data.contains("id"))
|
if (!data.contains("id"))
|
||||||
continue;
|
continue;
|
||||||
selectedIds << data["id"].toString();
|
selectedIds << data[ERRORID].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete all errors with selected message Ids
|
// delete all errors with selected message Ids
|
||||||
|
@ -1022,18 +1054,59 @@ void ResultsTree::suppressSelectedIds()
|
||||||
for (int j = 0; j < file->rowCount();) {
|
for (int j = 0; j < file->rowCount();) {
|
||||||
QStandardItem *errorItem = file->child(j, 0);
|
QStandardItem *errorItem = file->child(j, 0);
|
||||||
QVariantMap userdata = errorItem->data().toMap();
|
QVariantMap userdata = errorItem->data().toMap();
|
||||||
if (selectedIds.contains(userdata["id"].toString())) {
|
if (selectedIds.contains(userdata[ERRORID].toString())) {
|
||||||
file->removeRow(j);
|
file->removeRow(j);
|
||||||
} else {
|
} else {
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file->rowCount() == 0)
|
||||||
|
mModel.removeRow(file->row());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
emit suppressIds(selectedIds.toList());
|
emit suppressIds(selectedIds.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResultsTree::suppressHash()
|
||||||
|
{
|
||||||
|
if (!mSelectionModel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Extract selected warnings
|
||||||
|
QSet<QStandardItem *> selectedWarnings;
|
||||||
|
foreach (QModelIndex index, mSelectionModel->selectedRows()) {
|
||||||
|
QStandardItem *item = mModel.itemFromIndex(index);
|
||||||
|
if (!item->parent())
|
||||||
|
continue;
|
||||||
|
while (item->parent()->parent())
|
||||||
|
item = item->parent();
|
||||||
|
selectedWarnings.insert(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed = false;
|
||||||
|
ProjectFile *projectFile = ProjectFile::getActiveProject();
|
||||||
|
for (QStandardItem *item: selectedWarnings) {
|
||||||
|
QStandardItem *fileItem = item->parent();
|
||||||
|
const QVariantMap data = item->data().toMap();
|
||||||
|
if (projectFile && data.contains(HASH)) {
|
||||||
|
Suppressions::Suppression suppression;
|
||||||
|
suppression.hash = data[HASH].toULongLong();
|
||||||
|
suppression.errorId = data[ERRORID].toString().toStdString();
|
||||||
|
suppression.fileName = data[FILENAME].toString().toStdString();
|
||||||
|
suppression.lineNumber = data[LINE].toInt();
|
||||||
|
projectFile->addSuppression(suppression);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
fileItem->removeRow(item->row());
|
||||||
|
if (fileItem->rowCount() == 0)
|
||||||
|
mModel.removeRow(fileItem->row());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
projectFile->write();
|
||||||
|
}
|
||||||
|
|
||||||
void ResultsTree::openContainingFolder()
|
void ResultsTree::openContainingFolder()
|
||||||
{
|
{
|
||||||
QString filePath = getFilePath(mContextItem, true);
|
QString filePath = getFilePath(mContextItem, true);
|
||||||
|
@ -1053,18 +1126,22 @@ void ResultsTree::tagSelectedItems(const QString &tag)
|
||||||
if (!mSelectionModel)
|
if (!mSelectionModel)
|
||||||
return;
|
return;
|
||||||
bool isTagged = false;
|
bool isTagged = false;
|
||||||
|
ProjectFile *currentProject = ProjectFile::getActiveProject();
|
||||||
foreach (QModelIndex index, mSelectionModel->selectedRows()) {
|
foreach (QModelIndex index, mSelectionModel->selectedRows()) {
|
||||||
QStandardItem *item = mModel.itemFromIndex(index);
|
QStandardItem *item = mModel.itemFromIndex(index);
|
||||||
QVariantMap data = item->data().toMap();
|
QVariantMap data = item->data().toMap();
|
||||||
if (data.contains("tags")) {
|
if (data.contains("tags")) {
|
||||||
data["tags"] = tag;
|
data[TAGS] = tag;
|
||||||
item->setData(QVariant(data));
|
item->setData(QVariant(data));
|
||||||
item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag);
|
item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag);
|
||||||
isTagged = true;
|
if (currentProject && data.contains(HASH)) {
|
||||||
|
isTagged = true;
|
||||||
|
currentProject->setWarningTags(data[HASH].toULongLong(), tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isTagged)
|
if (isTagged)
|
||||||
emit tagged();
|
currentProject->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResultsTree::context(int application)
|
void ResultsTree::context(int application)
|
||||||
|
@ -1088,7 +1165,7 @@ QString ResultsTree::getFilePath(QStandardItem *target, bool fullPath)
|
||||||
QString pathStr;
|
QString pathStr;
|
||||||
|
|
||||||
//Replace (file) with filename
|
//Replace (file) with filename
|
||||||
QString file = data["file"].toString();
|
QString file = data[FILENAME].toString();
|
||||||
pathStr = QDir::toNativeSeparators(file);
|
pathStr = QDir::toNativeSeparators(file);
|
||||||
if (!fullPath) {
|
if (!fullPath) {
|
||||||
QFileInfo fi(pathStr);
|
QFileInfo fi(pathStr);
|
||||||
|
@ -1188,12 +1265,12 @@ void ResultsTree::updateFromOldReport(const QString &filename)
|
||||||
|
|
||||||
// New error .. set the "sinceDate" property
|
// New error .. set the "sinceDate" property
|
||||||
if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) {
|
if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) {
|
||||||
data["sinceDate"] = oldErrors[oldErrorIndex].sinceDate;
|
data[SINCEDATE] = oldErrors[oldErrorIndex].sinceDate;
|
||||||
error->setData(data);
|
error->setData(data);
|
||||||
fileItem->child(j, COLUMN_SINCE_DATE)->setText(oldErrors[oldErrorIndex].sinceDate);
|
fileItem->child(j, COLUMN_SINCE_DATE)->setText(oldErrors[oldErrorIndex].sinceDate);
|
||||||
} else if (oldErrorIndex < 0 || data["sinceDate"].toString().isEmpty()) {
|
} else if (oldErrorIndex < 0 || data[SINCEDATE].toString().isEmpty()) {
|
||||||
const QString sinceDate = QDate::currentDate().toString(Qt::SystemLocaleShortDate);
|
const QString sinceDate = QDate::currentDate().toString(Qt::SystemLocaleShortDate);
|
||||||
data["sinceDate"] = sinceDate;
|
data[SINCEDATE] = sinceDate;
|
||||||
error->setData(data);
|
error->setData(data);
|
||||||
fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate);
|
fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate);
|
||||||
if (oldErrorIndex < 0)
|
if (oldErrorIndex < 0)
|
||||||
|
@ -1204,7 +1281,7 @@ void ResultsTree::updateFromOldReport(const QString &filename)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex];
|
const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex];
|
||||||
data["tags"] = oldErrorItem.tags;
|
data[TAGS] = oldErrorItem.tags;
|
||||||
error->setData(data);
|
error->setData(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1215,22 +1292,23 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con
|
||||||
// Get error's user data
|
// Get error's user data
|
||||||
QVariantMap data = error->data().toMap();
|
QVariantMap data = error->data().toMap();
|
||||||
|
|
||||||
item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data["severity"]));
|
item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data[SEVERITY]));
|
||||||
item->summary = data["summary"].toString();
|
item->summary = data[SUMMARY].toString();
|
||||||
item->message = data["message"].toString();
|
item->message = data[MESSAGE].toString();
|
||||||
item->errorId = data["id"].toString();
|
item->errorId = data[ERRORID].toString();
|
||||||
item->incomplete = data["incomplete"].toBool();
|
item->incomplete = data[INCOMPLETE].toBool();
|
||||||
item->cwe = data["cwe"].toInt();
|
item->cwe = data[CWE].toInt();
|
||||||
item->inconclusive = data["inconclusive"].toBool();
|
item->hash = data[HASH].toULongLong();
|
||||||
item->file0 = data["file0"].toString();
|
item->inconclusive = data[INCONCLUSIVE].toBool();
|
||||||
item->sinceDate = data["sinceDate"].toString();
|
item->file0 = data[FILE0].toString();
|
||||||
item->tags = data["tags"].toString();
|
item->sinceDate = data[SINCEDATE].toString();
|
||||||
|
item->tags = data[TAGS].toString();
|
||||||
|
|
||||||
if (error->rowCount() == 0) {
|
if (error->rowCount() == 0) {
|
||||||
QErrorPathItem e;
|
QErrorPathItem e;
|
||||||
e.file = stripPath(data["file"].toString(), true);
|
e.file = stripPath(data[FILENAME].toString(), true);
|
||||||
e.line = data["line"].toUInt();
|
e.line = data[LINE].toInt();
|
||||||
e.info = data["message"].toString();
|
e.info = data[MESSAGE].toString();
|
||||||
item->errorPath << e;
|
item->errorPath << e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1242,9 +1320,9 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con
|
||||||
QVariantMap child_data = child_userdata.toMap();
|
QVariantMap child_data = child_userdata.toMap();
|
||||||
|
|
||||||
QErrorPathItem e;
|
QErrorPathItem e;
|
||||||
e.file = stripPath(child_data["file"].toString(), true);
|
e.file = stripPath(child_data[FILENAME].toString(), true);
|
||||||
e.line = child_data["line"].toUInt();
|
e.line = child_data[LINE].toUInt();
|
||||||
e.info = child_data["message"].toString();
|
e.info = child_data[MESSAGE].toString();
|
||||||
item->errorPath << e;
|
item->errorPath << e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1312,7 +1390,7 @@ void ResultsTree::refreshFilePaths(QStandardItem *item)
|
||||||
QVariantMap data = userdata.toMap();
|
QVariantMap data = userdata.toMap();
|
||||||
|
|
||||||
//Get list of files
|
//Get list of files
|
||||||
QString file = data["file"].toString();
|
QString file = data[FILENAME].toString();
|
||||||
|
|
||||||
//Update this error's text
|
//Update this error's text
|
||||||
error->setText(stripPath(file, false));
|
error->setText(stripPath(file, false));
|
||||||
|
@ -1332,7 +1410,7 @@ void ResultsTree::refreshFilePaths(QStandardItem *item)
|
||||||
QVariantMap child_data = child_userdata.toMap();
|
QVariantMap child_data = child_userdata.toMap();
|
||||||
|
|
||||||
//Get list of files
|
//Get list of files
|
||||||
QString child_files = child_data["file"].toString();
|
QString child_files = child_data[FILENAME].toString();
|
||||||
//Update file's path
|
//Update file's path
|
||||||
child->setText(stripPath(child_files, false));
|
child->setText(stripPath(child_files, false));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,6 @@ public:
|
||||||
virtual ~ResultsTree();
|
virtual ~ResultsTree();
|
||||||
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler);
|
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler);
|
||||||
|
|
||||||
void setTags(const QStringList &tags) {
|
|
||||||
mTags = tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a new item to the tree
|
* @brief Add a new item to the tree
|
||||||
*
|
*
|
||||||
|
@ -206,11 +202,6 @@ signals:
|
||||||
*/
|
*/
|
||||||
void treeSelectionChanged(const QModelIndex ¤t);
|
void treeSelectionChanged(const QModelIndex ¤t);
|
||||||
|
|
||||||
/**
|
|
||||||
* Selected item(s) has been tagged
|
|
||||||
*/
|
|
||||||
void tagged();
|
|
||||||
|
|
||||||
/** Suppress Ids */
|
/** Suppress Ids */
|
||||||
void suppressIds(QStringList ids);
|
void suppressIds(QStringList ids);
|
||||||
|
|
||||||
|
@ -285,6 +276,9 @@ protected slots:
|
||||||
/** Slot for context menu item to suppress all messages with the current message id */
|
/** Slot for context menu item to suppress all messages with the current message id */
|
||||||
void suppressSelectedIds();
|
void suppressSelectedIds();
|
||||||
|
|
||||||
|
/** Slot for context menu item to suppress message with hash */
|
||||||
|
void suppressHash();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Slot for context menu item to open the folder containing the current file.
|
* @brief Slot for context menu item to open the folder containing the current file.
|
||||||
*/
|
*/
|
||||||
|
@ -529,8 +523,6 @@ private:
|
||||||
/** @brief Convert GUI error item into data error item */
|
/** @brief Convert GUI error item into data error item */
|
||||||
void readErrorItem(const QStandardItem *error, ErrorItem *item) const;
|
void readErrorItem(const QStandardItem *error, ErrorItem *item) const;
|
||||||
|
|
||||||
QStringList mTags;
|
|
||||||
|
|
||||||
QStringList mHiddenMessageId;
|
QStringList mHiddenMessageId;
|
||||||
|
|
||||||
QItemSelectionModel *mSelectionModel;
|
QItemSelectionModel *mSelectionModel;
|
||||||
|
|
|
@ -53,7 +53,6 @@ ResultsView::ResultsView(QWidget * parent) :
|
||||||
connect(mUI.mTree, &ResultsTree::resultsHidden, this, &ResultsView::resultsHidden);
|
connect(mUI.mTree, &ResultsTree::resultsHidden, this, &ResultsView::resultsHidden);
|
||||||
connect(mUI.mTree, &ResultsTree::checkSelected, this, &ResultsView::checkSelected);
|
connect(mUI.mTree, &ResultsTree::checkSelected, this, &ResultsView::checkSelected);
|
||||||
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::suppressIds, this, &ResultsView::suppressIds);
|
connect(mUI.mTree, &ResultsTree::suppressIds, this, &ResultsView::suppressIds);
|
||||||
connect(mUI.mTree, &ResultsTree::editFunctionContract, this, &ResultsView::editFunctionContract);
|
connect(mUI.mTree, &ResultsTree::editFunctionContract, this, &ResultsView::editFunctionContract);
|
||||||
connect(this, &ResultsView::showResults, mUI.mTree, &ResultsTree::showResults);
|
connect(this, &ResultsView::showResults, mUI.mTree, &ResultsTree::showResults);
|
||||||
|
@ -386,9 +385,8 @@ void ResultsView::updateDetails(const QModelIndex &index)
|
||||||
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(mUI.mTree->model());
|
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(mUI.mTree->model());
|
||||||
QStandardItem *item = model->itemFromIndex(index);
|
QStandardItem *item = model->itemFromIndex(index);
|
||||||
|
|
||||||
mUI.mCode->setPlainText(QString());
|
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
mUI.mCode->clear();
|
||||||
mUI.mDetails->setText(QString());
|
mUI.mDetails->setText(QString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -401,6 +399,7 @@ void ResultsView::updateDetails(const QModelIndex &index)
|
||||||
|
|
||||||
// If there is no severity data then it is a parent item without summary and message
|
// If there is no severity data then it is a parent item without summary and message
|
||||||
if (!data.contains("severity")) {
|
if (!data.contains("severity")) {
|
||||||
|
mUI.mCode->clear();
|
||||||
mUI.mDetails->setText(QString());
|
mUI.mDetails->setText(QString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -426,19 +425,24 @@ void ResultsView::updateDetails(const QModelIndex &index)
|
||||||
if (!QFileInfo(filepath).exists() && QFileInfo(mUI.mTree->getCheckDirectory() + '/' + filepath).exists())
|
if (!QFileInfo(filepath).exists() && QFileInfo(mUI.mTree->getCheckDirectory() + '/' + filepath).exists())
|
||||||
filepath = mUI.mTree->getCheckDirectory() + '/' + filepath;
|
filepath = mUI.mTree->getCheckDirectory() + '/' + filepath;
|
||||||
|
|
||||||
QFile file(filepath);
|
QStringList symbols;
|
||||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (data.contains("symbolNames"))
|
||||||
QStringList symbols;
|
symbols = data["symbolNames"].toString().split("\n");
|
||||||
QRegularExpression re(".*: ([A-Za-z_][A-Za-z0-9_]*)$");
|
|
||||||
const QString errorMessage = data["message"].toString();
|
|
||||||
QRegularExpressionMatch match = re.match(errorMessage);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
symbols << match.captured(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextStream in(&file);
|
if (filepath == mUI.mCode->getFileName()) {
|
||||||
mUI.mCode->setError(in.readAll(), lineNumber, symbols);
|
mUI.mCode->setError(lineNumber, symbols);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QFile file(filepath);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
mUI.mCode->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
mUI.mCode->setError(in.readAll(), lineNumber, symbols);
|
||||||
|
mUI.mCode->setFileName(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResultsView::log(const QString &str)
|
void ResultsView::log(const QString &str)
|
||||||
|
@ -448,7 +452,7 @@ void ResultsView::log(const QString &str)
|
||||||
|
|
||||||
void ResultsView::debugError(const ErrorItem &item)
|
void ResultsView::debugError(const ErrorItem &item)
|
||||||
{
|
{
|
||||||
mUI.mListLog->addItem(item.ToString());
|
mUI.mListLog->addItem(item.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResultsView::bughuntingReportLine(const QString& line)
|
void ResultsView::bughuntingReportLine(const QString& line)
|
||||||
|
|
|
@ -50,10 +50,6 @@ public:
|
||||||
virtual ~ResultsView();
|
virtual ~ResultsView();
|
||||||
ResultsView &operator=(const ResultsView &) = delete;
|
ResultsView &operator=(const ResultsView &) = delete;
|
||||||
|
|
||||||
void setTags(const QStringList &tags) {
|
|
||||||
mUI.mTree->setTags(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAddedContracts(const QStringList &addedContracts);
|
void setAddedContracts(const QStringList &addedContracts);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,11 +218,6 @@ signals:
|
||||||
*/
|
*/
|
||||||
void checkSelected(QStringList selectedFilesList);
|
void checkSelected(QStringList selectedFilesList);
|
||||||
|
|
||||||
/**
|
|
||||||
* Some results have been tagged
|
|
||||||
*/
|
|
||||||
void tagged();
|
|
||||||
|
|
||||||
/** Suppress Ids */
|
/** Suppress Ids */
|
||||||
void suppressIds(QStringList ids);
|
void suppressIds(QStringList ids);
|
||||||
|
|
||||||
|
@ -368,6 +359,9 @@ private slots:
|
||||||
void on_mListLog_customContextMenuRequested(const QPoint &pos);
|
void on_mListLog_customContextMenuRequested(const QPoint &pos);
|
||||||
private:
|
private:
|
||||||
QSet<QString> mContracts;
|
QSet<QString> mContracts;
|
||||||
|
|
||||||
|
/** Current file shown in the code editor */
|
||||||
|
QString mCurrentFileName;
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
#endif // RESULTSVIEW_H
|
#endif // RESULTSVIEW_H
|
||||||
|
|
|
@ -159,49 +159,34 @@
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSplitter" name="splitter">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="orientation">
|
<property name="text">
|
||||||
<enum>Qt::Vertical</enum>
|
<string>Configured contracts:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="mListAddedContracts">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Missing contracts:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListWidget" name="mListMissingContracts">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="layoutWidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Configured contracts:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="mListAddedContracts">
|
|
||||||
<property name="editTriggers">
|
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="layoutWidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Missing contracts:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="mListMissingContracts">
|
|
||||||
<property name="editTriggers">
|
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
|
||||||
</property>
|
|
||||||
<property name="sortingEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -34,6 +34,7 @@ static const QString ErrorElementName = "error";
|
||||||
static const QString ErrorsElementName = "errors";
|
static const QString ErrorsElementName = "errors";
|
||||||
static const QString LocationElementName = "location";
|
static const QString LocationElementName = "location";
|
||||||
static const QString CWEAttribute = "cwe";
|
static const QString CWEAttribute = "cwe";
|
||||||
|
static const QString HashAttribute = "hash";
|
||||||
static const QString SinceDateAttribute = "sinceDate";
|
static const QString SinceDateAttribute = "sinceDate";
|
||||||
static const QString TagsAttribute = "tag";
|
static const QString TagsAttribute = "tag";
|
||||||
static const QString FilenameAttribute = "file";
|
static const QString FilenameAttribute = "file";
|
||||||
|
@ -122,6 +123,8 @@ void XmlReportV2::writeError(const ErrorItem &error)
|
||||||
mXmlWriter->writeAttribute(InconclusiveAttribute, "true");
|
mXmlWriter->writeAttribute(InconclusiveAttribute, "true");
|
||||||
if (error.cwe > 0)
|
if (error.cwe > 0)
|
||||||
mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe));
|
mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe));
|
||||||
|
if (error.hash > 0)
|
||||||
|
mXmlWriter->writeAttribute(HashAttribute, QString::number(error.hash));
|
||||||
if (!error.sinceDate.isEmpty())
|
if (!error.sinceDate.isEmpty())
|
||||||
mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate);
|
mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate);
|
||||||
if (!error.tags.isEmpty())
|
if (!error.tags.isEmpty())
|
||||||
|
@ -214,7 +217,9 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader)
|
||||||
if (attribs.hasAttribute(QString(), InconclusiveAttribute))
|
if (attribs.hasAttribute(QString(), InconclusiveAttribute))
|
||||||
item.inconclusive = true;
|
item.inconclusive = true;
|
||||||
if (attribs.hasAttribute(QString(), CWEAttribute))
|
if (attribs.hasAttribute(QString(), CWEAttribute))
|
||||||
item.cwe = attribs.value(QString(), CWEAttribute).toString().toInt();
|
item.cwe = attribs.value(QString(), CWEAttribute).toInt();
|
||||||
|
if (attribs.hasAttribute(QString(), HashAttribute))
|
||||||
|
item.hash = attribs.value(QString(), HashAttribute).toULongLong();
|
||||||
if (attribs.hasAttribute(QString(), SinceDateAttribute))
|
if (attribs.hasAttribute(QString(), SinceDateAttribute))
|
||||||
item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString();
|
item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString();
|
||||||
if (attribs.hasAttribute(QString(), TagsAttribute))
|
if (attribs.hasAttribute(QString(), TagsAttribute))
|
||||||
|
|
|
@ -157,6 +157,45 @@ static void integerOverflow(const Token *tok, const ExprEngine::Value &value, Ex
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** check if variable is unconditionally assigned */
|
||||||
|
static bool isVariableAssigned(const Variable *var, const Token *tok, const Token *scopeStart=nullptr)
|
||||||
|
{
|
||||||
|
const Token * const start = scopeStart && precedes(var->nameToken(), scopeStart) ? scopeStart : var->nameToken();
|
||||||
|
|
||||||
|
for (const Token *prev = tok->previous(); prev; prev = prev->previous()) {
|
||||||
|
if (!precedes(start, prev))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (prev->str() == "}") {
|
||||||
|
if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) {
|
||||||
|
const Token *elseEnd = prev;
|
||||||
|
const Token *elseStart = prev->link();
|
||||||
|
const Token *ifEnd = elseStart->tokAt(-2);
|
||||||
|
const Token *ifStart = ifEnd->link();
|
||||||
|
if (isVariableAssigned(var, ifEnd, ifStart) && isVariableAssigned(var, elseEnd, elseStart)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = prev->link();
|
||||||
|
}
|
||||||
|
if (scopeStart && Token::Match(prev, "return|throw|continue|break"))
|
||||||
|
return true;
|
||||||
|
if (Token::Match(prev, "%varid% =", var->declarationId())) {
|
||||||
|
bool usedInRhs = false;
|
||||||
|
visitAstNodes(prev->next()->astOperand2(), [&usedInRhs, var](const Token *tok) {
|
||||||
|
if (tok->varId() == var->declarationId()) {
|
||||||
|
usedInRhs = true;
|
||||||
|
return ChildrenToVisit::done;
|
||||||
|
}
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
|
if (!usedInRhs)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
|
static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase)
|
||||||
{
|
{
|
||||||
if (!tok->astParent())
|
if (!tok->astParent())
|
||||||
|
@ -230,6 +269,10 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
if (Token::Match(tok, "%var% .") && tok->next()->originalName() != "->")
|
if (Token::Match(tok, "%var% .") && tok->next()->originalName() != "->")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Assume that stream object is initialized
|
||||||
|
if (Token::Match(tok->previous(), "[;{}] %var% <<|>>") && !tok->next()->astParent())
|
||||||
|
return;
|
||||||
|
|
||||||
// Containers are not uninitialized
|
// Containers are not uninitialized
|
||||||
std::vector<const Token *> tokens{tok, tok->astOperand1(), tok->astOperand2()};
|
std::vector<const Token *> tokens{tok, tok->astOperand1(), tok->astOperand2()};
|
||||||
if (Token::Match(tok->previous(), ". %name%"))
|
if (Token::Match(tok->previous(), ". %name%"))
|
||||||
|
@ -248,7 +291,7 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
if (!var->isLocal() || var->isStatic())
|
if (!var->isLocal() || var->isStatic())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (var && (Token::Match(var->nameToken(), "%name% [=:]") || Token::Match(var->nameToken(), "%varid% ; %varid% =", var->declarationId())))
|
if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || Token::Match(var->nameToken(), "%varid% ; %varid% =", var->declarationId())))
|
||||||
return;
|
return;
|
||||||
if (var && var->nameToken() == tok)
|
if (var && var->nameToken() == tok)
|
||||||
return;
|
return;
|
||||||
|
@ -256,14 +299,13 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
// Are there unconditional assignment?
|
// Are there unconditional assignment?
|
||||||
if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId()))
|
if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId()))
|
||||||
return;
|
return;
|
||||||
for (const Token *prev = tok->previous(); prev; prev = prev->previous()) {
|
|
||||||
if (!precedes(var->nameToken(), prev))
|
// Arrays are allocated on the stack
|
||||||
break;
|
if (var && Token::Match(tok, "%var% [") && var->isArray())
|
||||||
if (prev->str() == "}")
|
return;
|
||||||
prev = prev->link();
|
|
||||||
if (Token::Match(prev, "%varid% =", tok->varId()))
|
if (tok->variable() && isVariableAssigned(tok->variable(), tok))
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uninitialized function argument
|
// Uninitialized function argument
|
||||||
|
@ -318,10 +360,11 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
const std::string inconclusiveMessage(inconclusive ? ". It is inconclusive if there would be a problem in the function call." : "");
|
const std::string inconclusiveMessage(inconclusive ? ". It is inconclusive if there would be a problem in the function call." : "");
|
||||||
|
|
||||||
if (!uninitStructMember.empty()) {
|
if (!uninitStructMember.empty()) {
|
||||||
|
const std::string symbol = tok->expressionString() + "." + uninitStructMember;
|
||||||
dataBase->reportError(tok,
|
dataBase->reportError(tok,
|
||||||
Severity::SeverityType::error,
|
Severity::SeverityType::error,
|
||||||
"bughuntingUninitStructMember",
|
"bughuntingUninitStructMember",
|
||||||
"Cannot determine that '" + tok->expressionString() + "." + uninitStructMember + "' is initialized" + inconclusiveMessage,
|
"$symbol:" + symbol + "\nCannot determine that '$symbol' is initialized" + inconclusiveMessage,
|
||||||
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
||||||
inconclusive,
|
inconclusive,
|
||||||
value.type == ExprEngine::ValueType::BailoutValue);
|
value.type == ExprEngine::ValueType::BailoutValue);
|
||||||
|
@ -332,10 +375,12 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
||||||
if (uninitData)
|
if (uninitData)
|
||||||
uninitexpr += "[0]";
|
uninitexpr += "[0]";
|
||||||
|
|
||||||
|
const std::string symbol = (tok->varId() > 0) ? ("$symbol:" + tok->str() + "\n") : std::string();
|
||||||
|
|
||||||
dataBase->reportError(tok,
|
dataBase->reportError(tok,
|
||||||
Severity::SeverityType::error,
|
Severity::SeverityType::error,
|
||||||
"bughuntingUninit",
|
"bughuntingUninit",
|
||||||
"Cannot determine that '" + uninitexpr + "' is initialized" + inconclusiveMessage,
|
symbol + "Cannot determine that '" + uninitexpr + "' is initialized" + inconclusiveMessage,
|
||||||
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
||||||
inconclusive,
|
inconclusive,
|
||||||
value.type == ExprEngine::ValueType::BailoutValue);
|
value.type == ExprEngine::ValueType::BailoutValue);
|
||||||
|
|
|
@ -688,16 +688,11 @@ void CheckCondition::multiCondition2()
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
|
|
||||||
if (type == MULTICONDITIONTYPE::INNER) {
|
if (type == MULTICONDITIONTYPE::INNER) {
|
||||||
std::stack<const Token *> tokens1;
|
visitAstNodes(cond1, [&](const Token* firstCondition) {
|
||||||
tokens1.push(cond1);
|
|
||||||
while (!tokens1.empty()) {
|
|
||||||
const Token *firstCondition = tokens1.top();
|
|
||||||
tokens1.pop();
|
|
||||||
if (!firstCondition)
|
if (!firstCondition)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (firstCondition->str() == "&&") {
|
if (firstCondition->str() == "&&") {
|
||||||
tokens1.push(firstCondition->astOperand1());
|
return ChildrenToVisit::op1_and_op2;
|
||||||
tokens1.push(firstCondition->astOperand2());
|
|
||||||
} else if (!firstCondition->hasKnownIntValue()) {
|
} else if (!firstCondition->hasKnownIntValue()) {
|
||||||
if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) {
|
if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) {
|
||||||
if (!isAliased(vars))
|
if (!isAliased(vars))
|
||||||
|
@ -706,7 +701,8 @@ void CheckCondition::multiCondition2()
|
||||||
identicalInnerConditionError(firstCondition, cond2, errorPath);
|
identicalInnerConditionError(firstCondition, cond2, errorPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return ChildrenToVisit::none;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
visitAstNodes(cond2, [&](const Token *secondCondition) {
|
visitAstNodes(cond2, [&](const Token *secondCondition) {
|
||||||
if (secondCondition->str() == "||" || secondCondition->str() == "&&")
|
if (secondCondition->str() == "||" || secondCondition->str() == "&&")
|
||||||
|
@ -1436,20 +1432,15 @@ void CheckCondition::alwaysTrueFalse()
|
||||||
|
|
||||||
// Don't warn when there are expanded macros..
|
// Don't warn when there are expanded macros..
|
||||||
bool isExpandedMacro = false;
|
bool isExpandedMacro = false;
|
||||||
std::stack<const Token*> tokens;
|
visitAstNodes(tok, [&](const Token * tok2) {
|
||||||
tokens.push(tok);
|
|
||||||
while (!tokens.empty()) {
|
|
||||||
const Token *tok2 = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!tok2)
|
if (!tok2)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
tokens.push(tok2->astOperand1());
|
|
||||||
tokens.push(tok2->astOperand2());
|
|
||||||
if (tok2->isExpandedMacro()) {
|
if (tok2->isExpandedMacro()) {
|
||||||
isExpandedMacro = true;
|
isExpandedMacro = true;
|
||||||
break;
|
return ChildrenToVisit::done;
|
||||||
}
|
}
|
||||||
}
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
if (isExpandedMacro)
|
if (isExpandedMacro)
|
||||||
continue;
|
continue;
|
||||||
for (const Token *parent = tok; parent; parent = parent->astParent()) {
|
for (const Token *parent = tok; parent; parent = parent->astParent()) {
|
||||||
|
@ -1464,24 +1455,21 @@ void CheckCondition::alwaysTrueFalse()
|
||||||
// don't warn when condition checks sizeof result
|
// don't warn when condition checks sizeof result
|
||||||
bool hasSizeof = false;
|
bool hasSizeof = false;
|
||||||
bool hasNonNumber = false;
|
bool hasNonNumber = false;
|
||||||
tokens.push(tok);
|
visitAstNodes(tok, [&](const Token * tok2) {
|
||||||
while (!tokens.empty()) {
|
|
||||||
const Token *tok2 = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!tok2)
|
if (!tok2)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (tok2->isNumber())
|
if (tok2->isNumber())
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (Token::simpleMatch(tok2->previous(), "sizeof (")) {
|
if (Token::simpleMatch(tok2->previous(), "sizeof (")) {
|
||||||
hasSizeof = true;
|
hasSizeof = true;
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
}
|
}
|
||||||
if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) {
|
if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) {
|
||||||
tokens.push(tok2->astOperand1());
|
return ChildrenToVisit::op1_and_op2;
|
||||||
tokens.push(tok2->astOperand2());
|
|
||||||
} else
|
} else
|
||||||
hasNonNumber = true;
|
hasNonNumber = true;
|
||||||
}
|
return ChildrenToVisit::none;
|
||||||
|
});
|
||||||
if (!hasNonNumber && hasSizeof)
|
if (!hasNonNumber && hasSizeof)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -463,27 +463,21 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
VarInfo varInfo1(*varInfo); // VarInfo for if code
|
VarInfo varInfo1(*varInfo); // VarInfo for if code
|
||||||
VarInfo varInfo2(*varInfo); // VarInfo for else code
|
VarInfo varInfo2(*varInfo); // VarInfo for else code
|
||||||
|
|
||||||
// Recursively scan variable comparisons in condition
|
|
||||||
std::stack<const Token *> tokens;
|
|
||||||
// Skip expressions before commas
|
// Skip expressions before commas
|
||||||
const Token * astOperand2AfterCommas = tok->next()->astOperand2();
|
const Token * astOperand2AfterCommas = tok->next()->astOperand2();
|
||||||
while (Token::simpleMatch(astOperand2AfterCommas, ","))
|
while (Token::simpleMatch(astOperand2AfterCommas, ","))
|
||||||
astOperand2AfterCommas = astOperand2AfterCommas->astOperand2();
|
astOperand2AfterCommas = astOperand2AfterCommas->astOperand2();
|
||||||
tokens.push(astOperand2AfterCommas);
|
|
||||||
while (!tokens.empty()) {
|
// Recursively scan variable comparisons in condition
|
||||||
const Token *tok3 = tokens.top();
|
visitAstNodes(astOperand2AfterCommas, [&](const Token *tok3) {
|
||||||
tokens.pop();
|
|
||||||
if (!tok3)
|
if (!tok3)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (tok3->str() == "&&" || tok3->str() == "||") {
|
if (tok3->str() == "&&" || tok3->str() == "||") {
|
||||||
// FIXME: handle && ! || better
|
// FIXME: handle && ! || better
|
||||||
tokens.push(tok3->astOperand1());
|
return ChildrenToVisit::op1_and_op2;
|
||||||
tokens.push(tok3->astOperand2());
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
|
if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
|
||||||
tokens.push(tok3->astOperand2());
|
return ChildrenToVisit::op2;
|
||||||
continue;
|
|
||||||
} else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) {
|
} else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) {
|
||||||
const std::vector<const Token *> params = getArguments(tok3->previous());
|
const std::vector<const Token *> params = getArguments(tok3->previous());
|
||||||
for (const Token *par : params) {
|
for (const Token *par : params) {
|
||||||
|
@ -500,7 +494,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
varInfo2.erase(vartok->varId());
|
varInfo2.erase(vartok->varId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token *vartok = nullptr;
|
const Token *vartok = nullptr;
|
||||||
|
@ -517,7 +511,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
||||||
} else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) {
|
} else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) {
|
||||||
varInfo1.erase(vartok->varId());
|
varInfo1.erase(vartok->varId());
|
||||||
}
|
}
|
||||||
}
|
return ChildrenToVisit::none;
|
||||||
|
});
|
||||||
|
|
||||||
checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount);
|
checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount);
|
||||||
closingParenthesis = closingParenthesis->linkAt(1);
|
closingParenthesis = closingParenthesis->linkAt(1);
|
||||||
|
|
|
@ -2394,12 +2394,25 @@ void CheckStl::useStlAlgorithm()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isMutex(const Variable* var)
|
||||||
|
{
|
||||||
|
const Token* tok = Token::typeDecl(var->nameToken()).first;
|
||||||
|
return Token::Match(tok, "std :: mutex|recursive_mutex|timed_mutex|recursive_timed_mutex|shared_mutex");
|
||||||
|
}
|
||||||
|
|
||||||
static bool isLockGuard(const Variable* var)
|
static bool isLockGuard(const Variable* var)
|
||||||
{
|
{
|
||||||
const Token* tok = Token::typeDecl(var->nameToken()).first;
|
const Token* tok = Token::typeDecl(var->nameToken()).first;
|
||||||
return Token::Match(tok, "std :: lock_guard|unique_lock|scoped_lock");
|
return Token::Match(tok, "std :: lock_guard|unique_lock|scoped_lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isLocalMutex(const Variable* var, const Scope* scope)
|
||||||
|
{
|
||||||
|
if (!var)
|
||||||
|
return false;
|
||||||
|
return !var->isReference() && !var->isRValueReference() && !var->isStatic() && var->scope() == scope;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckStl::globalLockGuardError(const Token* tok)
|
void CheckStl::globalLockGuardError(const Token* tok)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::warning,
|
reportError(tok, Severity::warning,
|
||||||
|
@ -2407,19 +2420,42 @@ void CheckStl::globalLockGuardError(const Token* tok)
|
||||||
"Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program.", CWE833, false);
|
"Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program.", CWE833, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckStl::localMutexError(const Token* tok)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::warning,
|
||||||
|
"localMutex",
|
||||||
|
"The lock is ineffective because the mutex is locked at the same scope as the mutex itself.", CWE667, false);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckStl::checkMutexes()
|
void CheckStl::checkMutexes()
|
||||||
{
|
{
|
||||||
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||||
|
std::set<nonneg int> checkedVars;
|
||||||
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
|
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
|
||||||
|
if (!Token::Match(tok, "%var%"))
|
||||||
|
continue;
|
||||||
const Variable* var = tok->variable();
|
const Variable* var = tok->variable();
|
||||||
if (!var)
|
if (!var)
|
||||||
continue;
|
continue;
|
||||||
if (Token::Match(tok, "%var% (|{ %var% )|}|,")) {
|
if (Token::Match(tok, "%var% . lock ( )")) {
|
||||||
|
if (!isMutex(var))
|
||||||
|
continue;
|
||||||
|
if (!checkedVars.insert(var->declarationId()).second)
|
||||||
|
continue;
|
||||||
|
if (isLocalMutex(var, tok->scope()))
|
||||||
|
localMutexError(tok);
|
||||||
|
} else if (Token::Match(tok, "%var% (|{ %var% )|}|,")) {
|
||||||
if (!isLockGuard(var))
|
if (!isLockGuard(var))
|
||||||
continue;
|
continue;
|
||||||
const Variable* mvar = tok->tokAt(2)->variable();
|
const Variable* mvar = tok->tokAt(2)->variable();
|
||||||
|
if (!mvar)
|
||||||
|
continue;
|
||||||
|
if (!checkedVars.insert(mvar->declarationId()).second)
|
||||||
|
continue;
|
||||||
if (var->isStatic() || var->isGlobal())
|
if (var->isStatic() || var->isGlobal())
|
||||||
globalLockGuardError(tok);
|
globalLockGuardError(tok);
|
||||||
|
else if (isLocalMutex(mvar, tok->scope()))
|
||||||
|
localMutexError(tok);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,7 @@ private:
|
||||||
void useStlAlgorithmError(const Token *tok, const std::string &algoName);
|
void useStlAlgorithmError(const Token *tok, const std::string &algoName);
|
||||||
|
|
||||||
void globalLockGuardError(const Token *tok);
|
void globalLockGuardError(const Token *tok);
|
||||||
|
void localMutexError(const Token *tok);
|
||||||
|
|
||||||
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE {
|
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE {
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
|
@ -271,6 +272,7 @@ private:
|
||||||
c.readingEmptyStlContainerError(nullptr);
|
c.readingEmptyStlContainerError(nullptr);
|
||||||
c.useStlAlgorithmError(nullptr, "");
|
c.useStlAlgorithmError(nullptr, "");
|
||||||
c.globalLockGuardError(nullptr);
|
c.globalLockGuardError(nullptr);
|
||||||
|
c.localMutexError(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string myName() {
|
static std::string myName() {
|
||||||
|
|
|
@ -355,37 +355,32 @@ void CheckString::overlappingStrcmp()
|
||||||
continue;
|
continue;
|
||||||
std::list<const Token *> equals0;
|
std::list<const Token *> equals0;
|
||||||
std::list<const Token *> notEquals0;
|
std::list<const Token *> notEquals0;
|
||||||
std::stack<const Token *> tokens;
|
visitAstNodes(tok, [&](const Token * t) {
|
||||||
tokens.push(tok);
|
|
||||||
while (!tokens.empty()) {
|
|
||||||
const Token * const t = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!t)
|
if (!t)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (t->str() == "||") {
|
if (t->str() == "||") {
|
||||||
tokens.push(t->astOperand1());
|
return ChildrenToVisit::op1_and_op2;
|
||||||
tokens.push(t->astOperand2());
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (t->str() == "==") {
|
if (t->str() == "==") {
|
||||||
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
||||||
equals0.push_back(t->astOperand1());
|
equals0.push_back(t->astOperand1());
|
||||||
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
||||||
equals0.push_back(t->astOperand2());
|
equals0.push_back(t->astOperand2());
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
}
|
}
|
||||||
if (t->str() == "!=") {
|
if (t->str() == "!=") {
|
||||||
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
||||||
notEquals0.push_back(t->astOperand1());
|
notEquals0.push_back(t->astOperand1());
|
||||||
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
||||||
notEquals0.push_back(t->astOperand2());
|
notEquals0.push_back(t->astOperand2());
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
}
|
}
|
||||||
if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "("))
|
if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "("))
|
||||||
equals0.push_back(t->astOperand1());
|
equals0.push_back(t->astOperand1());
|
||||||
else if (t->str() == "(")
|
else if (t->str() == "(")
|
||||||
notEquals0.push_back(t);
|
notEquals0.push_back(t);
|
||||||
}
|
return ChildrenToVisit::none;
|
||||||
|
});
|
||||||
|
|
||||||
for (const Token *eq0 : equals0) {
|
for (const Token *eq0 : equals0) {
|
||||||
for (const Token * ne0 : notEquals0) {
|
for (const Token * ne0 : notEquals0) {
|
||||||
|
|
|
@ -239,12 +239,8 @@ void CheckType::checkSignConversion()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if an operand can be negative..
|
// Check if an operand can be negative..
|
||||||
std::stack<const Token *> tokens;
|
const Token * astOperands[] = { tok->astOperand1(), tok->astOperand2() };
|
||||||
tokens.push(tok->astOperand1());
|
for (const Token * tok1 : astOperands) {
|
||||||
tokens.push(tok->astOperand2());
|
|
||||||
while (!tokens.empty()) {
|
|
||||||
const Token *tok1 = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!tok1)
|
if (!tok1)
|
||||||
continue;
|
continue;
|
||||||
const ValueFlow::Value *negativeValue = tok1->getValueLE(-1,mSettings);
|
const ValueFlow::Value *negativeValue = tok1->getValueLE(-1,mSettings);
|
||||||
|
|
|
@ -881,22 +881,17 @@ bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const
|
||||||
usetok = tok;
|
usetok = tok;
|
||||||
else if (tok->strAt(1) == "=") {
|
else if (tok->strAt(1) == "=") {
|
||||||
bool varIsUsedInRhs = false;
|
bool varIsUsedInRhs = false;
|
||||||
std::stack<const Token *> tokens;
|
visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) {
|
||||||
tokens.push(tok->next()->astOperand2());
|
|
||||||
while (!tokens.empty()) {
|
|
||||||
const Token *t = tokens.top();
|
|
||||||
tokens.pop();
|
|
||||||
if (!t)
|
if (!t)
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
if (t->varId() == var.declarationId()) {
|
if (t->varId() == var.declarationId()) {
|
||||||
varIsUsedInRhs = true;
|
varIsUsedInRhs = true;
|
||||||
break;
|
return ChildrenToVisit::done;
|
||||||
}
|
}
|
||||||
if (Token::simpleMatch(t->previous(),"sizeof ("))
|
if (Token::simpleMatch(t->previous(),"sizeof ("))
|
||||||
continue;
|
return ChildrenToVisit::none;
|
||||||
tokens.push(t->astOperand1());
|
return ChildrenToVisit::op1_and_op2;
|
||||||
tokens.push(t->astOperand2());
|
});
|
||||||
}
|
|
||||||
if (!varIsUsedInRhs)
|
if (!varIsUsedInRhs)
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <functional> // std::hash
|
||||||
|
|
||||||
InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) :
|
InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type type) :
|
||||||
token(tok), errorMessage(errorMsg), type(type)
|
token(tok), errorMessage(errorMsg), type(type)
|
||||||
|
@ -58,8 +59,15 @@ InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::size_t calculateWarningHash(const TokenList *tokenList, const std::string &msg)
|
||||||
|
{
|
||||||
|
if (!tokenList)
|
||||||
|
return 0;
|
||||||
|
return std::hash<std::string> {}(msg + "\n" + tokenList->front()->stringifyList(false, true, false, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
ErrorMessage::ErrorMessage()
|
ErrorMessage::ErrorMessage()
|
||||||
: incomplete(false), severity(Severity::none), cwe(0U), inconclusive(false)
|
: incomplete(false), severity(Severity::none), cwe(0U), inconclusive(false), hash(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +78,8 @@ ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::
|
||||||
incomplete(false),
|
incomplete(false),
|
||||||
severity(severity), // severity for this error message
|
severity(severity), // severity for this error message
|
||||||
cwe(0U),
|
cwe(0U),
|
||||||
inconclusive(inconclusive)
|
inconclusive(inconclusive),
|
||||||
|
hash(0)
|
||||||
{
|
{
|
||||||
// set the summary and verbose messages
|
// set the summary and verbose messages
|
||||||
setmsg(msg);
|
setmsg(msg);
|
||||||
|
@ -85,14 +94,15 @@ ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::
|
||||||
incomplete(false),
|
incomplete(false),
|
||||||
severity(severity), // severity for this error message
|
severity(severity), // severity for this error message
|
||||||
cwe(cwe.id),
|
cwe(cwe.id),
|
||||||
inconclusive(inconclusive)
|
inconclusive(inconclusive),
|
||||||
|
hash(0)
|
||||||
{
|
{
|
||||||
// set the summary and verbose messages
|
// set the summary and verbose messages
|
||||||
setmsg(msg);
|
setmsg(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive)
|
ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const TokenList* list, Severity::SeverityType severity, const std::string& id, const std::string& msg, bool inconclusive)
|
||||||
: id(id), incomplete(false), severity(severity), cwe(0U), inconclusive(inconclusive)
|
: id(id), incomplete(false), severity(severity), cwe(0U), inconclusive(inconclusive), hash(0)
|
||||||
{
|
{
|
||||||
// Format callstack
|
// Format callstack
|
||||||
for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
|
for (std::list<const Token *>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) {
|
||||||
|
@ -126,15 +136,22 @@ ErrorMessage::ErrorMessage(const std::list<const Token*>& callstack, const Token
|
||||||
file0 = list->getFiles()[0];
|
file0 = list->getFiles()[0];
|
||||||
|
|
||||||
setmsg(msg);
|
setmsg(msg);
|
||||||
|
|
||||||
|
std::ostringstream hashWarning;
|
||||||
|
for (const Token *tok: callstack)
|
||||||
|
hashWarning << std::hex << (tok ? tok->index() : 0) << " ";
|
||||||
|
hashWarning << mShortMessage;
|
||||||
|
|
||||||
|
hash = calculateWarningHash(list, hashWarning.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive)
|
ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity::SeverityType severity, const char id[], const std::string &msg, const CWE &cwe, bool inconclusive)
|
||||||
: id(id), incomplete(false), severity(severity), cwe(cwe.id), inconclusive(inconclusive)
|
: id(id), incomplete(false), severity(severity), cwe(cwe.id), inconclusive(inconclusive)
|
||||||
{
|
{
|
||||||
// Format callstack
|
// Format callstack
|
||||||
for (ErrorPath::const_iterator it = errorPath.begin(); it != errorPath.end(); ++it) {
|
for (const ErrorPathItem& e: errorPath) {
|
||||||
const Token *tok = it->first;
|
const Token *tok = e.first;
|
||||||
const std::string &info = it->second;
|
const std::string &info = e.second;
|
||||||
|
|
||||||
// --errorlist can provide null values here
|
// --errorlist can provide null values here
|
||||||
if (tok)
|
if (tok)
|
||||||
|
@ -145,6 +162,13 @@ ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenLis
|
||||||
file0 = tokenList->getFiles()[0];
|
file0 = tokenList->getFiles()[0];
|
||||||
|
|
||||||
setmsg(msg);
|
setmsg(msg);
|
||||||
|
|
||||||
|
std::ostringstream hashWarning;
|
||||||
|
for (const ErrorPathItem &e: errorPath)
|
||||||
|
hashWarning << std::hex << (e.first ? e.first->index() : 0) << " ";
|
||||||
|
hashWarning << mShortMessage;
|
||||||
|
|
||||||
|
hash = calculateWarningHash(tokenList, hashWarning.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
|
ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
|
||||||
|
@ -173,6 +197,9 @@ ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
|
||||||
attr = errmsg->Attribute("verbose");
|
attr = errmsg->Attribute("verbose");
|
||||||
mVerboseMessage = attr ? attr : "";
|
mVerboseMessage = attr ? attr : "";
|
||||||
|
|
||||||
|
attr = errmsg->Attribute("hash");
|
||||||
|
std::istringstream(attr ? attr : "0") >> hash;
|
||||||
|
|
||||||
for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
||||||
if (std::strcmp(e->Name(),"location")==0) {
|
if (std::strcmp(e->Name(),"location")==0) {
|
||||||
const char *strfile = e->Attribute("file");
|
const char *strfile = e->Attribute("file");
|
||||||
|
@ -218,6 +245,7 @@ void ErrorMessage::setmsg(const std::string &msg)
|
||||||
Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const
|
Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const
|
||||||
{
|
{
|
||||||
Suppressions::ErrorMessage ret;
|
Suppressions::ErrorMessage ret;
|
||||||
|
ret.hash = hash;
|
||||||
ret.errorId = id;
|
ret.errorId = id;
|
||||||
if (!callStack.empty()) {
|
if (!callStack.empty()) {
|
||||||
ret.setFileName(callStack.back().getfile(false));
|
ret.setFileName(callStack.back().getfile(false));
|
||||||
|
@ -236,6 +264,7 @@ std::string ErrorMessage::serialize() const
|
||||||
oss << id.length() << " " << id;
|
oss << id.length() << " " << id;
|
||||||
oss << Severity::toString(severity).length() << " " << Severity::toString(severity);
|
oss << Severity::toString(severity).length() << " " << Severity::toString(severity);
|
||||||
oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id);
|
oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id);
|
||||||
|
oss << MathLib::toString(hash).length() << " " << MathLib::toString(hash);
|
||||||
if (inconclusive) {
|
if (inconclusive) {
|
||||||
const std::string text("inconclusive");
|
const std::string text("inconclusive");
|
||||||
oss << text.length() << " " << text;
|
oss << text.length() << " " << text;
|
||||||
|
@ -262,9 +291,9 @@ bool ErrorMessage::deserialize(const std::string &data)
|
||||||
inconclusive = false;
|
inconclusive = false;
|
||||||
callStack.clear();
|
callStack.clear();
|
||||||
std::istringstream iss(data);
|
std::istringstream iss(data);
|
||||||
std::array<std::string, 5> results;
|
std::array<std::string, 6> results;
|
||||||
std::size_t elem = 0;
|
std::size_t elem = 0;
|
||||||
while (iss.good()) {
|
while (iss.good() && elem < 6) {
|
||||||
unsigned int len = 0;
|
unsigned int len = 0;
|
||||||
if (!(iss >> len))
|
if (!(iss >> len))
|
||||||
return false;
|
return false;
|
||||||
|
@ -282,19 +311,17 @@ bool ErrorMessage::deserialize(const std::string &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
results[elem++] = temp;
|
results[elem++] = temp;
|
||||||
if (elem == 5)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem != 5)
|
if (elem != 6)
|
||||||
throw InternalError(nullptr, "Internal Error: Deserialization of error message failed");
|
throw InternalError(nullptr, "Internal Error: Deserialization of error message failed");
|
||||||
|
|
||||||
id = results[0];
|
id = results[0];
|
||||||
severity = Severity::fromString(results[1]);
|
severity = Severity::fromString(results[1]);
|
||||||
std::istringstream scwe(results[2]);
|
std::istringstream(results[2]) >> cwe.id;
|
||||||
scwe >> cwe.id;
|
std::istringstream(results[3]) >> hash;
|
||||||
mShortMessage = results[3];
|
mShortMessage = results[4];
|
||||||
mVerboseMessage = results[4];
|
mVerboseMessage = results[5];
|
||||||
|
|
||||||
unsigned int stackSize = 0;
|
unsigned int stackSize = 0;
|
||||||
if (!(iss >> stackSize))
|
if (!(iss >> stackSize))
|
||||||
|
@ -347,8 +374,6 @@ bool ErrorMessage::deserialize(const std::string &data)
|
||||||
|
|
||||||
std::string ErrorMessage::getXMLHeader()
|
std::string ErrorMessage::getXMLHeader()
|
||||||
{
|
{
|
||||||
// xml_version 1 is the default xml format
|
|
||||||
|
|
||||||
tinyxml2::XMLPrinter printer;
|
tinyxml2::XMLPrinter printer;
|
||||||
|
|
||||||
// standard xml header
|
// standard xml header
|
||||||
|
@ -403,6 +428,8 @@ std::string ErrorMessage::toXML() const
|
||||||
printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str());
|
printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str());
|
||||||
if (cwe.id)
|
if (cwe.id)
|
||||||
printer.PushAttribute("cwe", cwe.id);
|
printer.PushAttribute("cwe", cwe.id);
|
||||||
|
if (hash)
|
||||||
|
printer.PushAttribute("hash", MathLib::toString(hash).c_str());
|
||||||
if (inconclusive)
|
if (inconclusive)
|
||||||
printer.PushAttribute("inconclusive", "true");
|
printer.PushAttribute("inconclusive", "true");
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,9 @@ public:
|
||||||
CWE cwe;
|
CWE cwe;
|
||||||
bool inconclusive;
|
bool inconclusive;
|
||||||
|
|
||||||
|
/** Warning hash */
|
||||||
|
std::size_t hash;
|
||||||
|
|
||||||
/** set short and verbose messages */
|
/** set short and verbose messages */
|
||||||
void setmsg(const std::string &msg);
|
void setmsg(const std::string &msg);
|
||||||
|
|
||||||
|
|
|
@ -2241,6 +2241,10 @@ static std::string execute(const Token *start, const Token *end, Data &data)
|
||||||
if (Token::Match(tok, "[;{}]"))
|
if (Token::Match(tok, "[;{}]"))
|
||||||
data.trackProgramState(tok);
|
data.trackProgramState(tok);
|
||||||
|
|
||||||
|
if (Token::simpleMatch(tok, "__CPPCHECK_BAILOUT__ ;"))
|
||||||
|
// This is intended for testing
|
||||||
|
throw ExprEngineException(tok, "__CPPCHECK_BAILOUT__");
|
||||||
|
|
||||||
if (Token::simpleMatch(tok, "while (") && (tok->linkAt(1), ") ;") && tok->next()->astOperand1()->hasKnownIntValue() && tok->next()->astOperand1()->getKnownIntValue() == 0) {
|
if (Token::simpleMatch(tok, "while (") && (tok->linkAt(1), ") ;") && tok->next()->astOperand1()->hasKnownIntValue() && tok->next()->astOperand1()->getKnownIntValue() == 0) {
|
||||||
tok = tok->tokAt(4);
|
tok = tok->tokAt(4);
|
||||||
continue;
|
continue;
|
||||||
|
@ -2567,8 +2571,15 @@ static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data)
|
||||||
data.addConstraints(value, var.nameToken());
|
data.addConstraints(value, var.nameToken());
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (valueType->type == ValueType::Type::RECORD)
|
if (valueType->type == ValueType::Type::RECORD) {
|
||||||
return createStructVal(valueType->typeScope, var.isLocal() && !var.isStatic(), data);
|
bool init = true;
|
||||||
|
if (var.isLocal() && !var.isStatic()) {
|
||||||
|
init = valueType->typeScope &&
|
||||||
|
valueType->typeScope->definedType &&
|
||||||
|
valueType->typeScope->definedType->needInitialization != Type::NeedInitialization::False;
|
||||||
|
}
|
||||||
|
return createStructVal(valueType->typeScope, init, data);
|
||||||
|
}
|
||||||
if (valueType->smartPointerType) {
|
if (valueType->smartPointerType) {
|
||||||
auto structValue = createStructVal(valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data);
|
auto structValue = createStructVal(valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data);
|
||||||
auto size = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), 1, ~0UL);
|
auto size = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), 1, ~0UL);
|
||||||
|
|
|
@ -1033,7 +1033,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
||||||
const std::string &path = mPath;
|
const std::string &path = mPath;
|
||||||
|
|
||||||
std::list<std::string> paths;
|
std::list<std::string> paths;
|
||||||
std::list<std::string> suppressions;
|
std::list<Suppressions::Suppression> suppressions;
|
||||||
Settings temp;
|
Settings temp;
|
||||||
|
|
||||||
guiProject.analyzeAllVsConfigs.clear();
|
guiProject.analyzeAllVsConfigs.clear();
|
||||||
|
@ -1072,9 +1072,24 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
||||||
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)
|
||||||
guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr);
|
guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr);
|
||||||
else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0)
|
else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) {
|
||||||
suppressions = readXmlStringList(node, "", CppcheckXml::SuppressionElementName, nullptr);
|
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
|
||||||
else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0)
|
if (strcmp(child->Name(), CppcheckXml::SuppressionElementName) != 0)
|
||||||
|
continue;
|
||||||
|
auto read = [](const char *s, const char *def) {
|
||||||
|
return s ? s : def;
|
||||||
|
};
|
||||||
|
Suppressions::Suppression s;
|
||||||
|
s.errorId = read(child->GetText(), "");
|
||||||
|
s.fileName = read(child->Attribute("fileName"), "");
|
||||||
|
if (!s.fileName.empty())
|
||||||
|
s.fileName = joinRelativePath(path, s.fileName);
|
||||||
|
s.lineNumber = child->IntAttribute("lineNumber", Suppressions::Suppression::NO_LINE);
|
||||||
|
s.symbolName = read(child->Attribute("symbolName"), "");
|
||||||
|
std::istringstream(read(child->Attribute("hash"), "0")) >> s.hash;
|
||||||
|
suppressions.push_back(s);
|
||||||
|
}
|
||||||
|
} else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0)
|
||||||
guiProject.checkVsConfigs = readXmlStringList(node, "", CppcheckXml::VSConfigurationName, nullptr);
|
guiProject.checkVsConfigs = readXmlStringList(node, "", CppcheckXml::VSConfigurationName, nullptr);
|
||||||
else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0)
|
else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0)
|
||||||
guiProject.platform = node->GetText();
|
guiProject.platform = node->GetText();
|
||||||
|
@ -1115,7 +1130,9 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else
|
} else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0)
|
||||||
|
; // TODO
|
||||||
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
settings->basePaths = temp.basePaths;
|
settings->basePaths = temp.basePaths;
|
||||||
|
@ -1130,8 +1147,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
||||||
|
|
||||||
for (const std::string &p : paths)
|
for (const std::string &p : paths)
|
||||||
guiProject.pathNames.push_back(p);
|
guiProject.pathNames.push_back(p);
|
||||||
for (const std::string &supp : suppressions)
|
for (const Suppressions::Suppression &supp : suppressions)
|
||||||
settings->nomsg.addSuppressionLine(supp);
|
settings->nomsg.addSuppression(supp);
|
||||||
settings->checkHeaders = temp.checkHeaders;
|
settings->checkHeaders = temp.checkHeaders;
|
||||||
settings->checkUnusedTemplates = temp.checkUnusedTemplates;
|
settings->checkUnusedTemplates = temp.checkUnusedTemplates;
|
||||||
settings->maxCtuDepth = temp.maxCtuDepth;
|
settings->maxCtuDepth = temp.maxCtuDepth;
|
||||||
|
|
|
@ -161,6 +161,10 @@ namespace CppcheckXml {
|
||||||
const char ToolsElementName[] = "tools";
|
const char ToolsElementName[] = "tools";
|
||||||
const char TagsElementName[] = "tags";
|
const char TagsElementName[] = "tags";
|
||||||
const char TagElementName[] = "tag";
|
const char TagElementName[] = "tag";
|
||||||
|
const char TagWarningsElementName[] = "tag-warnings";
|
||||||
|
const char TagAttributeName[] = "tag";
|
||||||
|
const char WarningElementName[] = "warning";
|
||||||
|
const char HashAttributeName[] = "hash";
|
||||||
const char CheckHeadersElementName[] = "check-headers";
|
const char CheckHeadersElementName[] = "check-headers";
|
||||||
const char CheckUnusedTemplatesElementName[] = "check-unused-templates";
|
const char CheckUnusedTemplatesElementName[] = "check-unused-templates";
|
||||||
const char MaxCtuDepthElementName[] = "max-ctu-depth";
|
const char MaxCtuDepthElementName[] = "max-ctu-depth";
|
||||||
|
|
|
@ -85,7 +85,7 @@ std::string Suppressions::parseXmlFile(const char *filename)
|
||||||
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
|
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
|
||||||
for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
||||||
if (std::strcmp(e->Name(), "suppress") != 0)
|
if (std::strcmp(e->Name(), "suppress") != 0)
|
||||||
return "Invalid suppression xml file format, expected <suppress> element but got a <" + std::string(e->Name()) + '>';
|
return "Invalid suppression xml file format, expected <suppress> element but got a \"" + std::string(e->Name()) + '\"';
|
||||||
|
|
||||||
Suppression s;
|
Suppression s;
|
||||||
for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) {
|
for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) {
|
||||||
|
@ -98,8 +98,10 @@ std::string Suppressions::parseXmlFile(const char *filename)
|
||||||
s.lineNumber = std::atoi(text);
|
s.lineNumber = std::atoi(text);
|
||||||
else if (std::strcmp(e2->Name(), "symbolName") == 0)
|
else if (std::strcmp(e2->Name(), "symbolName") == 0)
|
||||||
s.symbolName = text;
|
s.symbolName = text;
|
||||||
|
else if (*text && std::strcmp(e2->Name(), "hash") == 0)
|
||||||
|
std::istringstream(text) >> s.hash;
|
||||||
else
|
else
|
||||||
return "Unknown suppression element <" + std::string(e2->Name()) + ">, expected <id>/<fileName>/<lineNumber>/<symbolName>";
|
return "Unknown suppression element \"" + std::string(e2->Name()) + "\", expected id/fileName/lineNumber/symbolName/hash";
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string err = addSuppression(s);
|
const std::string err = addSuppression(s);
|
||||||
|
@ -204,9 +206,9 @@ std::string Suppressions::addSuppressionLine(const std::string &line)
|
||||||
std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression)
|
std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression)
|
||||||
{
|
{
|
||||||
// Check that errorId is valid..
|
// Check that errorId is valid..
|
||||||
if (suppression.errorId.empty()) {
|
if (suppression.errorId.empty() && suppression.hash == 0)
|
||||||
return "Failed to add suppression. No id.";
|
return "Failed to add suppression. No id.";
|
||||||
}
|
|
||||||
if (suppression.errorId != "*") {
|
if (suppression.errorId != "*") {
|
||||||
for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) {
|
for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) {
|
||||||
if (suppression.errorId[pos] < 0 || !isAcceptedErrorIdChar(suppression.errorId[pos])) {
|
if (suppression.errorId[pos] < 0 || !isAcceptedErrorIdChar(suppression.errorId[pos])) {
|
||||||
|
@ -271,6 +273,8 @@ bool Suppressions::Suppression::parseComment(std::string comment, std::string *e
|
||||||
|
|
||||||
bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const
|
bool Suppressions::Suppression::isSuppressed(const Suppressions::ErrorMessage &errmsg) const
|
||||||
{
|
{
|
||||||
|
if (hash > 0 && hash != errmsg.hash)
|
||||||
|
return false;
|
||||||
if (!errorId.empty() && !matchglob(errorId, errmsg.errorId))
|
if (!errorId.empty() && !matchglob(errorId, errmsg.errorId))
|
||||||
return false;
|
return false;
|
||||||
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
|
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
|
||||||
|
@ -315,6 +319,8 @@ std::string Suppressions::Suppression::getText() const
|
||||||
ret += " lineNumber=" + MathLib::toString(lineNumber);
|
ret += " lineNumber=" + MathLib::toString(lineNumber);
|
||||||
if (!symbolName.empty())
|
if (!symbolName.empty())
|
||||||
ret += " symbolName=" + symbolName;
|
ret += " symbolName=" + symbolName;
|
||||||
|
if (hash > 0)
|
||||||
|
ret += " hash=" + MathLib::toString(hash);
|
||||||
if (ret.compare(0,1," ")==0)
|
if (ret.compare(0,1," ")==0)
|
||||||
return ret.substr(1);
|
return ret.substr(1);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -358,19 +364,21 @@ void Suppressions::dump(std::ostream & out) const
|
||||||
out << " lineNumber=\"" << suppression.lineNumber << '"';
|
out << " lineNumber=\"" << suppression.lineNumber << '"';
|
||||||
if (!suppression.symbolName.empty())
|
if (!suppression.symbolName.empty())
|
||||||
out << " symbolName=\"" << ErrorLogger::toxml(suppression.symbolName) << '\"';
|
out << " symbolName=\"" << ErrorLogger::toxml(suppression.symbolName) << '\"';
|
||||||
|
if (suppression.hash > 0)
|
||||||
|
out << " hash=\"" << suppression.hash << '\"';
|
||||||
out << " />" << std::endl;
|
out << " />" << std::endl;
|
||||||
}
|
}
|
||||||
out << " </suppressions>" << std::endl;
|
out << " </suppressions>" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
std::list<Suppressions::Suppression> Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
|
std::list<Suppressions::Suppression> Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
|
||||||
{
|
{
|
||||||
std::list<Suppression> result;
|
std::list<Suppression> result;
|
||||||
for (const Suppression &s : mSuppressions) {
|
for (const Suppression &s : mSuppressions) {
|
||||||
if (s.matched)
|
if (s.matched)
|
||||||
continue;
|
continue;
|
||||||
|
if (s.hash > 0)
|
||||||
|
continue;
|
||||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||||
continue;
|
continue;
|
||||||
if (file.empty() || !s.isLocal() || s.fileName != file)
|
if (file.empty() || !s.isLocal() || s.fileName != file)
|
||||||
|
@ -386,6 +394,8 @@ std::list<Suppressions::Suppression> Suppressions::getUnmatchedGlobalSuppression
|
||||||
for (const Suppression &s : mSuppressions) {
|
for (const Suppression &s : mSuppressions) {
|
||||||
if (s.matched)
|
if (s.matched)
|
||||||
continue;
|
continue;
|
||||||
|
if (s.hash > 0)
|
||||||
|
continue;
|
||||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||||
continue;
|
continue;
|
||||||
if (s.isLocal())
|
if (s.isLocal())
|
||||||
|
|
|
@ -35,6 +35,7 @@ class CPPCHECKLIB Suppressions {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct CPPCHECKLIB ErrorMessage {
|
struct CPPCHECKLIB ErrorMessage {
|
||||||
|
std::size_t hash;
|
||||||
std::string errorId;
|
std::string errorId;
|
||||||
void setFileName(const std::string &s);
|
void setFileName(const std::string &s);
|
||||||
const std::string &getFileName() const {
|
const std::string &getFileName() const {
|
||||||
|
@ -48,17 +49,18 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CPPCHECKLIB Suppression {
|
struct CPPCHECKLIB Suppression {
|
||||||
Suppression() : lineNumber(NO_LINE), matched(false) {}
|
Suppression() : lineNumber(NO_LINE), hash(0), matched(false) {}
|
||||||
Suppression(const Suppression &other) {
|
Suppression(const Suppression &other) {
|
||||||
*this = other;
|
*this = other;
|
||||||
}
|
}
|
||||||
Suppression(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), matched(false) {}
|
Suppression(const std::string &id, const std::string &file, int line=NO_LINE) : errorId(id), fileName(file), lineNumber(line), hash(0), matched(false) {}
|
||||||
|
|
||||||
Suppression & operator=(const Suppression &other) {
|
Suppression & operator=(const Suppression &other) {
|
||||||
errorId = other.errorId;
|
errorId = other.errorId;
|
||||||
fileName = other.fileName;
|
fileName = other.fileName;
|
||||||
lineNumber = other.lineNumber;
|
lineNumber = other.lineNumber;
|
||||||
symbolName = other.symbolName;
|
symbolName = other.symbolName;
|
||||||
|
hash = other.hash;
|
||||||
matched = other.matched;
|
matched = other.matched;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -72,6 +74,8 @@ public:
|
||||||
return fileName < other.fileName;
|
return fileName < other.fileName;
|
||||||
if (symbolName != other.symbolName)
|
if (symbolName != other.symbolName)
|
||||||
return symbolName < other.symbolName;
|
return symbolName < other.symbolName;
|
||||||
|
if (hash != other.hash)
|
||||||
|
return hash < other.hash;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +100,7 @@ public:
|
||||||
std::string fileName;
|
std::string fileName;
|
||||||
int lineNumber;
|
int lineNumber;
|
||||||
std::string symbolName;
|
std::string symbolName;
|
||||||
|
std::size_t hash;
|
||||||
bool matched;
|
bool matched;
|
||||||
|
|
||||||
enum { NO_LINE = -1 };
|
enum { NO_LINE = -1 };
|
||||||
|
|
|
@ -9624,11 +9624,16 @@ void Tokenizer::findGarbageCode() const
|
||||||
if (match1 && match2)
|
if (match1 && match2)
|
||||||
syntaxError(tok);
|
syntaxError(tok);
|
||||||
}
|
}
|
||||||
if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|% )|]|}")) {
|
if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|%")) {
|
||||||
if (isC())
|
std::string code = "";
|
||||||
syntaxError(tok, tok->str() + tok->next()->str());
|
if (Token::Match(tok->next(), ")|]|}"))
|
||||||
if (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator"))
|
code = tok->str() + tok->next()->str();
|
||||||
syntaxError(tok, tok->str() + " " + tok->next()->str());
|
if (Token::simpleMatch(tok->next(), "( )"))
|
||||||
|
code = tok->str() + "()";
|
||||||
|
if (!code.empty()) {
|
||||||
|
if (isC() || (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator")))
|
||||||
|
syntaxError(tok, code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%"))
|
if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%"))
|
||||||
syntaxError(tok);
|
syntaxError(tok);
|
||||||
|
|
|
@ -1387,6 +1387,18 @@ static Token * createAstAtToken(Token *tok, bool cpp)
|
||||||
init1 = tok2;
|
init1 = tok2;
|
||||||
AST_state state1(cpp);
|
AST_state state1(cpp);
|
||||||
compileExpression(tok2, state1);
|
compileExpression(tok2, state1);
|
||||||
|
if (init1->str() == "(") {
|
||||||
|
for (Token *tok3 = init1; tok3 != tok3->link(); tok3 = tok3->next()) {
|
||||||
|
if (tok3->astParent()) {
|
||||||
|
while (tok3->astParent())
|
||||||
|
tok3 = tok3->astParent();
|
||||||
|
init1 = tok3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!Token::Match(tok3, "%op%|(|["))
|
||||||
|
init1 = tok3;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
while (tok2 && tok2 != endPar && tok2->str() != ";") {
|
while (tok2 && tok2 != endPar && tok2->str() != ";") {
|
||||||
if (tok2->str() == "<" && tok2->link()) {
|
if (tok2->str() == "<" && tok2->link()) {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
# To install required tools in debian:
|
# To install required tools in debian:
|
||||||
# sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-latex-extra
|
# sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-latex-extra
|
||||||
|
|
||||||
pandoc manual.md -o manual.pdf -s --number-sections --toc
|
pandoc manual.md -o manual.pdf -s --number-sections --toc --css manual.css
|
||||||
pandoc manual.md -o manual.html -s --number-sections --toc
|
pandoc manual.md -o manual.html -s --number-sections --toc --css manual.css
|
||||||
|
|
||||||
pandoc reference-cfg-format.md -o reference-cfg-format.pdf -s --number-sections --toc
|
pandoc reference-cfg-format.md -o reference-cfg-format.pdf -s --number-sections --toc
|
||||||
pandoc reference-cfg-format.md -o reference-cfg-format.html -s --number-sections --toc
|
pandoc reference-cfg-format.md -o reference-cfg-format.html -s --number-sections --toc
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* stylelint-disable */
|
||||||
|
html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}
|
||||||
|
/* stylelint-enable */
|
||||||
|
|
||||||
|
html {
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
max-width: 1012px;
|
||||||
|
margin: 20px auto;
|
||||||
|
border: 1px solid #eaecef;
|
||||||
|
padding: 20px 100px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0366d6;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4, h5, h6 {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2 {
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
border-bottom: 1px solid #eaecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
border-radius: 6px;
|
||||||
|
line-height: 1.45;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
font-size: 100%;
|
||||||
|
word-break: normal;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol, ul {
|
||||||
|
padding-left: 2em;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
|
@ -175,8 +175,9 @@ As you can see Cppcheck has instantiated `a<i+1>` until `a<101>` was reached
|
||||||
and then it bails out.
|
and then it bails out.
|
||||||
|
|
||||||
To limit template recursion you can;
|
To limit template recursion you can;
|
||||||
* add template specialisation
|
|
||||||
* configure cppcheck (in the GUI project file dialog)
|
- add template specialisation
|
||||||
|
- configure cppcheck (in the GUI project file dialog)
|
||||||
|
|
||||||
Example code with template specialisation:
|
Example code with template specialisation:
|
||||||
|
|
||||||
|
@ -348,14 +349,15 @@ Use `--std` on the command line to specify a C/C++ standard.
|
||||||
Cppcheck assumes that the code is compatible with the latest C/C++ standard but you can override this.
|
Cppcheck assumes that the code is compatible with the latest C/C++ standard but you can override this.
|
||||||
|
|
||||||
The available options are:
|
The available options are:
|
||||||
* c89: C code is C89 compatible
|
|
||||||
* c99: C code is C99 compatible
|
- c89: C code is C89 compatible
|
||||||
* c11: C code is C11 compatible (default)
|
- c99: C code is C99 compatible
|
||||||
* c++03: C++ code is C++03 compatible
|
- c11: C code is C11 compatible (default)
|
||||||
* c++11: C++ code is C++11 compatible
|
- c++03: C++ code is C++03 compatible
|
||||||
* c++14: C++ code is C++14 compatible
|
- c++11: C++ code is C++11 compatible
|
||||||
* c++17: C++ code is C++17 compatible
|
- c++14: C++ code is C++14 compatible
|
||||||
* c++20: C++ code is C++20 compatible (default)
|
- c++17: C++ code is C++17 compatible
|
||||||
|
- c++20: C++ code is C++20 compatible (default)
|
||||||
|
|
||||||
# Suppressions
|
# Suppressions
|
||||||
|
|
||||||
|
@ -873,10 +875,11 @@ This analysis is "soundy"; it should diagnose most bugs reported in CVEs and fro
|
||||||
You have to expect false alarms. However Cppcheck tries to limit false alarms. The purpose of the data flow analysis is to limit false alarms.
|
You have to expect false alarms. However Cppcheck tries to limit false alarms. The purpose of the data flow analysis is to limit false alarms.
|
||||||
|
|
||||||
Some possible use cases;
|
Some possible use cases;
|
||||||
* you are writing new code and want to ensure it is safe.
|
|
||||||
* you are reviewing code and want to get hints about possible UB.
|
- you are writing new code and want to ensure it is safe.
|
||||||
* you need extra help troubleshooting a weird bug.
|
- you are reviewing code and want to get hints about possible UB.
|
||||||
* you want to check if a release candidate is safe.
|
- you need extra help troubleshooting a weird bug.
|
||||||
|
- you want to check if a release candidate is safe.
|
||||||
|
|
||||||
The intention is that this will be used primarily in the GUI.
|
The intention is that this will be used primarily in the GUI.
|
||||||
|
|
||||||
|
@ -923,8 +926,9 @@ Cppcheck will warn:
|
||||||
## Adding a contract in the GUI
|
## Adding a contract in the GUI
|
||||||
|
|
||||||
There are two ways:
|
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.
|
|
||||||
* 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.
|
- Open the "Contracts" tab at the bottom of the screen. Find the function 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
|
## Incomplete analysis
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ private:
|
||||||
TEST_CASE(uninit_array);
|
TEST_CASE(uninit_array);
|
||||||
TEST_CASE(uninit_function_par);
|
TEST_CASE(uninit_function_par);
|
||||||
TEST_CASE(uninit_malloc);
|
TEST_CASE(uninit_malloc);
|
||||||
|
TEST_CASE(uninit_struct);
|
||||||
|
TEST_CASE(uninit_bailout);
|
||||||
TEST_CASE(ctu);
|
TEST_CASE(ctu);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,33 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that '*p' is initialized\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that '*p' is initialized\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uninit_struct() {
|
||||||
|
// Assume that constructors initialize all members
|
||||||
|
// TODO whole program analysis
|
||||||
|
check("struct Data { Data(); int x; }\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" Data data;\n"
|
||||||
|
" x = data.x;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninit_bailout() {
|
||||||
|
check("void foo() {\n"
|
||||||
|
" __CPPCHECK_BAILOUT__;\n"
|
||||||
|
" int values[5];\n"
|
||||||
|
" values[i] = 123;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo() {\n"
|
||||||
|
" __CPPCHECK_BAILOUT__;\n"
|
||||||
|
" std::ostringstream comm;\n"
|
||||||
|
" comm << 123;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void ctu() {
|
void ctu() {
|
||||||
check("void init(int &x) {\n"
|
check("void init(int &x) {\n"
|
||||||
" x = 1;\n"
|
" x = 1;\n"
|
||||||
|
|
|
@ -252,6 +252,7 @@ private:
|
||||||
ASSERT_EQUALS("7 errorId"
|
ASSERT_EQUALS("7 errorId"
|
||||||
"5 error"
|
"5 error"
|
||||||
"1 0"
|
"1 0"
|
||||||
|
"1 0"
|
||||||
"12 inconclusive"
|
"12 inconclusive"
|
||||||
"17 Programming error"
|
"17 Programming error"
|
||||||
"17 Programming error"
|
"17 Programming error"
|
||||||
|
@ -278,6 +279,7 @@ private:
|
||||||
ASSERT_EQUALS("7 errorId"
|
ASSERT_EQUALS("7 errorId"
|
||||||
"5 error"
|
"5 error"
|
||||||
"1 0"
|
"1 0"
|
||||||
|
"1 0"
|
||||||
"33 Illegal character in \"foo\\001bar\""
|
"33 Illegal character in \"foo\\001bar\""
|
||||||
"33 Illegal character in \"foo\\001bar\""
|
"33 Illegal character in \"foo\\001bar\""
|
||||||
"0 ", msg.serialize());
|
"0 ", msg.serialize());
|
||||||
|
|
|
@ -1402,7 +1402,7 @@ private:
|
||||||
|
|
||||||
void garbageCode164() {
|
void garbageCode164() {
|
||||||
//7234
|
//7234
|
||||||
checkCode("class d{k p;}(){d::d():B<()}");
|
ASSERT_THROW(checkCode("class d{k p;}(){d::d():B<()}"), InternalError);
|
||||||
}
|
}
|
||||||
|
|
||||||
void garbageCode165() {
|
void garbageCode165() {
|
||||||
|
|
|
@ -4479,6 +4479,28 @@ private:
|
||||||
true);
|
true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" std::lock_guard<std::mutex> g(m);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" std::unique_lock<std::mutex> g(m);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" std::unique_lock<std::mutex> g(m, std::defer_lock);\n"
|
||||||
|
" std::lock(g);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
check("void g();\n"
|
check("void g();\n"
|
||||||
"void f() {\n"
|
"void f() {\n"
|
||||||
" static std::mutex m;\n"
|
" static std::mutex m;\n"
|
||||||
|
@ -4489,6 +4511,16 @@ private:
|
||||||
true);
|
true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void g();\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" m.lock();\n"
|
||||||
|
" g();\n"
|
||||||
|
" m.unlock();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
check("class A {\n"
|
check("class A {\n"
|
||||||
" std::mutex m;\n"
|
" std::mutex m;\n"
|
||||||
" void f() {\n"
|
" void f() {\n"
|
||||||
|
@ -4537,6 +4569,55 @@ private:
|
||||||
"}\n",
|
"}\n",
|
||||||
true);
|
true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("std::mutex& h();\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" auto m = h();\n"
|
||||||
|
" std::lock_guard<std::mutex> g(m);\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:4]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
|
check("void g();\n"
|
||||||
|
"std::mutex& h();\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" auto m = h();\n"
|
||||||
|
" m.lock();\n"
|
||||||
|
" g();\n"
|
||||||
|
" m.unlock();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (warning) The lock is ineffective because the mutex is locked at the same scope as the mutex itself.\n", errout.str());
|
||||||
|
|
||||||
|
check("void foo();\n"
|
||||||
|
"void bar();\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" std::thread t([&m](){\n"
|
||||||
|
" m.lock();\n"
|
||||||
|
" foo();\n"
|
||||||
|
" m.unlock();\n"
|
||||||
|
" });\n"
|
||||||
|
" m.lock();\n"
|
||||||
|
" bar();\n"
|
||||||
|
" m.unlock();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("void foo();\n"
|
||||||
|
"void bar();\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" std::mutex m;\n"
|
||||||
|
" std::thread t([&m](){\n"
|
||||||
|
" std::unique_lock<std::mutex> g{m};\n"
|
||||||
|
" foo();\n"
|
||||||
|
" });\n"
|
||||||
|
" std::unique_lock<std::mutex> g{m};\n"
|
||||||
|
" bar();\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7677,6 +7677,8 @@ private:
|
||||||
|
|
||||||
ASSERT_EQUALS("pf.pf.12,(&&", testAst("((p.f) && (p.f)(1,2))"));
|
ASSERT_EQUALS("pf.pf.12,(&&", testAst("((p.f) && (p.f)(1,2))"));
|
||||||
|
|
||||||
|
ASSERT_EQUALS("forresdirGetFirst.file&_T(,(=;;(", testAst("for ((res = dir.GetFirst(&file, _T("")));;) {}"));
|
||||||
|
|
||||||
// problems with: if (x[y]==z)
|
// problems with: if (x[y]==z)
|
||||||
ASSERT_EQUALS("ifa(0[1==(", testAst("if(a()[0]==1){}"));
|
ASSERT_EQUALS("ifa(0[1==(", testAst("if(a()[0]==1){}"));
|
||||||
ASSERT_EQUALS("ifbuff0[&(*1==(", testAst("if (*((DWORD*)&buff[0])==1){}"));
|
ASSERT_EQUALS("ifbuff0[&(*1==(", testAst("if (*((DWORD*)&buff[0])==1){}"));
|
||||||
|
@ -8087,7 +8089,7 @@ private:
|
||||||
ASSERT_THROW(tokenizeAndStringify("void foo() { for_chain( if (!done) done = 1); }"), InternalError);
|
ASSERT_THROW(tokenizeAndStringify("void foo() { for_chain( if (!done) done = 1); }"), InternalError);
|
||||||
ASSERT_THROW(tokenizeAndStringify("void foo() { for_chain( a, b, if (!done) done = 1); }"), InternalError);
|
ASSERT_THROW(tokenizeAndStringify("void foo() { for_chain( a, b, if (!done) done = 1); }"), InternalError);
|
||||||
|
|
||||||
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { if (retval==){} }"), InternalError, "syntax error: == )");
|
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { if (retval==){} }"), InternalError, "syntax error: ==)");
|
||||||
|
|
||||||
// after (expr)
|
// after (expr)
|
||||||
ASSERT_NO_THROW(tokenizeAndStringify("void f() { switch (a) int b; }"));
|
ASSERT_NO_THROW(tokenizeAndStringify("void f() { switch (a) int b; }"));
|
||||||
|
@ -8108,6 +8110,9 @@ private:
|
||||||
|
|
||||||
// Ticket #9664
|
// Ticket #9664
|
||||||
ASSERT_NO_THROW(tokenizeAndStringify("S s = { .x { 2 }, .y[0] { 3 } };"));
|
ASSERT_NO_THROW(tokenizeAndStringify("S s = { .x { 2 }, .y[0] { 3 } };"));
|
||||||
|
|
||||||
|
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a==()); }"), InternalError, "syntax error: ==()");
|
||||||
|
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a+()); }"), InternalError, "syntax error: +()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -252,6 +252,9 @@ private:
|
||||||
check("x = -4 * (unsigned)y;");
|
check("x = -4 * (unsigned)y;");
|
||||||
ASSERT_EQUALS("[test.cpp:1]: (warning) Expression '-4' has a negative value. That is converted to an unsigned value and used in an unsigned calculation.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1]: (warning) Expression '-4' has a negative value. That is converted to an unsigned value and used in an unsigned calculation.\n", errout.str());
|
||||||
|
|
||||||
|
check("x = (unsigned)y * -4;");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (warning) Expression '-4' has a negative value. That is converted to an unsigned value and used in an unsigned calculation.\n", errout.str());
|
||||||
|
|
||||||
check("unsigned int dostuff(int x) {\n" // x is signed
|
check("unsigned int dostuff(int x) {\n" // x is signed
|
||||||
" if (x==0) {}\n"
|
" if (x==0) {}\n"
|
||||||
" return (x-1)*sizeof(int);\n"
|
" return (x-1)*sizeof(int);\n"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<Include>
|
<Include>
|
||||||
<?define CliBuildDir = "..\bin" ?>
|
<?define CliBuildDir = "..\bin" ?>
|
||||||
<?define GuiBuildDir = "..\bin" ?>
|
<?define GuiBuildDir = "..\bin" ?>
|
||||||
|
<?define GuiHelpDir = "..\gui\help" ?>
|
||||||
<?define TranslationsDir = "..\gui" ?>
|
<?define TranslationsDir = "..\gui" ?>
|
||||||
<?define CfgsDir = "..\cfg" ?>
|
<?define CfgsDir = "..\cfg" ?>
|
||||||
<?define PtfsDir = "..\platforms" ?>
|
<?define PtfsDir = "..\platforms" ?>
|
||||||
|
|
|
@ -55,6 +55,12 @@
|
||||||
<File Id='qwindowsvistastyledll' Name='qwindowsvistastyle.dll' Source='$(var.QtDllDir)\styles\qwindowsvistastyle.dll' />
|
<File Id='qwindowsvistastyledll' Name='qwindowsvistastyle.dll' Source='$(var.QtDllDir)\styles\qwindowsvistastyle.dll' />
|
||||||
</Component>
|
</Component>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
<Directory Id='GuiHelpFolder' Name='help'>
|
||||||
|
<Component Id='GuiHelp' DiskId='1' Guid='$(var.guihelpGUID)'>
|
||||||
|
<File Id='onlinehelpqhc' Name='online-help.qhc' Source='$(var.GuiHelpDir)\online-help.qhc' />
|
||||||
|
<File Id='onlinehelpqch' Name='online-help.qch' Source='$(var.GuiHelpDir)\online-help.qch' />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
<Directory Id='TranslationsFolder' Name='lang'>
|
<Directory Id='TranslationsFolder' Name='lang'>
|
||||||
<Component Id='GuiTranslations' Guid='$(var.guiTranslationsGUID)'>
|
<Component Id='GuiTranslations' Guid='$(var.guiTranslationsGUID)'>
|
||||||
<File Id='cppcheck_de.qm' Name='cppcheck_de.qm' Source='$(var.TranslationsDir)\cppcheck_de.qm' />
|
<File Id='cppcheck_de.qm' Name='cppcheck_de.qm' Source='$(var.TranslationsDir)\cppcheck_de.qm' />
|
||||||
|
|
|
@ -21,4 +21,5 @@
|
||||||
<?define optionalPtfsGUID = "F9EB9DB1-C0F1-4548-A5B0-27B51CD43A34" ?>
|
<?define optionalPtfsGUID = "F9EB9DB1-C0F1-4548-A5B0-27B51CD43A34" ?>
|
||||||
<?define basedocsGUID = "9F62B23C-CD2B-4826-A567-6AB6F75A2AEA" ?>
|
<?define basedocsGUID = "9F62B23C-CD2B-4826-A567-6AB6F75A2AEA" ?>
|
||||||
<?define registrykeysGUID = "8CB515B1-6EF6-4A2A-97A0-F8A13E3A505E" ?>
|
<?define registrykeysGUID = "8CB515B1-6EF6-4A2A-97A0-F8A13E3A505E" ?>
|
||||||
|
<?define guihelpGUID = "0A0C93BF-59E8-4919-B08F-D4BA63B9C93C" ?>
|
||||||
</Include>
|
</Include>
|
||||||
|
|
Loading…
Reference in New Issue