Merge branch 'main' into fp-invalid-container-pointer
This commit is contained in:
commit
abeea7b32b
|
@ -50,7 +50,7 @@ jobs:
|
|||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v2
|
||||
with:
|
||||
modules: 'qtcharts'
|
||||
modules: 'qtcharts qthelp'
|
||||
|
||||
- name: Create .qm
|
||||
run: |
|
||||
|
@ -58,6 +58,11 @@ jobs:
|
|||
lupdate gui.pro
|
||||
lrelease gui.pro -removeidentical
|
||||
|
||||
- name: Create online-help
|
||||
run: |
|
||||
cd gui\help
|
||||
qhelpgenerator online-help.qhcp -o online-help.qhc
|
||||
|
||||
- name: Matchcompiler
|
||||
run: python tools\matchcompiler.py --write-dir lib
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ env:
|
|||
before_install:
|
||||
# install needed deps
|
||||
- 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
|
||||
- travis_retry python2 -m pip install --user pytest==4.6.4
|
||||
- travis_retry python2 -m pip install --user pylint
|
||||
|
|
|
@ -1059,6 +1059,8 @@ class MisraChecker:
|
|||
|
||||
self.severity = None
|
||||
|
||||
self.existing_violations = set()
|
||||
|
||||
def __repr__(self):
|
||||
attrs = ["settings", "verify_expected", "verify_actual", "violations",
|
||||
"ruleTexts", "suppressedRules", "dumpfileSuppressions",
|
||||
|
@ -2726,6 +2728,12 @@ class MisraChecker:
|
|||
if self.severity:
|
||||
cppcheck_severity = self.severity
|
||||
|
||||
this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum)
|
||||
|
||||
# If this is new violation then record it and show it. If not then
|
||||
# skip it since it has already been displayed.
|
||||
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:
|
||||
|
|
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/>
|
||||
</arg>
|
||||
</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) -->
|
||||
<function name="wxGrid::SetTable">
|
||||
<noreturn>false</noreturn>
|
||||
|
|
|
@ -24,6 +24,13 @@
|
|||
</data>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name="hash">
|
||||
<data type="integer">
|
||||
<param name="minExclusive">1</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</optional>
|
||||
<attribute name="id">
|
||||
<data type="NCName"/>
|
||||
</attribute>
|
||||
|
|
|
@ -257,6 +257,19 @@ void CodeEditor::setError(const QString &code, int errorLine, const QStringList
|
|||
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 digits = 1;
|
||||
|
|
|
@ -77,6 +77,26 @@ public:
|
|||
*/
|
||||
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:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
|
@ -93,6 +113,7 @@ private:
|
|||
Highlighter *mHighlighter;
|
||||
CodeEditorStyle *mWidgetStyle;
|
||||
int mErrorPosition;
|
||||
QString mFileName;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ ErrorItem::ErrorItem()
|
|||
, incomplete(false)
|
||||
, inconclusive(false)
|
||||
, cwe(-1)
|
||||
, hash(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,7 @@ ErrorItem::ErrorItem(const ErrorMessage &errmsg)
|
|||
, summary(QString::fromStdString(errmsg.shortMessage()))
|
||||
, message(QString::fromStdString(errmsg.verboseMessage()))
|
||||
, cwe(errmsg.cwe.id)
|
||||
, hash(errmsg.hash)
|
||||
, symbolNames(QString::fromStdString(errmsg.symbolNames()))
|
||||
{
|
||||
for (std::list<ErrorMessage::FileLocation>::const_iterator loc = errmsg.callStack.begin();
|
||||
|
@ -70,7 +72,7 @@ QString ErrorItem::tool() const
|
|||
return "cppcheck";
|
||||
}
|
||||
|
||||
QString ErrorItem::ToString() const
|
||||
QString ErrorItem::toString() const
|
||||
{
|
||||
QString str = errorPath.back().file + " - " + errorId + " - ";
|
||||
if (inconclusive)
|
||||
|
@ -86,7 +88,10 @@ QString ErrorItem::ToString() const
|
|||
|
||||
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 &&
|
||||
errorItem1.errorPath == errorItem2.errorPath &&
|
||||
errorItem1.file0 == errorItem2.file0 &&
|
||||
|
|
|
@ -76,7 +76,7 @@ public:
|
|||
* @brief Convert error item to string.
|
||||
* @return Error item as string.
|
||||
*/
|
||||
QString ToString() const;
|
||||
QString toString() const;
|
||||
QString tool() const;
|
||||
|
||||
QString file0;
|
||||
|
@ -88,6 +88,7 @@ public:
|
|||
QString summary;
|
||||
QString message;
|
||||
int cwe;
|
||||
unsigned long long hash;
|
||||
QList<QErrorPathItem> errorPath;
|
||||
QString symbolNames;
|
||||
|
||||
|
@ -114,6 +115,7 @@ public:
|
|||
QString errorId;
|
||||
bool incomplete;
|
||||
int cwe;
|
||||
unsigned long long hash;
|
||||
bool inconclusive;
|
||||
Severity::SeverityType severity;
|
||||
QString summary;
|
||||
|
|
10
gui/gui.pro
10
gui/gui.pro
|
@ -10,6 +10,13 @@ INCLUDEPATH += . \
|
|||
../externals/z3/include
|
||||
QT += widgets
|
||||
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]) {
|
||||
LIBS += -l../bin/cppcheck-core
|
||||
|
@ -61,6 +68,7 @@ FORMS = about.ui \
|
|||
application.ui \
|
||||
file.ui \
|
||||
functioncontractdialog.ui \
|
||||
helpdialog.ui \
|
||||
mainwindow.ui \
|
||||
projectfiledialog.ui \
|
||||
resultsview.ui \
|
||||
|
@ -110,6 +118,7 @@ HEADERS += aboutdialog.h \
|
|||
filelist.h \
|
||||
fileviewdialog.h \
|
||||
functioncontractdialog.h \
|
||||
helpdialog.h \
|
||||
mainwindow.h \
|
||||
platforms.h \
|
||||
printablereport.h \
|
||||
|
@ -150,6 +159,7 @@ SOURCES += aboutdialog.cpp \
|
|||
filelist.cpp \
|
||||
fileviewdialog.cpp \
|
||||
functioncontractdialog.cpp \
|
||||
helpdialog.cpp \
|
||||
main.cpp \
|
||||
mainwindow.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>
|
||||
<filterSection>
|
||||
<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>
|
||||
<keywords/>
|
||||
<keywords>
|
||||
<keyword name="Project" ref="./projectfiledialog.html"/>
|
||||
<keyword name="Tag" ref="./tagging.html"/>
|
||||
</keywords>
|
||||
<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>
|
||||
</filterSection>
|
||||
</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 "aboutdialog.h"
|
||||
#include "common.h"
|
||||
#include "threadhandler.h"
|
||||
#include "filelist.h"
|
||||
#include "fileviewdialog.h"
|
||||
#include "functioncontractdialog.h"
|
||||
#include "helpdialog.h"
|
||||
#include "librarydialog.h"
|
||||
#include "projectfile.h"
|
||||
#include "projectfiledialog.h"
|
||||
#include "report.h"
|
||||
#include "scratchpad.h"
|
||||
#include "showtypes.h"
|
||||
#include "statsdialog.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "threadhandler.h"
|
||||
#include "threadresult.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 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::resultsHidden, mUI.mActionShowHidden, &QAction::setEnabled);
|
||||
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::editFunctionContract, this, &MainWindow::editFunctionContract);
|
||||
connect(mUI.mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu);
|
||||
|
@ -341,6 +341,7 @@ void MainWindow::loadSettings()
|
|||
if (inf.exists() && inf.isReadable()) {
|
||||
setPath(SETTINGS_LAST_PROJECT_PATH, projectFile);
|
||||
mProjectFile = new ProjectFile(this);
|
||||
mProjectFile->setActiveProject();
|
||||
mProjectFile->read(projectFile);
|
||||
loadLastResults();
|
||||
QDir::setCurrent(inf.absolutePath());
|
||||
|
@ -1465,7 +1466,8 @@ void MainWindow::openHelpContents()
|
|||
|
||||
void MainWindow::openOnlineHelp()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(OnlineHelpURL));
|
||||
HelpDialog *helpDialog = new HelpDialog;
|
||||
helpDialog->showMaximized();
|
||||
}
|
||||
|
||||
void MainWindow::openProjectFile()
|
||||
|
@ -1508,6 +1510,7 @@ void MainWindow::loadProjectFile(const QString &filePath)
|
|||
mUI.mActionEditProjectFile->setEnabled(true);
|
||||
delete mProjectFile;
|
||||
mProjectFile = new ProjectFile(filePath, this);
|
||||
mProjectFile->setActiveProject();
|
||||
updateContractsTab();
|
||||
if (!loadLastResults())
|
||||
analyzeProject(mProjectFile);
|
||||
|
@ -1522,8 +1525,6 @@ QString MainWindow::getLastResults() const
|
|||
|
||||
bool MainWindow::loadLastResults()
|
||||
{
|
||||
if (mProjectFile)
|
||||
mUI.mResults->setTags(mProjectFile->getTags());
|
||||
const QString &lastResults = getLastResults();
|
||||
if (lastResults.isEmpty())
|
||||
return false;
|
||||
|
@ -1546,7 +1547,6 @@ void MainWindow::analyzeProject(const ProjectFile *projectFile, const bool check
|
|||
QDir::setCurrent(inf.absolutePath());
|
||||
|
||||
mThread->setAddonsAndTools(projectFile->getAddonsAndTools());
|
||||
mUI.mResults->setTags(projectFile->getTags());
|
||||
|
||||
// If the root path is not given or is not "current dir", use project
|
||||
// file's location directory as root path
|
||||
|
@ -1630,6 +1630,7 @@ void MainWindow::newProjectFile()
|
|||
|
||||
delete mProjectFile;
|
||||
mProjectFile = new ProjectFile(this);
|
||||
mProjectFile->setActiveProject();
|
||||
mProjectFile->setFilename(filepath);
|
||||
mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir");
|
||||
|
||||
|
@ -1649,7 +1650,6 @@ void MainWindow::closeProjectFile()
|
|||
delete mProjectFile;
|
||||
mProjectFile = nullptr;
|
||||
mUI.mResults->clear(true);
|
||||
mUI.mResults->setTags(QStringList());
|
||||
enableProjectActions(false);
|
||||
enableProjectOpenActions(true);
|
||||
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)
|
||||
{
|
||||
if (!mProjectFile)
|
||||
|
|
|
@ -221,9 +221,6 @@ protected slots:
|
|||
/** @brief Selects the platform as analyzed platform. */
|
||||
void selectPlatform();
|
||||
|
||||
/** Some results were tagged */
|
||||
void tagged();
|
||||
|
||||
/** Suppress error ids */
|
||||
void suppressIds(QStringList ids);
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#include "settings.h"
|
||||
|
||||
ProjectFile *ProjectFile::mActiveProject;
|
||||
|
||||
ProjectFile::ProjectFile(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
@ -70,6 +72,8 @@ void ProjectFile::clear()
|
|||
mCheckUnknownFunctionReturn.clear();
|
||||
safeChecks.clear();
|
||||
mVsConfigurations.clear();
|
||||
mTags.clear();
|
||||
mWarningTags.clear();
|
||||
}
|
||||
|
||||
bool ProjectFile::read(const QString &filename)
|
||||
|
@ -186,6 +190,9 @@ bool ProjectFile::read(const QString &filename)
|
|||
if (xmlReader.name() == CppcheckXml::TagsElementName)
|
||||
readStringList(mTags, xmlReader, CppcheckXml::TagElementName);
|
||||
|
||||
if (xmlReader.name() == CppcheckXml::TagWarningsElementName)
|
||||
readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString());
|
||||
|
||||
if (xmlReader.name() == CppcheckXml::MaxCtuDepthElementName)
|
||||
mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth);
|
||||
|
||||
|
@ -603,6 +610,8 @@ void ProjectFile::readSuppressions(QXmlStreamReader &reader)
|
|||
suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt();
|
||||
if (reader.attributes().hasAttribute(QString(),"symbolName"))
|
||||
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();
|
||||
if (type == QXmlStreamReader::Characters) {
|
||||
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[])
|
||||
{
|
||||
QXmlStreamReader::TokenType type;
|
||||
|
@ -715,6 +759,11 @@ void ProjectFile::setSuppressions(const QList<Suppressions::Suppression> &suppre
|
|||
mSuppressions = suppressions;
|
||||
}
|
||||
|
||||
void ProjectFile::addSuppression(const Suppressions::Suppression &suppression)
|
||||
{
|
||||
mSuppressions.append(suppression);
|
||||
}
|
||||
|
||||
void ProjectFile::setAddons(const QStringList &addons)
|
||||
{
|
||||
mAddons = addons;
|
||||
|
@ -725,6 +774,20 @@ void ProjectFile::setVSConfigurations(const QStringList &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)
|
||||
{
|
||||
if (!filename.isEmpty())
|
||||
|
@ -873,6 +936,8 @@ bool ProjectFile::write(const QString &filename)
|
|||
xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber));
|
||||
if (!suppression.symbolName.empty())
|
||||
xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName));
|
||||
if (suppression.hash > 0)
|
||||
xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash));
|
||||
if (!suppression.errorId.empty())
|
||||
xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId));
|
||||
xmlWriter.writeEndElement();
|
||||
|
@ -903,6 +968,26 @@ bool ProjectFile::write(const QString &filename)
|
|||
CppcheckXml::ToolElementName);
|
||||
|
||||
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();
|
||||
file.close();
|
||||
|
@ -1021,4 +1106,3 @@ QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon)
|
|||
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,16 @@ class ProjectFile : public QObject {
|
|||
public:
|
||||
explicit ProjectFile(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.
|
||||
|
@ -299,6 +309,9 @@ public:
|
|||
*/
|
||||
void setSuppressions(const QList<Suppressions::Suppression> &suppressions);
|
||||
|
||||
/** Add suppression */
|
||||
void addSuppression(const Suppressions::Suppression &suppression);
|
||||
|
||||
/**
|
||||
* @brief Set list of addons.
|
||||
* @param addons List of addons.
|
||||
|
@ -318,6 +331,12 @@ public:
|
|||
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).
|
||||
* @param filename Filename to use.
|
||||
|
@ -424,6 +443,12 @@ protected:
|
|||
*/
|
||||
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
|
||||
* @param stringlist destination string list
|
||||
|
@ -539,10 +564,15 @@ private:
|
|||
bool mClangTidy;
|
||||
|
||||
/**
|
||||
* @brief Warning tags
|
||||
* @brief Tags
|
||||
*/
|
||||
QStringList mTags;
|
||||
|
||||
/**
|
||||
* @brief Warning tags
|
||||
*/
|
||||
std::map<std::size_t, QString> mWarningTags;
|
||||
|
||||
/** Max CTU depth */
|
||||
int mMaxCtuDepth;
|
||||
|
||||
|
@ -551,6 +581,7 @@ private:
|
|||
|
||||
QStringList mCheckUnknownFunctionReturn;
|
||||
|
||||
static ProjectFile *mActiveProject;
|
||||
};
|
||||
/// @}
|
||||
#endif // PROJECT_FILE_H
|
||||
|
|
|
@ -62,6 +62,8 @@ static const int numberOfBuiltinPlatforms = sizeof(builtinPlatforms) / sizeof(bu
|
|||
|
||||
QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName)
|
||||
{
|
||||
if (!fileName.endsWith(".sln") && !fileName.endsWith(".vcxproj"))
|
||||
return QStringList();
|
||||
QStringList ret;
|
||||
ImportProject importer;
|
||||
Settings projSettings;
|
||||
|
@ -199,6 +201,7 @@ ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, QWidget *parent)
|
|||
connect(mUI.mBtnEditInclude, &QPushButton::clicked, this, &ProjectFileDialog::editIncludeDir);
|
||||
connect(mUI.mBtnRemoveInclude, &QPushButton::clicked, this, &ProjectFileDialog::removeIncludeDir);
|
||||
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.mBtnRemoveIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::removeExcludePath);
|
||||
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.mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile);
|
||||
connect(mUI.mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs);
|
||||
connect(mUI.mBtnNormalAnalysis, &QCheckBox::toggled, mUI.mBtnSafeClasses, &QCheckBox::setEnabled);
|
||||
loadFromProjectFile(projectFile);
|
||||
}
|
||||
|
||||
|
@ -242,8 +246,12 @@ static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, c
|
|||
|
||||
void ProjectFileDialog::checkAllVSConfigs()
|
||||
{
|
||||
if (mUI.mChkAllVsConfigs->isChecked())
|
||||
mUI.mListVsConfigs->selectAll();
|
||||
if (mUI.mChkAllVsConfigs->isChecked()) {
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -257,6 +265,14 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
setCheckPaths(projectFile->getCheckPaths());
|
||||
setImportProject(projectFile->getImportProject());
|
||||
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.mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates());
|
||||
mUI.mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth());
|
||||
|
@ -266,7 +282,7 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
else
|
||||
mUI.mBtnCppcheckParser->setChecked(true);
|
||||
mUI.mBtnSafeClasses->setChecked(projectFile->safeChecks.classes);
|
||||
mUI.mBugHunting->setChecked(projectFile->bugHunting);
|
||||
mUI.mBtnBugHunting->setChecked(projectFile->bugHunting);
|
||||
setExcludedPaths(projectFile->getExcludedPaths());
|
||||
setLibraries(projectFile->getLibraries());
|
||||
const QString platform = projectFile->getPlatform();
|
||||
|
@ -340,17 +356,6 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile)
|
|||
}
|
||||
mUI.mEditTags->setText(projectFile->getTags().join(';'));
|
||||
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
|
||||
|
@ -359,6 +364,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
projectFile->setBuildDir(getBuildDir());
|
||||
projectFile->setImportProject(getImportProject());
|
||||
projectFile->setAnalyzeAllVsConfigs(mUI.mChkAllVsConfigs->isChecked());
|
||||
projectFile->setVSConfigurations(getProjectConfigurations());
|
||||
projectFile->setCheckHeaders(mUI.mCheckHeaders->isChecked());
|
||||
projectFile->setCheckUnusedTemplates(mUI.mCheckUnusedTemplates->isChecked());
|
||||
projectFile->setMaxCtuDepth(mUI.mMaxCtuDepth->value());
|
||||
|
@ -371,7 +377,7 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
projectFile->setLibraries(getLibraries());
|
||||
projectFile->clangParser = mUI.mBtnClangParser->isChecked();
|
||||
projectFile->safeChecks.classes = mUI.mBtnSafeClasses->isChecked();
|
||||
projectFile->bugHunting = mUI.mBugHunting->isChecked();
|
||||
projectFile->bugHunting = mUI.mBtnBugHunting->isChecked();
|
||||
if (mUI.mComboBoxPlatform->currentText().endsWith(".xml"))
|
||||
projectFile->setPlatform(mUI.mComboBoxPlatform->currentText());
|
||||
else {
|
||||
|
@ -412,7 +418,6 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const
|
|||
projectFile->setClangAnalyzer(mUI.mToolClangAnalyzer->isChecked());
|
||||
projectFile->setClangTidy(mUI.mToolClangTidy->isChecked());
|
||||
projectFile->setTags(mUI.mEditTags->text().split(";", QString::SkipEmptyParts));
|
||||
projectFile->setVSConfigurations(getVsConfigurations());
|
||||
}
|
||||
|
||||
void ProjectFileDialog::ok()
|
||||
|
@ -458,6 +463,7 @@ void ProjectFileDialog::updatePathsAndDefines()
|
|||
{
|
||||
const QString &fileName = mUI.mEditImportProject->text();
|
||||
bool importProject = !fileName.isEmpty();
|
||||
bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".vcxproj");
|
||||
mUI.mBtnClearImportProject->setEnabled(importProject);
|
||||
mUI.mListCheckPaths->setEnabled(!importProject);
|
||||
mUI.mListIncludeDirs->setEnabled(!importProject);
|
||||
|
@ -471,9 +477,9 @@ void ProjectFileDialog::updatePathsAndDefines()
|
|||
mUI.mBtnRemoveInclude->setEnabled(!importProject);
|
||||
mUI.mBtnIncludeUp->setEnabled(!importProject);
|
||||
mUI.mBtnIncludeDown->setEnabled(!importProject);
|
||||
mUI.mChkAllVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"));
|
||||
mUI.mListVsConfigs->setEnabled(fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"));
|
||||
if (!mUI.mListVsConfigs->isEnabled())
|
||||
mUI.mChkAllVsConfigs->setEnabled(hasConfigs);
|
||||
mUI.mListVsConfigs->setEnabled(hasConfigs && !mUI.mChkAllVsConfigs->isChecked());
|
||||
if (!hasConfigs)
|
||||
mUI.mListVsConfigs->clear();
|
||||
}
|
||||
|
||||
|
@ -497,24 +503,34 @@ void ProjectFileDialog::browseImportProject()
|
|||
if (!fileName.isEmpty()) {
|
||||
mUI.mEditImportProject->setText(dir.relativeFilePath(fileName));
|
||||
updatePathsAndDefines();
|
||||
setVsConfigurations(getProjectConfigs(fileName));
|
||||
mUI.mListVsConfigs->selectAll();
|
||||
setProjectConfigurations(getProjectConfigs(fileName));
|
||||
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;
|
||||
foreach (QListWidgetItem *item, mUI.mListVsConfigs->selectedItems())
|
||||
for (int row = 0; row < mUI.mListVsConfigs->count(); ++row) {
|
||||
QListWidgetItem *item = mUI.mListVsConfigs->item(row);
|
||||
if (item->checkState() == Qt::Checked)
|
||||
configs << item->text();
|
||||
|
||||
}
|
||||
return configs;
|
||||
}
|
||||
|
||||
void ProjectFileDialog::setVsConfigurations(const QStringList &configs)
|
||||
void ProjectFileDialog::setProjectConfigurations(const QStringList &configs)
|
||||
{
|
||||
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
|
||||
|
@ -717,9 +733,17 @@ void ProjectFileDialog::editIncludeDir()
|
|||
|
||||
void ProjectFileDialog::addExcludePath()
|
||||
{
|
||||
QString dir = getExistingDirectory(tr("Select directory to ignore"), true);
|
||||
if (!dir.isEmpty())
|
||||
addExcludePath(dir);
|
||||
addExcludePath(getExistingDirectory(tr("Select directory to ignore"), true));
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -802,7 +826,10 @@ int ProjectFileDialog::getSuppressionIndex(const QString &shortText) const
|
|||
|
||||
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()) {
|
||||
QSettings settings;
|
||||
mUI.mEditMisraFile->setText(fileName);
|
||||
|
|
|
@ -58,8 +58,8 @@ private:
|
|||
*/
|
||||
QString getRootPath() const;
|
||||
|
||||
QStringList getVsConfigurations() const;
|
||||
void setVsConfigurations(const QStringList &configs);
|
||||
QStringList getProjectConfigurations() const;
|
||||
void setProjectConfigurations(const QStringList &configs);
|
||||
|
||||
QString getImportProject() const;
|
||||
|
||||
|
@ -215,10 +215,15 @@ protected slots:
|
|||
void editIncludeDir();
|
||||
|
||||
/**
|
||||
* @brief Add new path to exclude.
|
||||
* @brief Add new path to exclude list.
|
||||
*/
|
||||
void addExcludePath();
|
||||
|
||||
/**
|
||||
* @brief Add new file to exclude list.
|
||||
*/
|
||||
void addExcludeFile();
|
||||
|
||||
/**
|
||||
* @brief Edit excluded path in the list.
|
||||
*/
|
||||
|
|
|
@ -130,17 +130,7 @@
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
<widget class="QListWidget" name="mListVsConfigs"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
@ -420,7 +410,11 @@
|
|||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<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>
|
||||
<widget class="QPushButton" name="mBtnBrowseBuildDir">
|
||||
|
@ -461,18 +455,31 @@
|
|||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_11">
|
||||
<property name="title">
|
||||
<string>Check that code is safe</string>
|
||||
<string>Analysis</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_19">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="mBugHunting">
|
||||
<widget class="QRadioButton" name="mBtnNormalAnalysis">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<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">
|
||||
<string>Check that each class has a safe public interface</string>
|
||||
</property>
|
||||
|
@ -605,7 +612,11 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -617,7 +628,11 @@
|
|||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<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>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -625,7 +640,7 @@
|
|||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_7">
|
||||
<property name="title">
|
||||
<string>Exclude source files in paths</string>
|
||||
<string>Exclude source files</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
|
@ -636,7 +651,14 @@
|
|||
<item>
|
||||
<widget class="QPushButton" name="mBtnAddIgnorePath">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -43,14 +43,33 @@
|
|||
#include "resultstree.h"
|
||||
#include "report.h"
|
||||
#include "application.h"
|
||||
#include "projectfile.h"
|
||||
#include "showtypes.h"
|
||||
#include "threadhandler.h"
|
||||
#include "path.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()
|
||||
static const unsigned int COLUMN_SINCE_DATE = 6;
|
||||
static const unsigned int COLUMN_TAGS = 7;
|
||||
static const int COLUMN_SINCE_DATE = 6;
|
||||
static const int COLUMN_TAGS = 7;
|
||||
|
||||
static QString getFunction(QStandardItem *item)
|
||||
{
|
||||
|
@ -168,12 +187,15 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
|||
line.errorId = item.errorId;
|
||||
line.incomplete = item.incomplete;
|
||||
line.cwe = item.cwe;
|
||||
line.hash = item.hash;
|
||||
line.inconclusive = item.inconclusive;
|
||||
line.summary = item.summary;
|
||||
line.message = item.message;
|
||||
line.severity = item.severity;
|
||||
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
|
||||
//file item as a parent
|
||||
QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide);
|
||||
|
@ -188,21 +210,23 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
|||
|
||||
//Add user data to that item
|
||||
QMap<QString, QVariant> data;
|
||||
data["severity"] = ShowTypes::SeverityToShowType(item.severity);
|
||||
data["summary"] = item.summary;
|
||||
data["message"] = item.message;
|
||||
data["file"] = loc.file;
|
||||
data["line"] = loc.line;
|
||||
data["column"] = loc.column;
|
||||
data["id"] = item.errorId;
|
||||
data["incomplete"] = item.incomplete;
|
||||
data["cwe"] = item.cwe;
|
||||
data["inconclusive"] = item.inconclusive;
|
||||
data["file0"] = stripPath(item.file0, true);
|
||||
data["function"] = item.function;
|
||||
data["sinceDate"] = item.sinceDate;
|
||||
data["tags"] = item.tags;
|
||||
data["hide"] = hide;
|
||||
data[SEVERITY] = ShowTypes::SeverityToShowType(item.severity);
|
||||
data[SUMMARY] = item.summary;
|
||||
data[MESSAGE] = item.message;
|
||||
data[FILENAME] = loc.file;
|
||||
data[LINE] = loc.line;
|
||||
data[COLUMN] = loc.column;
|
||||
data[ERRORID] = item.errorId;
|
||||
data[INCOMPLETE] = item.incomplete;
|
||||
data[CWE] = item.cwe;
|
||||
data[HASH] = item.hash;
|
||||
data[INCONCLUSIVE] = item.inconclusive;
|
||||
data[FILE0] = stripPath(item.file0, true);
|
||||
data[FUNCTION] = item.function;
|
||||
data[SINCEDATE] = item.sinceDate;
|
||||
data[SYMBOLNAMES] = item.symbolNames;
|
||||
data[TAGS] = line.tags;
|
||||
data[HIDE] = hide;
|
||||
stditem->setData(QVariant(data));
|
||||
|
||||
//Add backtrace files as children
|
||||
|
@ -223,16 +247,18 @@ bool ResultsTree::addErrorItem(const ErrorItem &item)
|
|||
|
||||
// Add user data to that item
|
||||
QMap<QString, QVariant> child_data;
|
||||
child_data["severity"] = ShowTypes::SeverityToShowType(line.severity);
|
||||
child_data["summary"] = line.summary;
|
||||
child_data["message"] = line.message;
|
||||
child_data["file"] = e.file;
|
||||
child_data["line"] = e.line;
|
||||
child_data["column"] = e.column;
|
||||
child_data["id"] = line.errorId;
|
||||
child_data["incomplete"] = line.incomplete;
|
||||
child_data["cwe"] = line.cwe;
|
||||
child_data["inconclusive"] = line.inconclusive;
|
||||
child_data[SEVERITY] = ShowTypes::SeverityToShowType(line.severity);
|
||||
child_data[SUMMARY] = line.summary;
|
||||
child_data[MESSAGE] = line.message;
|
||||
child_data[FILENAME] = e.file;
|
||||
child_data[LINE] = e.line;
|
||||
child_data[COLUMN] = e.column;
|
||||
child_data[ERRORID] = line.errorId;
|
||||
child_data[INCOMPLETE] = line.incomplete;
|
||||
child_data[CWE] = line.cwe;
|
||||
child_data[HASH] = line.hash;
|
||||
child_data[INCONCLUSIVE] = line.inconclusive;
|
||||
child_data[SYMBOLNAMES] = item.symbolNames;
|
||||
child_item->setData(QVariant(child_data));
|
||||
}
|
||||
}
|
||||
|
@ -359,8 +385,8 @@ void ResultsTree::clear(const QString &filename)
|
|||
continue;
|
||||
|
||||
QVariantMap data = fileItem->data().toMap();
|
||||
if (stripped == data["file"].toString() ||
|
||||
filename == data["file0"].toString()) {
|
||||
if (stripped == data[FILENAME].toString() ||
|
||||
filename == data[FILE0].toString()) {
|
||||
mModel.removeRow(i);
|
||||
break;
|
||||
}
|
||||
|
@ -376,7 +402,7 @@ void ResultsTree::clearRecheckFile(const QString &filename)
|
|||
|
||||
QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename);
|
||||
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);
|
||||
if (actualfile == storedfile) {
|
||||
mModel.removeRow(i);
|
||||
|
@ -446,7 +472,7 @@ void ResultsTree::showHiddenResults()
|
|||
continue;
|
||||
|
||||
QVariantMap data = fileItem->data().toMap();
|
||||
data["hide"] = false;
|
||||
data[HIDE] = false;
|
||||
fileItem->setData(QVariant(data));
|
||||
|
||||
int errorcount = fileItem->rowCount();
|
||||
|
@ -454,7 +480,7 @@ void ResultsTree::showHiddenResults()
|
|||
QStandardItem *child = fileItem->child(j, 0);
|
||||
if (child) {
|
||||
data = child->data().toMap();
|
||||
data["hide"] = false;
|
||||
data[HIDE] = false;
|
||||
child->setData(QVariant(data));
|
||||
}
|
||||
}
|
||||
|
@ -496,21 +522,21 @@ void ResultsTree::refreshTree()
|
|||
QVariantMap data = userdata.toMap();
|
||||
|
||||
//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 (!hide && !mFilter.isEmpty()) {
|
||||
if (!data["summary"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data["message"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data["file"].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data["id"].toString().contains(mFilter, Qt::CaseInsensitive)) {
|
||||
if (!data[SUMMARY].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data[MESSAGE].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data[FILENAME].toString().contains(mFilter, Qt::CaseInsensitive) &&
|
||||
!data[ERRORID].toString().contains(mFilter, Qt::CaseInsensitive)) {
|
||||
hide = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Tool filter
|
||||
if (!hide) {
|
||||
if (data["id"].toString().startsWith("clang"))
|
||||
if (data[ERRORID].toString().startsWith("clang"))
|
||||
hide = !mShowClang;
|
||||
else
|
||||
hide = !mShowCppcheck;
|
||||
|
@ -557,8 +583,8 @@ QStandardItem *ResultsTree::ensureFileItem(const QString &fullpath, const QStrin
|
|||
|
||||
//Add user data to that item
|
||||
QMap<QString, QVariant> data;
|
||||
data["file"] = fullpath;
|
||||
data["file0"] = file0;
|
||||
data[FILENAME] = fullpath;
|
||||
data[FILE0] = file0;
|
||||
item->setData(QVariant(data));
|
||||
mModel.appendRow(item);
|
||||
|
||||
|
@ -652,10 +678,15 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
|||
menu.addSeparator();
|
||||
menu.addAction(hide);
|
||||
menu.addAction(hideallid);
|
||||
|
||||
if (!bughunting) {
|
||||
QAction *suppress = new QAction(tr("Suppress selected id(s)"), &menu);
|
||||
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.addAction(opencontainingfolder);
|
||||
|
@ -666,7 +697,8 @@ void ResultsTree::contextMenuEvent(QContextMenuEvent * e)
|
|||
connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult()));
|
||||
connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder()));
|
||||
|
||||
if (!mTags.isEmpty()) {
|
||||
const ProjectFile *currentProject = ProjectFile::getActiveProject();
|
||||
if (currentProject && !currentProject->getTags().isEmpty()) {
|
||||
menu.addSeparator();
|
||||
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);
|
||||
tagMenu->addAction(action);
|
||||
connect(action, &QAction::triggered, [=]() {
|
||||
|
@ -745,7 +777,7 @@ void ResultsTree::startApplication(QStandardItem *target, int application)
|
|||
QVariantMap data = target->data().toMap();
|
||||
|
||||
//Replace (file) with filename
|
||||
QString file = data["file"].toString();
|
||||
QString file = data[FILENAME].toString();
|
||||
file = QDir::toNativeSeparators(file);
|
||||
#ifdef Q_OS_WIN
|
||||
file.replace(QString("\\"), QString("\\\\"));
|
||||
|
@ -781,11 +813,11 @@ void ResultsTree::startApplication(QStandardItem *target, int application)
|
|||
QString params = app.getParameters();
|
||||
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("(message)", data["message"].toString(), Qt::CaseInsensitive);
|
||||
params.replace("(severity)", data["severity"].toString(), Qt::CaseInsensitive);
|
||||
params.replace("(message)", data[MESSAGE].toString(), Qt::CaseInsensitive);
|
||||
params.replace("(severity)", data[SEVERITY].toString(), Qt::CaseInsensitive);
|
||||
|
||||
QString program = app.getPath();
|
||||
|
||||
|
@ -881,14 +913,14 @@ void ResultsTree::copy()
|
|||
QVariantMap data = item->data().toMap();
|
||||
if (!data.contains("id"))
|
||||
continue;
|
||||
QString inconclusive = data["inconclusive"].toBool() ? ",inconclusive" : "";
|
||||
text += '[' + data["file"].toString() + ':' + QString::number(data["line"].toInt())
|
||||
QString inconclusive = data[INCONCLUSIVE].toBool() ? ",inconclusive" : "";
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -906,7 +938,7 @@ void ResultsTree::hideResult()
|
|||
QStandardItem *item = mModel.itemFromIndex(index);
|
||||
//Set the "hide" flag for this item
|
||||
QVariantMap data = item->data().toMap();
|
||||
data["hide"] = true;
|
||||
data[HIDE] = true;
|
||||
item->setData(QVariant(data));
|
||||
|
||||
refreshTree();
|
||||
|
@ -926,7 +958,7 @@ void ResultsTree::recheckSelectedFiles()
|
|||
while (item->parent())
|
||||
item = item->parent();
|
||||
QVariantMap data = item->data().toMap();
|
||||
QString currentFile = data["file"].toString();
|
||||
QString currentFile = data[FILENAME].toString();
|
||||
if (!currentFile.isEmpty()) {
|
||||
QString fileNameWithCheckPath;
|
||||
QFileInfo curfileInfo(currentFile);
|
||||
|
@ -940,8 +972,8 @@ void ResultsTree::recheckSelectedFiles()
|
|||
return;
|
||||
}
|
||||
if (Path::isHeader(currentFile.toStdString())) {
|
||||
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());
|
||||
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());
|
||||
if (!selectedItems.contains(fileNameWithCheckPath))
|
||||
selectedItems<<fileNameWithCheckPath;
|
||||
}
|
||||
|
@ -962,7 +994,7 @@ void ResultsTree::hideAllIdResult()
|
|||
mContextItem = mContextItem->parent()->child(mContextItem->row(), 0);
|
||||
QVariantMap data = mContextItem->data().toMap();
|
||||
|
||||
QString messageId = data["id"].toString();
|
||||
QString messageId = data[ERRORID].toString();
|
||||
|
||||
mHiddenMessageId.append(messageId);
|
||||
|
||||
|
@ -986,8 +1018,8 @@ void ResultsTree::hideAllIdResult()
|
|||
}
|
||||
|
||||
QVariantMap userdata = child->data().toMap();
|
||||
if (userdata["id"].toString() == messageId) {
|
||||
userdata["hide"] = true;
|
||||
if (userdata[ERRORID].toString() == messageId) {
|
||||
userdata[HIDE] = true;
|
||||
child->setData(QVariant(userdata));
|
||||
}
|
||||
}
|
||||
|
@ -1013,7 +1045,7 @@ void ResultsTree::suppressSelectedIds()
|
|||
QVariantMap data = item->data().toMap();
|
||||
if (!data.contains("id"))
|
||||
continue;
|
||||
selectedIds << data["id"].toString();
|
||||
selectedIds << data[ERRORID].toString();
|
||||
}
|
||||
|
||||
// delete all errors with selected message Ids
|
||||
|
@ -1022,18 +1054,59 @@ void ResultsTree::suppressSelectedIds()
|
|||
for (int j = 0; j < file->rowCount();) {
|
||||
QStandardItem *errorItem = file->child(j, 0);
|
||||
QVariantMap userdata = errorItem->data().toMap();
|
||||
if (selectedIds.contains(userdata["id"].toString())) {
|
||||
if (selectedIds.contains(userdata[ERRORID].toString())) {
|
||||
file->removeRow(j);
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (file->rowCount() == 0)
|
||||
mModel.removeRow(file->row());
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
QString filePath = getFilePath(mContextItem, true);
|
||||
|
@ -1053,18 +1126,22 @@ void ResultsTree::tagSelectedItems(const QString &tag)
|
|||
if (!mSelectionModel)
|
||||
return;
|
||||
bool isTagged = false;
|
||||
ProjectFile *currentProject = ProjectFile::getActiveProject();
|
||||
foreach (QModelIndex index, mSelectionModel->selectedRows()) {
|
||||
QStandardItem *item = mModel.itemFromIndex(index);
|
||||
QVariantMap data = item->data().toMap();
|
||||
if (data.contains("tags")) {
|
||||
data["tags"] = tag;
|
||||
data[TAGS] = tag;
|
||||
item->setData(QVariant(data));
|
||||
item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag);
|
||||
if (currentProject && data.contains(HASH)) {
|
||||
isTagged = true;
|
||||
currentProject->setWarningTags(data[HASH].toULongLong(), tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isTagged)
|
||||
emit tagged();
|
||||
currentProject->write();
|
||||
}
|
||||
|
||||
void ResultsTree::context(int application)
|
||||
|
@ -1088,7 +1165,7 @@ QString ResultsTree::getFilePath(QStandardItem *target, bool fullPath)
|
|||
QString pathStr;
|
||||
|
||||
//Replace (file) with filename
|
||||
QString file = data["file"].toString();
|
||||
QString file = data[FILENAME].toString();
|
||||
pathStr = QDir::toNativeSeparators(file);
|
||||
if (!fullPath) {
|
||||
QFileInfo fi(pathStr);
|
||||
|
@ -1188,12 +1265,12 @@ void ResultsTree::updateFromOldReport(const QString &filename)
|
|||
|
||||
// New error .. set the "sinceDate" property
|
||||
if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) {
|
||||
data["sinceDate"] = oldErrors[oldErrorIndex].sinceDate;
|
||||
data[SINCEDATE] = oldErrors[oldErrorIndex].sinceDate;
|
||||
error->setData(data);
|
||||
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);
|
||||
data["sinceDate"] = sinceDate;
|
||||
data[SINCEDATE] = sinceDate;
|
||||
error->setData(data);
|
||||
fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate);
|
||||
if (oldErrorIndex < 0)
|
||||
|
@ -1204,7 +1281,7 @@ void ResultsTree::updateFromOldReport(const QString &filename)
|
|||
continue;
|
||||
|
||||
const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex];
|
||||
data["tags"] = oldErrorItem.tags;
|
||||
data[TAGS] = oldErrorItem.tags;
|
||||
error->setData(data);
|
||||
}
|
||||
}
|
||||
|
@ -1215,22 +1292,23 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con
|
|||
// Get error's user data
|
||||
QVariantMap data = error->data().toMap();
|
||||
|
||||
item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data["severity"]));
|
||||
item->summary = data["summary"].toString();
|
||||
item->message = data["message"].toString();
|
||||
item->errorId = data["id"].toString();
|
||||
item->incomplete = data["incomplete"].toBool();
|
||||
item->cwe = data["cwe"].toInt();
|
||||
item->inconclusive = data["inconclusive"].toBool();
|
||||
item->file0 = data["file0"].toString();
|
||||
item->sinceDate = data["sinceDate"].toString();
|
||||
item->tags = data["tags"].toString();
|
||||
item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data[SEVERITY]));
|
||||
item->summary = data[SUMMARY].toString();
|
||||
item->message = data[MESSAGE].toString();
|
||||
item->errorId = data[ERRORID].toString();
|
||||
item->incomplete = data[INCOMPLETE].toBool();
|
||||
item->cwe = data[CWE].toInt();
|
||||
item->hash = data[HASH].toULongLong();
|
||||
item->inconclusive = data[INCONCLUSIVE].toBool();
|
||||
item->file0 = data[FILE0].toString();
|
||||
item->sinceDate = data[SINCEDATE].toString();
|
||||
item->tags = data[TAGS].toString();
|
||||
|
||||
if (error->rowCount() == 0) {
|
||||
QErrorPathItem e;
|
||||
e.file = stripPath(data["file"].toString(), true);
|
||||
e.line = data["line"].toUInt();
|
||||
e.info = data["message"].toString();
|
||||
e.file = stripPath(data[FILENAME].toString(), true);
|
||||
e.line = data[LINE].toInt();
|
||||
e.info = data[MESSAGE].toString();
|
||||
item->errorPath << e;
|
||||
}
|
||||
|
||||
|
@ -1242,9 +1320,9 @@ void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) con
|
|||
QVariantMap child_data = child_userdata.toMap();
|
||||
|
||||
QErrorPathItem e;
|
||||
e.file = stripPath(child_data["file"].toString(), true);
|
||||
e.line = child_data["line"].toUInt();
|
||||
e.info = child_data["message"].toString();
|
||||
e.file = stripPath(child_data[FILENAME].toString(), true);
|
||||
e.line = child_data[LINE].toUInt();
|
||||
e.info = child_data[MESSAGE].toString();
|
||||
item->errorPath << e;
|
||||
}
|
||||
}
|
||||
|
@ -1312,7 +1390,7 @@ void ResultsTree::refreshFilePaths(QStandardItem *item)
|
|||
QVariantMap data = userdata.toMap();
|
||||
|
||||
//Get list of files
|
||||
QString file = data["file"].toString();
|
||||
QString file = data[FILENAME].toString();
|
||||
|
||||
//Update this error's text
|
||||
error->setText(stripPath(file, false));
|
||||
|
@ -1332,7 +1410,7 @@ void ResultsTree::refreshFilePaths(QStandardItem *item)
|
|||
QVariantMap child_data = child_userdata.toMap();
|
||||
|
||||
//Get list of files
|
||||
QString child_files = child_data["file"].toString();
|
||||
QString child_files = child_data[FILENAME].toString();
|
||||
//Update file's path
|
||||
child->setText(stripPath(child_files, false));
|
||||
}
|
||||
|
|
|
@ -52,10 +52,6 @@ public:
|
|||
virtual ~ResultsTree();
|
||||
void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler);
|
||||
|
||||
void setTags(const QStringList &tags) {
|
||||
mTags = tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add a new item to the tree
|
||||
*
|
||||
|
@ -206,11 +202,6 @@ signals:
|
|||
*/
|
||||
void treeSelectionChanged(const QModelIndex ¤t);
|
||||
|
||||
/**
|
||||
* Selected item(s) has been tagged
|
||||
*/
|
||||
void tagged();
|
||||
|
||||
/** Suppress 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 */
|
||||
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.
|
||||
*/
|
||||
|
@ -529,8 +523,6 @@ private:
|
|||
/** @brief Convert GUI error item into data error item */
|
||||
void readErrorItem(const QStandardItem *error, ErrorItem *item) const;
|
||||
|
||||
QStringList mTags;
|
||||
|
||||
QStringList mHiddenMessageId;
|
||||
|
||||
QItemSelectionModel *mSelectionModel;
|
||||
|
|
|
@ -53,7 +53,6 @@ ResultsView::ResultsView(QWidget * parent) :
|
|||
connect(mUI.mTree, &ResultsTree::resultsHidden, this, &ResultsView::resultsHidden);
|
||||
connect(mUI.mTree, &ResultsTree::checkSelected, this, &ResultsView::checkSelected);
|
||||
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::editFunctionContract, this, &ResultsView::editFunctionContract);
|
||||
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());
|
||||
QStandardItem *item = model->itemFromIndex(index);
|
||||
|
||||
mUI.mCode->setPlainText(QString());
|
||||
|
||||
if (!item) {
|
||||
mUI.mCode->clear();
|
||||
mUI.mDetails->setText(QString());
|
||||
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 (!data.contains("severity")) {
|
||||
mUI.mCode->clear();
|
||||
mUI.mDetails->setText(QString());
|
||||
return;
|
||||
}
|
||||
|
@ -426,19 +425,24 @@ void ResultsView::updateDetails(const QModelIndex &index)
|
|||
if (!QFileInfo(filepath).exists() && QFileInfo(mUI.mTree->getCheckDirectory() + '/' + filepath).exists())
|
||||
filepath = mUI.mTree->getCheckDirectory() + '/' + filepath;
|
||||
|
||||
QFile file(filepath);
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QStringList symbols;
|
||||
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);
|
||||
if (data.contains("symbolNames"))
|
||||
symbols = data["symbolNames"].toString().split("\n");
|
||||
|
||||
if (filepath == mUI.mCode->getFileName()) {
|
||||
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)
|
||||
|
@ -448,7 +452,7 @@ void ResultsView::log(const QString &str)
|
|||
|
||||
void ResultsView::debugError(const ErrorItem &item)
|
||||
{
|
||||
mUI.mListLog->addItem(item.ToString());
|
||||
mUI.mListLog->addItem(item.toString());
|
||||
}
|
||||
|
||||
void ResultsView::bughuntingReportLine(const QString& line)
|
||||
|
|
|
@ -50,10 +50,6 @@ public:
|
|||
virtual ~ResultsView();
|
||||
ResultsView &operator=(const ResultsView &) = delete;
|
||||
|
||||
void setTags(const QStringList &tags) {
|
||||
mUI.mTree->setTags(tags);
|
||||
}
|
||||
|
||||
void setAddedContracts(const QStringList &addedContracts);
|
||||
|
||||
/**
|
||||
|
@ -222,11 +218,6 @@ signals:
|
|||
*/
|
||||
void checkSelected(QStringList selectedFilesList);
|
||||
|
||||
/**
|
||||
* Some results have been tagged
|
||||
*/
|
||||
void tagged();
|
||||
|
||||
/** Suppress Ids */
|
||||
void suppressIds(QStringList ids);
|
||||
|
||||
|
@ -368,6 +359,9 @@ private slots:
|
|||
void on_mListLog_customContextMenuRequested(const QPoint &pos);
|
||||
private:
|
||||
QSet<QString> mContracts;
|
||||
|
||||
/** Current file shown in the code editor */
|
||||
QString mCurrentFileName;
|
||||
};
|
||||
/// @}
|
||||
#endif // RESULTSVIEW_H
|
||||
|
|
|
@ -158,13 +158,6 @@
|
|||
<string>Contracts</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
|
@ -179,10 +172,6 @@
|
|||
</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">
|
||||
|
@ -203,10 +192,6 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -34,6 +34,7 @@ static const QString ErrorElementName = "error";
|
|||
static const QString ErrorsElementName = "errors";
|
||||
static const QString LocationElementName = "location";
|
||||
static const QString CWEAttribute = "cwe";
|
||||
static const QString HashAttribute = "hash";
|
||||
static const QString SinceDateAttribute = "sinceDate";
|
||||
static const QString TagsAttribute = "tag";
|
||||
static const QString FilenameAttribute = "file";
|
||||
|
@ -122,6 +123,8 @@ void XmlReportV2::writeError(const ErrorItem &error)
|
|||
mXmlWriter->writeAttribute(InconclusiveAttribute, "true");
|
||||
if (error.cwe > 0)
|
||||
mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe));
|
||||
if (error.hash > 0)
|
||||
mXmlWriter->writeAttribute(HashAttribute, QString::number(error.hash));
|
||||
if (!error.sinceDate.isEmpty())
|
||||
mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate);
|
||||
if (!error.tags.isEmpty())
|
||||
|
@ -214,7 +217,9 @@ ErrorItem XmlReportV2::readError(QXmlStreamReader *reader)
|
|||
if (attribs.hasAttribute(QString(), InconclusiveAttribute))
|
||||
item.inconclusive = true;
|
||||
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))
|
||||
item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString();
|
||||
if (attribs.hasAttribute(QString(), TagsAttribute))
|
||||
|
|
|
@ -157,6 +157,45 @@ static void integerOverflow(const Token *tok, const ExprEngine::Value &value, Ex
|
|||
}
|
||||
#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)
|
||||
{
|
||||
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() != "->")
|
||||
return;
|
||||
|
||||
// Assume that stream object is initialized
|
||||
if (Token::Match(tok->previous(), "[;{}] %var% <<|>>") && !tok->next()->astParent())
|
||||
return;
|
||||
|
||||
// Containers are not uninitialized
|
||||
std::vector<const Token *> tokens{tok, tok->astOperand1(), tok->astOperand2()};
|
||||
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())
|
||||
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;
|
||||
if (var && var->nameToken() == tok)
|
||||
return;
|
||||
|
@ -256,14 +299,13 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
|||
// Are there unconditional assignment?
|
||||
if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId()))
|
||||
return;
|
||||
for (const Token *prev = tok->previous(); prev; prev = prev->previous()) {
|
||||
if (!precedes(var->nameToken(), prev))
|
||||
break;
|
||||
if (prev->str() == "}")
|
||||
prev = prev->link();
|
||||
if (Token::Match(prev, "%varid% =", tok->varId()))
|
||||
|
||||
// Arrays are allocated on the stack
|
||||
if (var && Token::Match(tok, "%var% [") && var->isArray())
|
||||
return;
|
||||
|
||||
if (tok->variable() && isVariableAssigned(tok->variable(), tok))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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." : "");
|
||||
|
||||
if (!uninitStructMember.empty()) {
|
||||
const std::string symbol = tok->expressionString() + "." + uninitStructMember;
|
||||
dataBase->reportError(tok,
|
||||
Severity::SeverityType::error,
|
||||
"bughuntingUninitStructMember",
|
||||
"Cannot determine that '" + tok->expressionString() + "." + uninitStructMember + "' is initialized" + inconclusiveMessage,
|
||||
"$symbol:" + symbol + "\nCannot determine that '$symbol' is initialized" + inconclusiveMessage,
|
||||
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
||||
inconclusive,
|
||||
value.type == ExprEngine::ValueType::BailoutValue);
|
||||
|
@ -332,10 +375,12 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
|||
if (uninitData)
|
||||
uninitexpr += "[0]";
|
||||
|
||||
const std::string symbol = (tok->varId() > 0) ? ("$symbol:" + tok->str() + "\n") : std::string();
|
||||
|
||||
dataBase->reportError(tok,
|
||||
Severity::SeverityType::error,
|
||||
"bughuntingUninit",
|
||||
"Cannot determine that '" + uninitexpr + "' is initialized" + inconclusiveMessage,
|
||||
symbol + "Cannot determine that '" + uninitexpr + "' is initialized" + inconclusiveMessage,
|
||||
CWE_USE_OF_UNINITIALIZED_VARIABLE,
|
||||
inconclusive,
|
||||
value.type == ExprEngine::ValueType::BailoutValue);
|
||||
|
|
|
@ -688,16 +688,11 @@ void CheckCondition::multiCondition2()
|
|||
ErrorPath errorPath;
|
||||
|
||||
if (type == MULTICONDITIONTYPE::INNER) {
|
||||
std::stack<const Token *> tokens1;
|
||||
tokens1.push(cond1);
|
||||
while (!tokens1.empty()) {
|
||||
const Token *firstCondition = tokens1.top();
|
||||
tokens1.pop();
|
||||
visitAstNodes(cond1, [&](const Token* firstCondition) {
|
||||
if (!firstCondition)
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (firstCondition->str() == "&&") {
|
||||
tokens1.push(firstCondition->astOperand1());
|
||||
tokens1.push(firstCondition->astOperand2());
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
} else if (!firstCondition->hasKnownIntValue()) {
|
||||
if (!isReturnVar && isOppositeCond(false, mTokenizer->isCPP(), firstCondition, cond2, mSettings->library, true, true, &errorPath)) {
|
||||
if (!isAliased(vars))
|
||||
|
@ -706,7 +701,8 @@ void CheckCondition::multiCondition2()
|
|||
identicalInnerConditionError(firstCondition, cond2, errorPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ChildrenToVisit::none;
|
||||
});
|
||||
} else {
|
||||
visitAstNodes(cond2, [&](const Token *secondCondition) {
|
||||
if (secondCondition->str() == "||" || secondCondition->str() == "&&")
|
||||
|
@ -1436,20 +1432,15 @@ void CheckCondition::alwaysTrueFalse()
|
|||
|
||||
// Don't warn when there are expanded macros..
|
||||
bool isExpandedMacro = false;
|
||||
std::stack<const Token*> tokens;
|
||||
tokens.push(tok);
|
||||
while (!tokens.empty()) {
|
||||
const Token *tok2 = tokens.top();
|
||||
tokens.pop();
|
||||
visitAstNodes(tok, [&](const Token * tok2) {
|
||||
if (!tok2)
|
||||
continue;
|
||||
tokens.push(tok2->astOperand1());
|
||||
tokens.push(tok2->astOperand2());
|
||||
return ChildrenToVisit::none;
|
||||
if (tok2->isExpandedMacro()) {
|
||||
isExpandedMacro = true;
|
||||
break;
|
||||
}
|
||||
return ChildrenToVisit::done;
|
||||
}
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
if (isExpandedMacro)
|
||||
continue;
|
||||
for (const Token *parent = tok; parent; parent = parent->astParent()) {
|
||||
|
@ -1464,24 +1455,21 @@ void CheckCondition::alwaysTrueFalse()
|
|||
// don't warn when condition checks sizeof result
|
||||
bool hasSizeof = false;
|
||||
bool hasNonNumber = false;
|
||||
tokens.push(tok);
|
||||
while (!tokens.empty()) {
|
||||
const Token *tok2 = tokens.top();
|
||||
tokens.pop();
|
||||
visitAstNodes(tok, [&](const Token * tok2) {
|
||||
if (!tok2)
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (tok2->isNumber())
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (Token::simpleMatch(tok2->previous(), "sizeof (")) {
|
||||
hasSizeof = true;
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
}
|
||||
if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) {
|
||||
tokens.push(tok2->astOperand1());
|
||||
tokens.push(tok2->astOperand2());
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
} else
|
||||
hasNonNumber = true;
|
||||
}
|
||||
return ChildrenToVisit::none;
|
||||
});
|
||||
if (!hasNonNumber && hasSizeof)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -463,27 +463,21 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
VarInfo varInfo1(*varInfo); // VarInfo for if code
|
||||
VarInfo varInfo2(*varInfo); // VarInfo for else code
|
||||
|
||||
// Recursively scan variable comparisons in condition
|
||||
std::stack<const Token *> tokens;
|
||||
// Skip expressions before commas
|
||||
const Token * astOperand2AfterCommas = tok->next()->astOperand2();
|
||||
while (Token::simpleMatch(astOperand2AfterCommas, ","))
|
||||
astOperand2AfterCommas = astOperand2AfterCommas->astOperand2();
|
||||
tokens.push(astOperand2AfterCommas);
|
||||
while (!tokens.empty()) {
|
||||
const Token *tok3 = tokens.top();
|
||||
tokens.pop();
|
||||
|
||||
// Recursively scan variable comparisons in condition
|
||||
visitAstNodes(astOperand2AfterCommas, [&](const Token *tok3) {
|
||||
if (!tok3)
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (tok3->str() == "&&" || tok3->str() == "||") {
|
||||
// FIXME: handle && ! || better
|
||||
tokens.push(tok3->astOperand1());
|
||||
tokens.push(tok3->astOperand2());
|
||||
continue;
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
}
|
||||
if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) {
|
||||
tokens.push(tok3->astOperand2());
|
||||
continue;
|
||||
return ChildrenToVisit::op2;
|
||||
} else if (tok3->str() == "(" && Token::Match(tok3->previous(), "%name%")) {
|
||||
const std::vector<const Token *> params = getArguments(tok3->previous());
|
||||
for (const Token *par : params) {
|
||||
|
@ -500,7 +494,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
varInfo2.erase(vartok->varId());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
}
|
||||
|
||||
const Token *vartok = nullptr;
|
||||
|
@ -517,7 +511,8 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
|
|||
} else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) {
|
||||
varInfo1.erase(vartok->varId());
|
||||
}
|
||||
}
|
||||
return ChildrenToVisit::none;
|
||||
});
|
||||
|
||||
checkScope(closingParenthesis->next(), &varInfo1, notzero, recursiveCount);
|
||||
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)
|
||||
{
|
||||
const Token* tok = Token::typeDecl(var->nameToken()).first;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||
std::set<nonneg int> checkedVars;
|
||||
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
|
||||
if (!Token::Match(tok, "%var%"))
|
||||
continue;
|
||||
const Variable* var = tok->variable();
|
||||
if (!var)
|
||||
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))
|
||||
continue;
|
||||
const Variable* mvar = tok->tokAt(2)->variable();
|
||||
if (!mvar)
|
||||
continue;
|
||||
if (!checkedVars.insert(mvar->declarationId()).second)
|
||||
continue;
|
||||
if (var->isStatic() || var->isGlobal())
|
||||
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 globalLockGuardError(const Token *tok);
|
||||
void localMutexError(const Token *tok);
|
||||
|
||||
void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const OVERRIDE {
|
||||
ErrorPath errorPath;
|
||||
|
@ -271,6 +272,7 @@ private:
|
|||
c.readingEmptyStlContainerError(nullptr);
|
||||
c.useStlAlgorithmError(nullptr, "");
|
||||
c.globalLockGuardError(nullptr);
|
||||
c.localMutexError(nullptr);
|
||||
}
|
||||
|
||||
static std::string myName() {
|
||||
|
|
|
@ -355,37 +355,32 @@ void CheckString::overlappingStrcmp()
|
|||
continue;
|
||||
std::list<const Token *> equals0;
|
||||
std::list<const Token *> notEquals0;
|
||||
std::stack<const Token *> tokens;
|
||||
tokens.push(tok);
|
||||
while (!tokens.empty()) {
|
||||
const Token * const t = tokens.top();
|
||||
tokens.pop();
|
||||
visitAstNodes(tok, [&](const Token * t) {
|
||||
if (!t)
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (t->str() == "||") {
|
||||
tokens.push(t->astOperand1());
|
||||
tokens.push(t->astOperand2());
|
||||
continue;
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
}
|
||||
if (t->str() == "==") {
|
||||
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
||||
equals0.push_back(t->astOperand1());
|
||||
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
||||
equals0.push_back(t->astOperand2());
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
}
|
||||
if (t->str() == "!=") {
|
||||
if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0"))
|
||||
notEquals0.push_back(t->astOperand1());
|
||||
else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0"))
|
||||
notEquals0.push_back(t->astOperand2());
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
}
|
||||
if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "("))
|
||||
equals0.push_back(t->astOperand1());
|
||||
else if (t->str() == "(")
|
||||
notEquals0.push_back(t);
|
||||
}
|
||||
return ChildrenToVisit::none;
|
||||
});
|
||||
|
||||
for (const Token *eq0 : equals0) {
|
||||
for (const Token * ne0 : notEquals0) {
|
||||
|
|
|
@ -239,12 +239,8 @@ void CheckType::checkSignConversion()
|
|||
continue;
|
||||
|
||||
// Check if an operand can be negative..
|
||||
std::stack<const Token *> tokens;
|
||||
tokens.push(tok->astOperand1());
|
||||
tokens.push(tok->astOperand2());
|
||||
while (!tokens.empty()) {
|
||||
const Token *tok1 = tokens.top();
|
||||
tokens.pop();
|
||||
const Token * astOperands[] = { tok->astOperand1(), tok->astOperand2() };
|
||||
for (const Token * tok1 : astOperands) {
|
||||
if (!tok1)
|
||||
continue;
|
||||
const ValueFlow::Value *negativeValue = tok1->getValueLE(-1,mSettings);
|
||||
|
|
|
@ -881,22 +881,17 @@ bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const
|
|||
usetok = tok;
|
||||
else if (tok->strAt(1) == "=") {
|
||||
bool varIsUsedInRhs = false;
|
||||
std::stack<const Token *> tokens;
|
||||
tokens.push(tok->next()->astOperand2());
|
||||
while (!tokens.empty()) {
|
||||
const Token *t = tokens.top();
|
||||
tokens.pop();
|
||||
visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) {
|
||||
if (!t)
|
||||
continue;
|
||||
return ChildrenToVisit::none;
|
||||
if (t->varId() == var.declarationId()) {
|
||||
varIsUsedInRhs = true;
|
||||
break;
|
||||
return ChildrenToVisit::done;
|
||||
}
|
||||
if (Token::simpleMatch(t->previous(),"sizeof ("))
|
||||
continue;
|
||||
tokens.push(t->astOperand1());
|
||||
tokens.push(t->astOperand2());
|
||||
}
|
||||
return ChildrenToVisit::none;
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
});
|
||||
if (!varIsUsedInRhs)
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <functional> // std::hash
|
||||
|
||||
InternalError::InternalError(const Token *tok, const std::string &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()
|
||||
: 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),
|
||||
severity(severity), // severity for this error message
|
||||
cwe(0U),
|
||||
inconclusive(inconclusive)
|
||||
inconclusive(inconclusive),
|
||||
hash(0)
|
||||
{
|
||||
// set the summary and verbose messages
|
||||
setmsg(msg);
|
||||
|
@ -85,14 +94,15 @@ ErrorMessage::ErrorMessage(const std::list<FileLocation> &callStack, const std::
|
|||
incomplete(false),
|
||||
severity(severity), // severity for this error message
|
||||
cwe(cwe.id),
|
||||
inconclusive(inconclusive)
|
||||
inconclusive(inconclusive),
|
||||
hash(0)
|
||||
{
|
||||
// set the summary and verbose messages
|
||||
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)
|
||||
: id(id), incomplete(false), severity(severity), cwe(0U), inconclusive(inconclusive)
|
||||
: id(id), incomplete(false), severity(severity), cwe(0U), inconclusive(inconclusive), hash(0)
|
||||
{
|
||||
// Format callstack
|
||||
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];
|
||||
|
||||
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)
|
||||
: id(id), incomplete(false), severity(severity), cwe(cwe.id), inconclusive(inconclusive)
|
||||
{
|
||||
// Format callstack
|
||||
for (ErrorPath::const_iterator it = errorPath.begin(); it != errorPath.end(); ++it) {
|
||||
const Token *tok = it->first;
|
||||
const std::string &info = it->second;
|
||||
for (const ErrorPathItem& e: errorPath) {
|
||||
const Token *tok = e.first;
|
||||
const std::string &info = e.second;
|
||||
|
||||
// --errorlist can provide null values here
|
||||
if (tok)
|
||||
|
@ -145,6 +162,13 @@ ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenLis
|
|||
file0 = tokenList->getFiles()[0];
|
||||
|
||||
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)
|
||||
|
@ -173,6 +197,9 @@ ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg)
|
|||
attr = errmsg->Attribute("verbose");
|
||||
mVerboseMessage = attr ? attr : "";
|
||||
|
||||
attr = errmsg->Attribute("hash");
|
||||
std::istringstream(attr ? attr : "0") >> hash;
|
||||
|
||||
for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
||||
if (std::strcmp(e->Name(),"location")==0) {
|
||||
const char *strfile = e->Attribute("file");
|
||||
|
@ -218,6 +245,7 @@ void ErrorMessage::setmsg(const std::string &msg)
|
|||
Suppressions::ErrorMessage ErrorMessage::toSuppressionsErrorMessage() const
|
||||
{
|
||||
Suppressions::ErrorMessage ret;
|
||||
ret.hash = hash;
|
||||
ret.errorId = id;
|
||||
if (!callStack.empty()) {
|
||||
ret.setFileName(callStack.back().getfile(false));
|
||||
|
@ -236,6 +264,7 @@ std::string ErrorMessage::serialize() const
|
|||
oss << id.length() << " " << id;
|
||||
oss << Severity::toString(severity).length() << " " << Severity::toString(severity);
|
||||
oss << MathLib::toString(cwe.id).length() << " " << MathLib::toString(cwe.id);
|
||||
oss << MathLib::toString(hash).length() << " " << MathLib::toString(hash);
|
||||
if (inconclusive) {
|
||||
const std::string text("inconclusive");
|
||||
oss << text.length() << " " << text;
|
||||
|
@ -262,9 +291,9 @@ bool ErrorMessage::deserialize(const std::string &data)
|
|||
inconclusive = false;
|
||||
callStack.clear();
|
||||
std::istringstream iss(data);
|
||||
std::array<std::string, 5> results;
|
||||
std::array<std::string, 6> results;
|
||||
std::size_t elem = 0;
|
||||
while (iss.good()) {
|
||||
while (iss.good() && elem < 6) {
|
||||
unsigned int len = 0;
|
||||
if (!(iss >> len))
|
||||
return false;
|
||||
|
@ -282,19 +311,17 @@ bool ErrorMessage::deserialize(const std::string &data)
|
|||
}
|
||||
|
||||
results[elem++] = temp;
|
||||
if (elem == 5)
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem != 5)
|
||||
if (elem != 6)
|
||||
throw InternalError(nullptr, "Internal Error: Deserialization of error message failed");
|
||||
|
||||
id = results[0];
|
||||
severity = Severity::fromString(results[1]);
|
||||
std::istringstream scwe(results[2]);
|
||||
scwe >> cwe.id;
|
||||
mShortMessage = results[3];
|
||||
mVerboseMessage = results[4];
|
||||
std::istringstream(results[2]) >> cwe.id;
|
||||
std::istringstream(results[3]) >> hash;
|
||||
mShortMessage = results[4];
|
||||
mVerboseMessage = results[5];
|
||||
|
||||
unsigned int stackSize = 0;
|
||||
if (!(iss >> stackSize))
|
||||
|
@ -347,8 +374,6 @@ bool ErrorMessage::deserialize(const std::string &data)
|
|||
|
||||
std::string ErrorMessage::getXMLHeader()
|
||||
{
|
||||
// xml_version 1 is the default xml format
|
||||
|
||||
tinyxml2::XMLPrinter printer;
|
||||
|
||||
// standard xml header
|
||||
|
@ -403,6 +428,8 @@ std::string ErrorMessage::toXML() const
|
|||
printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str());
|
||||
if (cwe.id)
|
||||
printer.PushAttribute("cwe", cwe.id);
|
||||
if (hash)
|
||||
printer.PushAttribute("hash", MathLib::toString(hash).c_str());
|
||||
if (inconclusive)
|
||||
printer.PushAttribute("inconclusive", "true");
|
||||
|
||||
|
|
|
@ -196,6 +196,9 @@ public:
|
|||
CWE cwe;
|
||||
bool inconclusive;
|
||||
|
||||
/** Warning hash */
|
||||
std::size_t hash;
|
||||
|
||||
/** set short and verbose messages */
|
||||
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, "[;{}]"))
|
||||
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) {
|
||||
tok = tok->tokAt(4);
|
||||
continue;
|
||||
|
@ -2567,8 +2571,15 @@ static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data)
|
|||
data.addConstraints(value, var.nameToken());
|
||||
return value;
|
||||
}
|
||||
if (valueType->type == ValueType::Type::RECORD)
|
||||
return createStructVal(valueType->typeScope, var.isLocal() && !var.isStatic(), data);
|
||||
if (valueType->type == ValueType::Type::RECORD) {
|
||||
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) {
|
||||
auto structValue = createStructVal(valueType->smartPointerType->classScope, var.isLocal() && !var.isStatic(), data);
|
||||
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;
|
||||
|
||||
std::list<std::string> paths;
|
||||
std::list<std::string> suppressions;
|
||||
std::list<Suppressions::Suppression> suppressions;
|
||||
Settings temp;
|
||||
|
||||
guiProject.analyzeAllVsConfigs.clear();
|
||||
|
@ -1072,9 +1072,24 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib);
|
||||
else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0)
|
||||
guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr);
|
||||
else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0)
|
||||
suppressions = readXmlStringList(node, "", CppcheckXml::SuppressionElementName, nullptr);
|
||||
else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0)
|
||||
else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) {
|
||||
for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) {
|
||||
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);
|
||||
else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0)
|
||||
guiProject.platform = node->GetText();
|
||||
|
@ -1115,7 +1130,9 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
else
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
} else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0)
|
||||
; // TODO
|
||||
else
|
||||
return false;
|
||||
}
|
||||
settings->basePaths = temp.basePaths;
|
||||
|
@ -1130,8 +1147,8 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti
|
|||
|
||||
for (const std::string &p : paths)
|
||||
guiProject.pathNames.push_back(p);
|
||||
for (const std::string &supp : suppressions)
|
||||
settings->nomsg.addSuppressionLine(supp);
|
||||
for (const Suppressions::Suppression &supp : suppressions)
|
||||
settings->nomsg.addSuppression(supp);
|
||||
settings->checkHeaders = temp.checkHeaders;
|
||||
settings->checkUnusedTemplates = temp.checkUnusedTemplates;
|
||||
settings->maxCtuDepth = temp.maxCtuDepth;
|
||||
|
|
|
@ -161,6 +161,10 @@ namespace CppcheckXml {
|
|||
const char ToolsElementName[] = "tools";
|
||||
const char TagsElementName[] = "tags";
|
||||
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 CheckUnusedTemplatesElementName[] = "check-unused-templates";
|
||||
const char MaxCtuDepthElementName[] = "max-ctu-depth";
|
||||
|
|
|
@ -85,7 +85,7 @@ std::string Suppressions::parseXmlFile(const char *filename)
|
|||
const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement();
|
||||
for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) {
|
||||
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;
|
||||
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);
|
||||
else if (std::strcmp(e2->Name(), "symbolName") == 0)
|
||||
s.symbolName = text;
|
||||
else if (*text && std::strcmp(e2->Name(), "hash") == 0)
|
||||
std::istringstream(text) >> s.hash;
|
||||
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);
|
||||
|
@ -204,9 +206,9 @@ std::string Suppressions::addSuppressionLine(const std::string &line)
|
|||
std::string Suppressions::addSuppression(const Suppressions::Suppression &suppression)
|
||||
{
|
||||
// Check that errorId is valid..
|
||||
if (suppression.errorId.empty()) {
|
||||
if (suppression.errorId.empty() && suppression.hash == 0)
|
||||
return "Failed to add suppression. No id.";
|
||||
}
|
||||
|
||||
if (suppression.errorId != "*") {
|
||||
for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++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
|
||||
{
|
||||
if (hash > 0 && hash != errmsg.hash)
|
||||
return false;
|
||||
if (!errorId.empty() && !matchglob(errorId, errmsg.errorId))
|
||||
return false;
|
||||
if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName()))
|
||||
|
@ -315,6 +319,8 @@ std::string Suppressions::Suppression::getText() const
|
|||
ret += " lineNumber=" + MathLib::toString(lineNumber);
|
||||
if (!symbolName.empty())
|
||||
ret += " symbolName=" + symbolName;
|
||||
if (hash > 0)
|
||||
ret += " hash=" + MathLib::toString(hash);
|
||||
if (ret.compare(0,1," ")==0)
|
||||
return ret.substr(1);
|
||||
return ret;
|
||||
|
@ -358,19 +364,21 @@ void Suppressions::dump(std::ostream & out) const
|
|||
out << " lineNumber=\"" << suppression.lineNumber << '"';
|
||||
if (!suppression.symbolName.empty())
|
||||
out << " symbolName=\"" << ErrorLogger::toxml(suppression.symbolName) << '\"';
|
||||
if (suppression.hash > 0)
|
||||
out << " hash=\"" << suppression.hash << '\"';
|
||||
out << " />" << std::endl;
|
||||
}
|
||||
out << " </suppressions>" << std::endl;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
std::list<Suppressions::Suppression> Suppressions::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const
|
||||
{
|
||||
std::list<Suppression> result;
|
||||
for (const Suppression &s : mSuppressions) {
|
||||
if (s.matched)
|
||||
continue;
|
||||
if (s.hash > 0)
|
||||
continue;
|
||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||
continue;
|
||||
if (file.empty() || !s.isLocal() || s.fileName != file)
|
||||
|
@ -386,6 +394,8 @@ std::list<Suppressions::Suppression> Suppressions::getUnmatchedGlobalSuppression
|
|||
for (const Suppression &s : mSuppressions) {
|
||||
if (s.matched)
|
||||
continue;
|
||||
if (s.hash > 0)
|
||||
continue;
|
||||
if (!unusedFunctionChecking && s.errorId == "unusedFunction")
|
||||
continue;
|
||||
if (s.isLocal())
|
||||
|
|
|
@ -35,6 +35,7 @@ class CPPCHECKLIB Suppressions {
|
|||
public:
|
||||
|
||||
struct CPPCHECKLIB ErrorMessage {
|
||||
std::size_t hash;
|
||||
std::string errorId;
|
||||
void setFileName(const std::string &s);
|
||||
const std::string &getFileName() const {
|
||||
|
@ -48,17 +49,18 @@ public:
|
|||
};
|
||||
|
||||
struct CPPCHECKLIB Suppression {
|
||||
Suppression() : lineNumber(NO_LINE), matched(false) {}
|
||||
Suppression() : lineNumber(NO_LINE), hash(0), matched(false) {}
|
||||
Suppression(const Suppression &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) {
|
||||
errorId = other.errorId;
|
||||
fileName = other.fileName;
|
||||
lineNumber = other.lineNumber;
|
||||
symbolName = other.symbolName;
|
||||
hash = other.hash;
|
||||
matched = other.matched;
|
||||
return *this;
|
||||
}
|
||||
|
@ -72,6 +74,8 @@ public:
|
|||
return fileName < other.fileName;
|
||||
if (symbolName != other.symbolName)
|
||||
return symbolName < other.symbolName;
|
||||
if (hash != other.hash)
|
||||
return hash < other.hash;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,6 +100,7 @@ public:
|
|||
std::string fileName;
|
||||
int lineNumber;
|
||||
std::string symbolName;
|
||||
std::size_t hash;
|
||||
bool matched;
|
||||
|
||||
enum { NO_LINE = -1 };
|
||||
|
|
|
@ -9624,11 +9624,16 @@ void Tokenizer::findGarbageCode() const
|
|||
if (match1 && match2)
|
||||
syntaxError(tok);
|
||||
}
|
||||
if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|% )|]|}")) {
|
||||
if (isC())
|
||||
syntaxError(tok, tok->str() + tok->next()->str());
|
||||
if (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator"))
|
||||
syntaxError(tok, tok->str() + " " + tok->next()->str());
|
||||
if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|%")) {
|
||||
std::string code = "";
|
||||
if (Token::Match(tok->next(), ")|]|}"))
|
||||
code = 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%"))
|
||||
syntaxError(tok);
|
||||
|
|
|
@ -1387,6 +1387,18 @@ static Token * createAstAtToken(Token *tok, bool cpp)
|
|||
init1 = tok2;
|
||||
AST_state state1(cpp);
|
||||
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 {
|
||||
while (tok2 && tok2 != endPar && tok2->str() != ";") {
|
||||
if (tok2->str() == "<" && tok2->link()) {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
# To install required tools in debian:
|
||||
# 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.html -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 --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.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.
|
||||
|
||||
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:
|
||||
|
||||
|
@ -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.
|
||||
|
||||
The available options are:
|
||||
* c89: C code is C89 compatible
|
||||
* c99: C code is C99 compatible
|
||||
* c11: C code is C11 compatible (default)
|
||||
* c++03: C++ code is C++03 compatible
|
||||
* c++11: C++ code is C++11 compatible
|
||||
* c++14: C++ code is C++14 compatible
|
||||
* c++17: C++ code is C++17 compatible
|
||||
* c++20: C++ code is C++20 compatible (default)
|
||||
|
||||
- c89: C code is C89 compatible
|
||||
- c99: C code is C99 compatible
|
||||
- c11: C code is C11 compatible (default)
|
||||
- c++03: C++ code is C++03 compatible
|
||||
- c++11: C++ code is C++11 compatible
|
||||
- c++14: C++ code is C++14 compatible
|
||||
- c++17: C++ code is C++17 compatible
|
||||
- c++20: C++ code is C++20 compatible (default)
|
||||
|
||||
# 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.
|
||||
|
||||
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 need extra help troubleshooting a weird bug.
|
||||
* you want to check if a release candidate is safe.
|
||||
|
||||
- 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 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.
|
||||
|
||||
|
@ -923,8 +926,9 @@ Cppcheck will warn:
|
|||
## Adding a contract in the GUI
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ private:
|
|||
TEST_CASE(uninit_array);
|
||||
TEST_CASE(uninit_function_par);
|
||||
TEST_CASE(uninit_malloc);
|
||||
TEST_CASE(uninit_struct);
|
||||
TEST_CASE(uninit_bailout);
|
||||
TEST_CASE(ctu);
|
||||
#endif
|
||||
}
|
||||
|
@ -98,6 +100,33 @@ private:
|
|||
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() {
|
||||
check("void init(int &x) {\n"
|
||||
" x = 1;\n"
|
||||
|
|
|
@ -252,6 +252,7 @@ private:
|
|||
ASSERT_EQUALS("7 errorId"
|
||||
"5 error"
|
||||
"1 0"
|
||||
"1 0"
|
||||
"12 inconclusive"
|
||||
"17 Programming error"
|
||||
"17 Programming error"
|
||||
|
@ -278,6 +279,7 @@ private:
|
|||
ASSERT_EQUALS("7 errorId"
|
||||
"5 error"
|
||||
"1 0"
|
||||
"1 0"
|
||||
"33 Illegal character in \"foo\\001bar\""
|
||||
"33 Illegal character in \"foo\\001bar\""
|
||||
"0 ", msg.serialize());
|
||||
|
|
|
@ -1402,7 +1402,7 @@ private:
|
|||
|
||||
void garbageCode164() {
|
||||
//7234
|
||||
checkCode("class d{k p;}(){d::d():B<()}");
|
||||
ASSERT_THROW(checkCode("class d{k p;}(){d::d():B<()}"), InternalError);
|
||||
}
|
||||
|
||||
void garbageCode165() {
|
||||
|
|
|
@ -4479,6 +4479,28 @@ private:
|
|||
true);
|
||||
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"
|
||||
"void f() {\n"
|
||||
" static std::mutex m;\n"
|
||||
|
@ -4489,6 +4511,16 @@ private:
|
|||
true);
|
||||
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"
|
||||
" std::mutex m;\n"
|
||||
" void f() {\n"
|
||||
|
@ -4537,6 +4569,55 @@ private:
|
|||
"}\n",
|
||||
true);
|
||||
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("forresdirGetFirst.file&_T(,(=;;(", testAst("for ((res = dir.GetFirst(&file, _T("")));;) {}"));
|
||||
|
||||
// problems with: if (x[y]==z)
|
||||
ASSERT_EQUALS("ifa(0[1==(", testAst("if(a()[0]==1){}"));
|
||||
ASSERT_EQUALS("ifbuff0[&(*1==(", testAst("if (*((DWORD*)&buff[0])==1){}"));
|
||||
|
@ -8108,6 +8110,9 @@ private:
|
|||
|
||||
// Ticket #9664
|
||||
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;");
|
||||
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
|
||||
" if (x==0) {}\n"
|
||||
" return (x-1)*sizeof(int);\n"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<Include>
|
||||
<?define CliBuildDir = "..\bin" ?>
|
||||
<?define GuiBuildDir = "..\bin" ?>
|
||||
<?define GuiHelpDir = "..\gui\help" ?>
|
||||
<?define TranslationsDir = "..\gui" ?>
|
||||
<?define CfgsDir = "..\cfg" ?>
|
||||
<?define PtfsDir = "..\platforms" ?>
|
||||
|
|
|
@ -55,6 +55,12 @@
|
|||
<File Id='qwindowsvistastyledll' Name='qwindowsvistastyle.dll' Source='$(var.QtDllDir)\styles\qwindowsvistastyle.dll' />
|
||||
</Component>
|
||||
</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'>
|
||||
<Component Id='GuiTranslations' Guid='$(var.guiTranslationsGUID)'>
|
||||
<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 basedocsGUID = "9F62B23C-CD2B-4826-A567-6AB6F75A2AEA" ?>
|
||||
<?define registrykeysGUID = "8CB515B1-6EF6-4A2A-97A0-F8A13E3A505E" ?>
|
||||
<?define guihelpGUID = "0A0C93BF-59E8-4919-B08F-D4BA63B9C93C" ?>
|
||||
</Include>
|
||||
|
|
Loading…
Reference in New Issue