Merge branch 'main' into condition-in-expr

This commit is contained in:
Paul 2020-07-21 13:28:59 -05:00
commit dbb410cdae
107 changed files with 3603 additions and 712 deletions

View File

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

View File

@ -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
@ -35,7 +35,6 @@ before_install:
- travis_retry python3 -m pip install --user pexpect # imported by tools/ci.py
- travis_retry python3 -m pip install --user requests # imported by tools/pr.py
- travis_retry python3 -m pip install --user pygments
- travis_retry sudo python3 -m pip install demjson # installs jsonlint => sudo required
- travis_retry python3 -m pip install --user natsort
- cp externals/z3_version_old.h externals/z3_version.h # because travis z3 version is old
@ -183,7 +182,7 @@ matrix:
- make -s -j2 CXXFLAGS=-funsigned-char testrunner
- ./testrunner TestSymbolDatabase
# check .json files
- find . -name '*.json' -not -path '*/\.*' | xargs jsonlint -s
- find . -name '*.json' | xargs -n 1 python3 -m json.tool > /dev/null
# build fuzz client
- make -s -j2 CXXFLAGS="-fsanitize=address" -C oss-fuzz fuzz-client

View File

@ -484,7 +484,7 @@ $(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/astutils.h lib/check.h
$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml/tinyxml2.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkunusedfunctions.o $(libcppdir)/checkunusedfunctions.cpp
$(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp externals/simplecpp/simplecpp.h lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/checkunusedvar.o $(libcppdir)/checkunusedvar.cpp
$(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
@ -556,7 +556,7 @@ $(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h
$(libcppdir)/token.o: lib/token.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/token.o $(libcppdir)/token.cpp
$(libcppdir)/tokenize.o: lib/tokenize.cpp lib/check.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/check.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/tokenize.o $(libcppdir)/tokenize.cpp
$(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h
@ -622,7 +622,7 @@ test/testclass.o: test/testclass.cpp externals/tinyxml/tinyxml2.h lib/check.h li
test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlineparser.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/redirect.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcmdlineparser.o test/testcmdlineparser.cpp
test/testcondition.o: test/testcondition.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkcondition.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
test/testcondition.o: test/testcondition.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkcondition.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testcondition.o test/testcondition.cpp
test/testconstructors.o: test/testconstructors.cpp lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
@ -679,7 +679,7 @@ test/testnullpointer.o: test/testnullpointer.cpp externals/simplecpp/simplecpp.h
test/testoptions.o: test/testoptions.cpp lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h test/options.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testoptions.o test/testoptions.cpp
test/testother.o: test/testother.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkother.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
test/testother.o: test/testother.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkother.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testother.o test/testother.cpp
test/testpath.o: test/testpath.cpp lib/config.h lib/errorlogger.h lib/errortypes.h lib/path.h lib/suppressions.h test/testsuite.h
@ -760,7 +760,7 @@ test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/check.h lib/checkun
test/testunusedprivfunc.o: test/testunusedprivfunc.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkclass.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedprivfunc.o test/testunusedprivfunc.cpp
test/testunusedvar.o: test/testunusedvar.cpp lib/check.h lib/checkunusedvar.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
test/testunusedvar.o: test/testunusedvar.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testunusedvar.o test/testunusedvar.cpp
test/testutils.o: test/testutils.cpp lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/utils.h test/testsuite.h

View File

@ -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,11 +2728,17 @@ class MisraChecker:
if self.severity:
cppcheck_severity = self.severity
cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity)
this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum)
if misra_severity not in self.violations:
self.violations[misra_severity] = []
self.violations[misra_severity].append('misra-' + errorId)
# 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:
self.violations[misra_severity] = []
self.violations[misra_severity].append('misra-' + errorId)
def loadRuleTexts(self, filename):
num1 = 0

View File

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

View File

@ -5047,11 +5047,14 @@
<define name="Q_DISABLE_COPY(C)" value="C(C&amp;);C&amp; operator=(const C&amp;);"/>
<define name="Q_ENUM(X)" value=""/>
<define name="Q_ENUMS(X)" value=""/>
<define name="Q_ENUM_NS(X)" value=""/>
<define name="Q_FLAG(X)" value=""/>
<define name="Q_FLAGS(X)" value=""/>
<define name="Q_FLAG_NS(X)" value=""/>
<define name="Q_FOREVER" value="for (;;)"/>
<define name="Q_INTERFACES(X)" value=""/>
<define name="Q_LIKELY(expr)" value="expr"/>
<define name="Q_NAMESPACE" value=""/>
<define name="Q_NULLPTR" value="NULL"/>
<define name="Q_OBJECT" value=""/>
<define name="Q_PRIVATE_SLOT(d, signature)" value=""/>

View File

@ -1475,6 +1475,16 @@
<not-uninit/>
</arg>
</function>
<!-- int std::at_quick_exit( /*atexit-handler*/* func ) noexcept;-->
<function name="std::at_quick_exit,at_quick_exit">
<noreturn>false</noreturn>
<use-retval/>
<returnValue type="int"/>
<arg nr="1" direction="in">
<not-uninit/>
<not-null/>
</arg>
</function>
<!-- double fabs(double x); -->
<function name="fabs,std::fabs">
<use-retval/>
@ -3755,8 +3765,8 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
<valid>0:</valid>
</arg>
</function>
<!-- const void * memchr ( const void * ptr, int value, size_t num );-->
<!-- void * memchr ( void * ptr, int value, size_t num );-->
<!-- const void * memchr ( const void * ptr, int value, size_t num ); -->
<!-- void * memchr ( void * ptr, int value, size_t num ); -->
<function name="memchr,std::memchr">
<use-retval/>
<pure/>
@ -4222,6 +4232,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
<arg nr="2" direction="in">
<not-null/>
<not-uninit/>
<minsize type="argvalue" arg="3"/>
</arg>
<arg nr="3" direction="in">
<not-uninit/>

View File

@ -6113,6 +6113,22 @@ HFONT CreateFont(
<not-uninit/>
</arg>
</function>
<!-- BOOL SystemParametersInfoA(UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); -->
<function name="SystemParametersInfo,SystemParametersInfoA,SystemParametersInfoW">
<noreturn>false</noreturn>
<returnValue type="BOOL"/>
<leak-ignore/>
<arg nr="1" direction="in">
<not-uninit/>
</arg>
<arg nr="2" direction="in">
<not-uninit/>
</arg>
<arg nr="3" direction="out"/>
<arg nr="4" direction="in">
<not-uninit/>
</arg>
</function>
<!-- BOOL WINAPI CancelIo(
_In_ HANDLE hFile); -->
<function name="CancelIo">
@ -12645,6 +12661,147 @@ HFONT CreateFont(
<define name="INVALID_SET_FILE_POINTER" value="((DWORD)-1)"/>
<define name="INVALID_FILE_ATTRIBUTES" value="((DWORD)-1)"/>
<!-- WinUser.h -->
<define name="SPI_GETBEEP" value="0x0001"/>
<define name="SPI_SETBEEP" value="0x0002"/>
<define name="SPI_GETMOUSE" value="0x0003"/>
<define name="SPI_SETMOUSE" value="0x0004"/>
<define name="SPI_GETBORDER" value="0x0005"/>
<define name="SPI_SETBORDER" value="0x0006"/>
<define name="SPI_GETKEYBOARDSPEED" value="0x000A"/>
<define name="SPI_SETKEYBOARDSPEED" value="0x000B"/>
<define name="SPI_LANGDRIVER" value="0x000C"/>
<define name="SPI_ICONHORIZONTALSPACING" value="0x000D"/>
<define name="SPI_GETSCREENSAVETIMEOUT" value="0x000E"/>
<define name="SPI_SETSCREENSAVETIMEOUT" value="0x000F"/>
<define name="SPI_GETSCREENSAVEACTIVE" value="0x0010"/>
<define name="SPI_SETSCREENSAVEACTIVE" value="0x0011"/>
<define name="SPI_GETGRIDGRANULARITY" value="0x0012"/>
<define name="SPI_SETGRIDGRANULARITY" value="0x0013"/>
<define name="SPI_SETDESKWALLPAPER" value="0x0014"/>
<define name="SPI_SETDESKPATTERN" value="0x0015"/>
<define name="SPI_GETKEYBOARDDELAY" value="0x0016"/>
<define name="SPI_SETKEYBOARDDELAY" value="0x0017"/>
<define name="SPI_ICONVERTICALSPACING" value="0x0018"/>
<define name="SPI_GETICONTITLEWRAP" value="0x0019"/>
<define name="SPI_SETICONTITLEWRAP" value="0x001A"/>
<define name="SPI_GETMENUDROPALIGNMENT" value="0x001B"/>
<define name="SPI_SETMENUDROPALIGNMENT" value="0x001C"/>
<define name="SPI_SETDOUBLECLKWIDTH" value="0x001D"/>
<define name="SPI_SETDOUBLECLKHEIGHT" value="0x001E"/>
<define name="SPI_GETICONTITLELOGFONT" value="0x001F"/>
<define name="SPI_SETDOUBLECLICKTIME" value="0x0020"/>
<define name="SPI_SETMOUSEBUTTONSWAP" value="0x0021"/>
<define name="SPI_SETICONTITLELOGFONT" value="0x0022"/>
<define name="SPI_GETFASTTASKSWITCH" value="0x0023"/>
<define name="SPI_SETFASTTASKSWITCH" value="0x0024"/>
<define name="SPI_SETDRAGFULLWINDOWS" value="0x0025"/>
<define name="SPI_GETDRAGFULLWINDOWS" value="0x0026"/>
<define name="SPI_GETNONCLIENTMETRICS" value="0x0029"/>
<define name="SPI_SETNONCLIENTMETRICS" value="0x002A"/>
<define name="SPI_GETMINIMIZEDMETRICS" value="0x002B"/>
<define name="SPI_SETMINIMIZEDMETRICS" value="0x002C"/>
<define name="SPI_GETICONMETRICS" value="0x002D"/>
<define name="SPI_SETICONMETRICS" value="0x002E"/>
<define name="SPI_SETWORKAREA" value="0x002F"/>
<define name="SPI_GETWORKAREA" value="0x0030"/>
<define name="SPI_SETPENWINDOWS" value="0x0031"/>
<define name="SPI_GETHIGHCONTRAST" value="0x0042"/>
<define name="SPI_SETHIGHCONTRAST" value="0x0043"/>
<define name="SPI_GETKEYBOARDPREF" value="0x0044"/>
<define name="SPI_SETKEYBOARDPREF" value="0x0045"/>
<define name="SPI_GETSCREENREADER" value="0x0046"/>
<define name="SPI_SETSCREENREADER" value="0x0047"/>
<define name="SPI_GETANIMATION" value="0x0048"/>
<define name="SPI_SETANIMATION" value="0x0049"/>
<define name="SPI_GETFONTSMOOTHING" value="0x004A"/>
<define name="SPI_SETFONTSMOOTHING" value="0x004B"/>
<define name="SPI_SETDRAGWIDTH" value="0x004C"/>
<define name="SPI_SETDRAGHEIGHT" value="0x004D"/>
<define name="SPI_SETHANDHELD" value="0x004E"/>
<define name="SPI_GETLOWPOWERTIMEOUT" value="0x004F"/>
<define name="SPI_GETPOWEROFFTIMEOUT" value="0x0050"/>
<define name="SPI_SETLOWPOWERTIMEOUT" value="0x0051"/>
<define name="SPI_SETPOWEROFFTIMEOUT" value="0x0052"/>
<define name="SPI_GETLOWPOWERACTIVE" value="0x0053"/>
<define name="SPI_GETPOWEROFFACTIVE" value="0x0054"/>
<define name="SPI_SETLOWPOWERACTIVE" value="0x0055"/>
<define name="SPI_SETPOWEROFFACTIVE" value="0x0056"/>
<define name="SPI_SETCURSORS" value="0x0057"/>
<define name="SPI_SETICONS" value="0x0058"/>
<define name="SPI_GETDEFAULTINPUTLANG" value="0x0059"/>
<define name="SPI_SETDEFAULTINPUTLANG" value="0x005A"/>
<define name="SPI_SETLANGTOGGLE" value="0x005B"/>
<define name="SPI_GETWINDOWSEXTENSION" value="0x005C"/>
<define name="SPI_SETMOUSETRAILS" value="0x005D"/>
<define name="SPI_GETMOUSETRAILS" value="0x005E"/>
<define name="SPI_SETSCREENSAVERRUNNING" value="0x0061"/>
<define name="SPI_SCREENSAVERRUNNING" value="SPI_SETSCREENSAVERRUNNING"/>
<define name="SPI_GETFILTERKEYS" value="0x0032"/>
<define name="SPI_SETFILTERKEYS" value="0x0033"/>
<define name="SPI_GETTOGGLEKEYS" value="0x0034"/>
<define name="SPI_SETTOGGLEKEYS" value="0x0035"/>
<define name="SPI_GETMOUSEKEYS" value="0x0036"/>
<define name="SPI_SETMOUSEKEYS" value="0x0037"/>
<define name="SPI_GETSHOWSOUNDS" value="0x0038"/>
<define name="SPI_SETSHOWSOUNDS" value="0x0039"/>
<define name="SPI_GETSTICKYKEYS" value="0x003A"/>
<define name="SPI_SETSTICKYKEYS" value="0x003B"/>
<define name="SPI_GETACCESSTIMEOUT" value="0x003C"/>
<define name="SPI_SETACCESSTIMEOUT" value="0x003D"/>
<define name="SPI_GETSERIALKEYS" value="0x003E"/>
<define name="SPI_SETSERIALKEYS" value="0x003F"/>
<define name="SPI_GETSOUNDSENTRY" value="0x0040"/>
<define name="SPI_SETSOUNDSENTRY" value="0x0041"/>
<define name="SPI_GETSNAPTODEFBUTTON" value="0x005F"/>
<define name="SPI_SETSNAPTODEFBUTTON" value="0x0060"/>
<define name="SPI_GETMOUSEHOVERWIDTH" value="0x0062"/>
<define name="SPI_SETMOUSEHOVERWIDTH" value="0x0063"/>
<define name="SPI_GETMOUSEHOVERHEIGHT" value="0x0064"/>
<define name="SPI_SETMOUSEHOVERHEIGHT" value="0x0065"/>
<define name="SPI_GETMOUSEHOVERTIME" value="0x0066"/>
<define name="SPI_SETMOUSEHOVERTIME" value="0x0067"/>
<define name="SPI_GETWHEELSCROLLLINES" value="0x0068"/>
<define name="SPI_SETWHEELSCROLLLINES" value="0x0069"/>
<define name="SPI_GETMENUSHOWDELAY" value="0x006A"/>
<define name="SPI_SETMENUSHOWDELAY" value="0x006B"/>
<define name="SPI_GETWHEELSCROLLCHARS" value="0x006C"/>
<define name="SPI_SETWHEELSCROLLCHARS" value="0x006D"/>
<define name="SPI_GETSHOWIMEUI" value="0x006E"/>
<define name="SPI_SETSHOWIMEUI" value="0x006F"/>
<define name="SPI_GETMOUSESPEED" value="0x0070"/>
<define name="SPI_SETMOUSESPEED" value="0x0071"/>
<define name="SPI_GETSCREENSAVERRUNNING" value="0x0072"/>
<define name="SPI_GETDESKWALLPAPER" value="0x0073"/>
<define name="SPI_GETAUDIODESCRIPTION" value="0x0074"/>
<define name="SPI_SETAUDIODESCRIPTION" value="0x0075"/>
<define name="SPI_GETSCREENSAVESECURE" value="0x0076"/>
<define name="SPI_SETSCREENSAVESECURE" value="0x0077"/>
<define name="SPI_GETHUNGAPPTIMEOUT" value="0x0078"/>
<define name="SPI_SETHUNGAPPTIMEOUT" value="0x0079"/>
<define name="SPI_GETWAITTOKILLTIMEOUT" value="0x007A"/>
<define name="SPI_SETWAITTOKILLTIMEOUT" value="0x007B"/>
<define name="SPI_GETWAITTOKILLSERVICETIMEOUT" value="0x007C"/>
<define name="SPI_SETWAITTOKILLSERVICETIMEOUT" value="0x007D"/>
<define name="SPI_GETMOUSEDOCKTHRESHOLD" value="0x007E"/>
<define name="SPI_SETMOUSEDOCKTHRESHOLD" value="0x007F"/>
<define name="SPI_GETPENDOCKTHRESHOLD" value="0x0080"/>
<define name="SPI_SETPENDOCKTHRESHOLD" value="0x0081"/>
<define name="SPI_GETWINARRANGING" value="0x0082"/>
<define name="SPI_SETWINARRANGING" value="0x0083"/>
<define name="SPI_GETMOUSEDRAGOUTTHRESHOLD" value="0x0084"/>
<define name="SPI_SETMOUSEDRAGOUTTHRESHOLD" value="0x0085"/>
<define name="SPI_GETPENDRAGOUTTHRESHOLD" value="0x0086"/>
<define name="SPI_SETPENDRAGOUTTHRESHOLD" value="0x0087"/>
<define name="SPI_GETMOUSESIDEMOVETHRESHOLD" value="0x0088"/>
<define name="SPI_SETMOUSESIDEMOVETHRESHOLD" value="0x0089"/>
<define name="SPI_GETPENSIDEMOVETHRESHOLD" value="0x008A"/>
<define name="SPI_SETPENSIDEMOVETHRESHOLD" value="0x008B"/>
<define name="SPI_GETDRAGFROMMAXIMIZE" value="0x008C"/>
<define name="SPI_SETDRAGFROMMAXIMIZE" value="0x008D"/>
<define name="SPI_GETSNAPSIZING" value="0x008E"/>
<define name="SPI_SETSNAPSIZING" value="0x008F"/>
<define name="SPI_GETDOCKMOVING" value="0x0090"/>
<define name="SPI_SETDOCKMOVING" value="0x0091"/>
<define name="IDOK" value="1"/>
<define name="IDCANCEL" value="2"/>
<define name="IDABORT" value="3"/>

View File

@ -5139,6 +5139,17 @@
<not-uninit/>
</arg>
</function>
<!-- void wxString::SetChar(size_t n, wxUniChar ch) -->
<function name="wxString::SetChar">
<returnValue type="size_t"/>
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1" direction="in">
<not-uninit/>
<valid>0:</valid>
</arg>
<arg nr="2" direction="in"/>
</function>
<!-- wxUniChar wxString::GetChar(size_t n) const -->
<function name="wxString::GetChar">
<returnValue type="wxUniChar"/>
@ -5528,6 +5539,19 @@
<not-bool/>
</arg>
</function>
<!-- virtual wxString wxListbox::GetString (unsigned int n) const-->
<function name="wxListbox::GetString">
<noreturn>false</noreturn>
<leak-ignore/>
<use-retval/>
<const/>
<returnValue type="wxString"/>
<arg nr="1" direction="in">
<valid>0:</valid>
<not-uninit/>
<not-bool/>
</arg>
</function>
<!--virtual int wxListBox::GetSelection(void) const-->
<function name="wxListBox::GetSelection">
<noreturn>false</noreturn>
@ -6189,6 +6213,7 @@
<use-retval/>
</function>
<!--virtual bool wxWindow::Enable(bool enable = true)-->
<!--virtual bool wxComboBox::Enable(bool enable = true)-->
<!--virtual bool wxButton::Enable(bool enable = true)-->
<!--virtual bool wxAnyButton::Enable(bool enable = true)-->
<!--virtual bool wxBitmapButton::Enable(bool enable = true)-->
@ -6201,7 +6226,7 @@
<!--virtual bool wxStaticBitmap::Enable(bool enable = true)-->
<!--virtual bool wxSpinCtrl::Enable(bool enable = true)-->
<!--virtual bool wxOwnerDrawnComboBox::Enable(bool enable = true)-->
<function name="wxWindow::Enable,wxButton::Enable,wxStaticBitmap::Enable,wxSpinCtrl::Enable,wxAnyButton::Enable,wxCommandLinkButton::Enable,wxToggleButton::Enable,wxOwnerDrawnComboBox::Enable,wxBitmapToggleButton::Enable,wxContextHelpButton::Enable,wxBitmapButton::Enable,wxPropertyGridManager::Enable,wxFilePickerCtrl::Enable">
<function name="wxWindow::Enable,wxComboBox::Enable,wxButton::Enable,wxStaticBitmap::Enable,wxSpinCtrl::Enable,wxAnyButton::Enable,wxCommandLinkButton::Enable,wxToggleButton::Enable,wxOwnerDrawnComboBox::Enable,wxBitmapToggleButton::Enable,wxContextHelpButton::Enable,wxBitmapButton::Enable,wxPropertyGridManager::Enable,wxFilePickerCtrl::Enable">
<noreturn>false</noreturn>
<leak-ignore/>
<returnValue type="bool"/>
@ -7387,6 +7412,25 @@
</arg>
<arg nr="6" direction="in" default="wxPanel"/>
</function>
<!-- bool wxFrame::Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr) -->
<function name="wxFrame::Create">
<noreturn>false</noreturn>
<leak-ignore/>
<returnValue type="bool"/>
<arg nr="1" direction="in">
<not-uninit/>
</arg>
<arg nr="2" default="wxID_ANY" direction="in">
<not-uninit/>
<not-bool/>
</arg>
<arg nr="3" direction="in" default="wxDefaultPosition"/>
<arg nr="4" direction="in" default="wxDefaultSize"/>
<arg nr="5" direction="in" default="wxDEFAULT_FRAME_STYLE">
<not-bool/>
</arg>
<arg nr="6" direction="in" default="wxFrame"/>
</function>
<!-- virtual bool wxImageList::GetSize(int index, int & width, int & height) const -->
<function name="wxImageList::GetSize">
<noreturn>false</noreturn>
@ -9888,7 +9932,8 @@
</function>
<!-- wxSizerItem* wxSizer::AddStretchSpacer (int prop = 1) -->
<!-- wxSizerItem* wxBoxSizer::AddStretchSpacer (int prop = 1) -->
<function name="wxSizer::AddStretchSpacer,wxBoxSizer::AddStretchSpacer">
<!-- wxSizerItem* wxStaticBoxSizer::AddStretchSpacer (int prop = 1) -->
<function name="wxSizer::AddStretchSpacer,wxBoxSizer::AddStretchSpacer,wxStaticBoxSizer::AddStretchSpacer">
<noreturn>false</noreturn>
<leak-ignore/>
<returnValue type="wxSizerItem *"/>
@ -11158,6 +11203,21 @@
<arg nr="4" direction="in" default="wxEmptyString"/>
<arg nr="5" direction="in" default="wxITEM_NORMAL"/>
</function>
<!-- wxAuiToolBarItem* wxAuiToolBar::AddTool(int tool_id, const wxString & label, const wxBitmap & bitmap, const wxString & short_help_string = wxEmptyString,
wxItemKind kind = wxITEM_NORMAL) -->
<function name="wxToolBar::AddTool">
<leak-ignore/>
<noreturn>false</noreturn>
<returnValue type="wxAuiToolBarItem*"/>
<arg nr="1" direction="in">
<not-uninit/>
<not-bool/>
</arg>
<arg nr="2" direction="in"/>
<arg nr="3" direction="in"/>
<arg nr="4" direction="in" default="wxEmptyString"/>
<arg nr="5" direction="in" default="wxITEM_NORMAL"/>
</function>
<!-- wxString wxString::SubString( size_t from, size_t to ) const -->
<function name="wxString::SubString">
<leak-ignore/>
@ -13057,6 +13117,17 @@
<use-retval/>
<arg nr="1"/>
</function>
<!-- bool wxAuiManager::AddPane(wxWindow * window, const wxAuiPaneInfo & pane_info)-->
<!-- bool wxAuiManager::AddPane(wxWindow * window, int direction = wxLEFT, const wxString & caption = wxEmptyString) -->
<!-- bool wxAuiManager::AddPane(wxWindow * window, const wxAuiPaneInfo & pane_info, const wxPoint & drop_pos) -->
<function name="wxAuiManager::AddPane">
<noreturn>false</noreturn>
<returnValue type="bool"/>
<leak-ignore/>
<arg nr="1"/>
<arg nr="2" direction="in" default="wxLEFT"/>
<arg nr="3" direction="in" default="wxEmptyString"/>
</function>
<!-- virtual void wxConfigBase::SetPath(const wxString &strPath) = 0 -->
<!-- virtual void wxConfig::SetPath(const wxString &strPath) -->
<!-- virtual void wxFileConfig::SetPath(const wxString &strPath)-->
@ -13357,6 +13428,13 @@
<not-null/>
</arg>
</function>
<!-- virtual void wxGridCellEnumRenderer::SetParameters (const wxString &params)-->
<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>
@ -14241,4 +14319,36 @@
<returnValue type="void"/>
<arg nr="1" direction="in"/>
</function>
<!-- long wxExecute (const wxString & command, int flags = wxEXEC_ASYNC, wxProcess * callback = NULL, const wxExecuteEnv * env = NULL) -->
<!-- long wxExecute (char ** argv , int flags = wxEXEC_ASYNC, wxProcess * callback = NULL, const wxExecuteEnv * env = NULL) -->
<!-- long wxExecute (wchar_t ** argv , int flags = wxEXEC_ASYNC, wxProcess * callback = NULL, const wxExecuteEnv * env = NULL) -->
<!-- long wxExecute (wxString &command , wxArrayString &output , int flags = 0 , const wxExecuteEnv * env = NULL) -->
<!-- long wxExecute(const wxString & command , wxArrayString & output , wxArrayString & errors , int flags = 0, const wxExecuteEnv * env = NULL) -->
<function name="wxExecute">
<noreturn>false</noreturn>
<returnValue type="long"/>
<arg nr="1" direction="in"/>
<arg nr="4" default="0" direction="in"/>
<arg nr="5" default="NULL" direction="in"/>
</function>
<!-- bool wxImageList::Create (int width, int height, bool mask = true, int initialCount = 1) -->
<function name="wxImageList::Create">
<noreturn>false</noreturn>
<returnValue type="bool"/>
<arg nr="1" direction="in">
<not-uninit/>
<valid>0:</valid>
</arg>
<arg nr="2" direction="in">
<not-uninit/>
<valid>0:</valid>
</arg>
<arg nr="3" default="true" direction="in">
<not-uninit/>
</arg>
<arg nr="4" default="1" direction="in">
<not-uninit/>
<valid>0:</valid>
</arg>
</function>
</def>

View File

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

View File

@ -1026,15 +1026,15 @@ void simplecpp::TokenList::constFoldBitwise(Token *tok)
{
Token * const tok1 = tok;
for (const char *op = "&^|"; *op; op++) {
const std::string* altop;
const std::string* alternativeOp;
if (*op == '&')
altop = &BITAND;
alternativeOp = &BITAND;
else if (*op == '|')
altop = &BITOR;
alternativeOp = &BITOR;
else
altop = &XOR;
alternativeOp = &XOR;
for (tok = tok1; tok && tok->op != ')'; tok = tok->next) {
if (tok->op != *op && !isAlternativeBinaryOp(tok, *altop))
if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp))
continue;
if (!tok->previous || !tok->previous->number)
continue;
@ -1472,7 +1472,8 @@ namespace simplecpp {
}
const Token *appendTokens(TokenList *tokens,
const Token *lpar,
const Location &rawloc,
const Token * const lpar,
const std::map<TokenString,Macro> &macros,
const std::set<TokenString> &expandedmacros,
const std::vector<const Token*> &parametertokens) const {
@ -1483,17 +1484,17 @@ namespace simplecpp {
while (sameline(lpar, tok)) {
if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) {
// A##B => AB
tok = expandHashHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens);
tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens);
} else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') {
tok = expandHash(tokens, tok->location, tok, macros, expandedmacros, parametertokens);
tok = expandHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens);
} else {
if (!expandArg(tokens, tok, tok->location, macros, expandedmacros, parametertokens)) {
if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) {
bool expanded = false;
const std::map<TokenString, Macro>::const_iterator it = macros.find(tok->str());
if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
const Macro &m = it->second;
if (!m.functionLike()) {
m.expand(tokens, tok->location, tok, macros, expandedmacros);
m.expand(tokens, rawloc, tok, macros, expandedmacros);
expanded = true;
}
}
@ -1511,6 +1512,8 @@ namespace simplecpp {
tok = tok->next;
}
}
for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next)
tok2->location = lpar->location;
return sameline(lpar,tok) ? tok : NULL;
}
@ -1612,13 +1615,19 @@ namespace simplecpp {
hashToken = hashToken->next;
++numberOfHash;
}
if (numberOfHash == 4) {
if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) {
// # ## # => ##
output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros)));
tok = hashToken;
continue;
}
if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) {
output->push_back(new Token(*tok));
tok = tok->next;
continue;
}
tok = tok->next;
if (tok == endToken) {
output->push_back(new Token(*tok->previous));
@ -1645,6 +1654,41 @@ namespace simplecpp {
return functionLike() ? parametertokens2.back()->next : nameTokInst->next;
}
const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const std::map<TokenString,Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) {
output->takeTokens(temp);
return tok->next;
}
if (!sameline(tok, tok->next)) {
output->takeTokens(temp);
return tok->next;
}
const std::map<TokenString, Macro>::const_iterator it = macros.find(temp.cback()->str());
if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) {
output->takeTokens(temp);
return tok->next;
}
const Macro &calledMacro = it->second;
if (!calledMacro.functionLike()) {
output->takeTokens(temp);
return tok->next;
}
TokenList temp2(files);
temp2.push_back(new Token(temp.cback()->str(), tok->location));
const Token *tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens);
if (!tok2)
return tok->next;
output->takeTokens(temp);
output->deleteToken(output->back());
calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros);
return tok2->next;
}
const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const std::map<TokenString,Macro> &macros, const std::set<TokenString> &expandedmacros, const std::vector<const Token*> &parametertokens) const {
// Not name..
if (!tok->name) {
@ -1655,63 +1699,36 @@ namespace simplecpp {
// Macro parameter..
{
TokenList temp(files);
if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) {
if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) {
output->takeTokens(temp);
return tok->next;
}
if (!sameline(tok, tok->next)) {
output->takeTokens(temp);
return tok->next;
}
const std::map<TokenString, Macro>::const_iterator it = macros.find(temp.cback()->str());
if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) {
output->takeTokens(temp);
return tok->next;
}
const Macro &calledMacro = it->second;
if (!calledMacro.functionLike()) {
output->takeTokens(temp);
return tok->next;
}
TokenList temp2(files);
temp2.push_back(new Token(temp.cback()->str(), tok->location));
const Token *tok2 = appendTokens(&temp2, tok->next, macros, expandedmacros, parametertokens);
if (!tok2)
return tok->next;
output->takeTokens(temp);
output->deleteToken(output->back());
calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros);
return tok2->next;
}
if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens))
return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens);
}
// Macro..
const std::map<TokenString, Macro>::const_iterator it = macros.find(tok->str());
if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) {
std::set<std::string> expandedmacros2(expandedmacros);
expandedmacros2.insert(tok->str());
const Macro &calledMacro = it->second;
if (!calledMacro.functionLike())
return calledMacro.expand(output, loc, tok, macros, expandedmacros);
if (!calledMacro.functionLike()) {
TokenList temp(files);
calledMacro.expand(&temp, loc, tok, macros, expandedmacros);
return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens);
}
if (!sameline(tok, tok->next) || tok->next->op != '(') {
output->push_back(newMacroToken(tok->str(), loc, true));
return tok->next;
}
TokenList tokens(files);
tokens.push_back(new Token(*tok));
const Token *tok2 = appendTokens(&tokens, tok->next, macros, expandedmacros, parametertokens);
const Token *tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens);
if (!tok2) {
output->push_back(newMacroToken(tok->str(), loc, true));
return tok->next;
}
calledMacro.expand(output, loc, tokens.cfront(), macros, expandedmacros);
return tok2->next;
TokenList temp(files);
calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros);
return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros2, parametertokens);
}
else if (tok->str() == DEFINED) {
@ -1879,7 +1896,7 @@ namespace simplecpp {
if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') {
const std::map<TokenString,Macro>::const_iterator it = macros.find(strAB);
if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) {
const Token *tok2 = appendTokens(&tokens, B->next, macros, expandedmacros, parametertokens);
const Token *tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens);
if (tok2)
nextTok = tok2->next;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

13
gui/help/index.html Normal file
View File

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

View File

@ -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
&quot;Warning details&quot; at the bottom.</p>
<p>You can right click warnings to get options. The difference of
&quot;hiding&quot; a warning and &quot;suppressing&quot; 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>

View File

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

74
gui/help/preferences.html Normal file
View File

@ -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 &quot;No errors found&quot; message when no errors found</b><br>
If you want to get a message box about this.</p>
<p><b>Display error id column &quot;Id&quot;</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>

View File

@ -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 &quot;too high&quot; value is that Cppcheck will be slow.</p>
<p>Max recursion in template instantiation: Max recursion when Cppcheck
instantiates templates. The risk with a &quot;too high&quot; 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>

View File

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

22
gui/help/tagging.html Normal file
View File

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

69
gui/help/walkthrough.html Normal file
View File

@ -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 &quot;Hide&quot; and &quot;Suppress&quot; 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>

53
gui/helpdialog.cpp Normal file
View File

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

34
gui/helpdialog.h Normal file
View File

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

67
gui/helpdialog.ui Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &quot;soundy&quot; 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>

View File

@ -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);
isTagged = true;
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);
@ -1133,7 +1210,7 @@ void ResultsTree::saveResults(Report *report) const
report->writeFooter();
}
void ResultsTree::saveErrors(Report *report, QStandardItem *fileItem) const
void ResultsTree::saveErrors(Report *report, const QStandardItem *fileItem) const
{
if (!fileItem) {
return;
@ -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));
}

View File

@ -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 &current);
/**
* 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.
*/
@ -333,7 +327,7 @@ protected:
* @param report Report that errors are saved to
* @param fileItem Item whose errors to save
*/
void saveErrors(Report *report, QStandardItem *fileItem) const;
void saveErrors(Report *report, const QStandardItem *fileItem) const;
/**
* @brief Convert a severity string to a icon filename
@ -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;

View File

@ -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);
@ -171,13 +170,6 @@ void ResultsView::updateFromOldReport(const QString &filename) const
void ResultsView::save(const QString &filename, Report::Type type) const
{
if (!hasResults()) {
QMessageBox msgBox;
msgBox.setText(tr("No errors found, nothing to save."));
msgBox.setIcon(QMessageBox::Critical);
msgBox.exec();
}
Report *report = nullptr;
switch (type) {
@ -393,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;
}
@ -408,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;
}
@ -433,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);
}
QStringList symbols;
if (data.contains("symbolNames"))
symbols = data["symbolNames"].toString().split("\n");
QTextStream in(&file);
mUI.mCode->setError(in.readAll(), lineNumber, symbols);
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)
@ -455,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)

View File

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

View File

@ -159,49 +159,34 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Configured contracts:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="mListAddedContracts">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Missing contracts:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="mListMissingContracts">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Configured contracts:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="mListAddedContracts">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Missing contracts:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="mListMissingContracts">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

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

View File

@ -59,12 +59,6 @@ void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::
const std::string afile = getFilename(fs.filename);
fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(Path::fromNativeSeparators(fs.filename)) << std::endl;
}
std::ofstream fc(buildDir + "/__temp__.c");
fc << "int x;\n";
std::ofstream fcpp(buildDir + "/__temp__.cpp");
fcpp << "int x;\n";
}
void AnalyzerInformation::close()

View File

@ -449,7 +449,9 @@ const Token* getCondTokFromEnd(const Token* endBlock)
bool extractForLoopValues(const Token *forToken,
nonneg int * const varid,
bool * const knownInitValue,
MathLib::bigint * const initValue,
bool * const partialCond,
MathLib::bigint * const stepValue,
MathLib::bigint * const lastValue)
{
@ -458,10 +460,21 @@ bool extractForLoopValues(const Token *forToken,
const Token *initExpr = forToken->next()->astOperand2()->astOperand1();
const Token *condExpr = forToken->next()->astOperand2()->astOperand2()->astOperand1();
const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2();
if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%") || !initExpr->astOperand2()->hasKnownIntValue())
if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%"))
return false;
*varid = initExpr->astOperand1()->varId();
*initValue = initExpr->astOperand2()->getKnownIntValue();
*knownInitValue = initExpr->astOperand2()->hasKnownIntValue();
*initValue = (*knownInitValue) ? initExpr->astOperand2()->getKnownIntValue() : 0;
*partialCond = Token::Match(condExpr, "%oror%|&&");
visitAstNodes(condExpr, [varid, &condExpr](const Token *tok) {
if (Token::Match(tok, "%oror%|&&"))
return ChildrenToVisit::op1_and_op2;
if (Token::Match(tok, "<|<=") && tok->isBinaryOp() && tok->astOperand1()->varId() == *varid && tok->astOperand2()->hasKnownIntValue()) {
if (Token::Match(condExpr, "%oror%|&&") || tok->astOperand2()->getKnownIntValue() < condExpr->astOperand2()->getKnownIntValue())
condExpr = tok;
}
return ChildrenToVisit::none;
});
if (!Token::Match(condExpr, "<|<=") || !condExpr->isBinaryOp() || condExpr->astOperand1()->varId() != *varid || !condExpr->astOperand2()->hasKnownIntValue())
return false;
if (!incExpr || !incExpr->isUnaryOp("++") || incExpr->astOperand1()->varId() != *varid)
@ -550,7 +563,10 @@ bool exprDependsOnThis(const Token* expr, nonneg int depth)
// calling nonstatic method?
if (Token::Match(expr->previous(), "!!:: %name% (") && expr->function() && expr->function()->nestedIn && expr->function()->nestedIn->isClassOrStruct()) {
// is it a method of this?
const Scope *nestedIn = expr->scope()->functionOf;
const Scope* fScope = expr->scope();
while (!fScope->functionOf && fScope->nestedIn)
fScope = fScope->nestedIn;
const Scope* nestedIn = fScope->functionOf;
if (nestedIn && nestedIn->function)
nestedIn = nestedIn->function->token->scope();
while (nestedIn && nestedIn != expr->function()->nestedIn) {
@ -1304,7 +1320,7 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn)
return tok;
}
const Variable* getArgumentVar(const Token* tok, int argnr)
static const Variable* getArgumentVar(const Token* tok, int argnr)
{
if (!tok)
return nullptr;
@ -1573,6 +1589,27 @@ bool isVariablesChanged(const Token* start,
return false;
}
bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp)
{
for (const Token* tok = start; tok != end; tok = tok->next()) {
if (!exprDependsOnThis(tok))
continue;
if (Token::Match(tok->previous(), "%name% (")) {
if (tok->previous()->function()) {
if (!tok->previous()->function()->isConst())
return true;
else
continue;
} else if (!tok->previous()->isKeyword()) {
return true;
}
}
if (isVariableChanged(tok, indirect, settings, cpp))
return true;
}
return false;
}
int numberOfArguments(const Token *start)
{
int arguments=0;

View File

@ -114,7 +114,9 @@ const Token* getCondTokFromEnd(const Token* endBlock);
*/
bool extractForLoopValues(const Token *forToken,
nonneg int * const varid,
bool * const knownInitValue,
long long * const initValue,
bool * const partialCond,
long long * const stepValue,
long long * const lastValue);
@ -193,6 +195,8 @@ bool isVariablesChanged(const Token* start,
const Settings* settings,
bool cpp);
bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp);
const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int varid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);

View File

@ -87,7 +87,7 @@ static void divByZero(const Token *tok, const ExprEngine::Value &value, ExprEngi
return;
if (tok->isImpossibleIntValue(0))
return;
if (value.isUninit())
if (value.isUninit() && value.type != ExprEngine::ValueType::BailoutValue)
return;
float f = getKnownFloatValue(tok, 0.0f);
if (f > 0.0f || f < 0.0f)
@ -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())
@ -185,6 +224,29 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
return;
}
// container is not uninitialized
if (tok->valueType() && tok->valueType()->pointer==0 && tok->valueType()->container)
return;
// container element is not uninitialized
if (tok->str() == "[" &&
tok->astOperand1() &&
tok->astOperand1()->valueType() &&
tok->astOperand1()->valueType()->pointer==0 &&
tok->astOperand1()->valueType()->container) {
if (tok->astOperand1()->valueType()->container->stdStringLike)
return;
bool pointerType = false;
for (const Token *typeTok = tok->astOperand1()->valueType()->containerTypeToken; Token::Match(typeTok, "%name%|*|::|<"); typeTok = typeTok->next()) {
if (typeTok->str() == "<" && typeTok->link())
typeTok = typeTok->link();
if (typeTok->str() == "*")
pointerType = true;
}
if (!pointerType)
return;
}
// lhs in assignment
if (tok->astParent()->str() == "=" && tok == tok->astParent()->astOperand1())
return;
@ -193,20 +255,22 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
if (value.type == ExprEngine::ValueType::BailoutValue) {
if (tok->hasKnownValue())
return;
if (tok->function())
return;
if (Token::Match(tok, "<<|>>|,"))
// Only warn about the operands
if (!tok->variable())
// FIXME
return;
// lhs for scope operator
if (Token::Match(tok, "%name% ::"))
return;
if (tok->astParent()->str() == "::" && tok == tok->astParent()->astOperand1())
return;
if (tok->str() == "(")
// cast: result is not uninitialized if expression is initialized
// function: does not return a uninitialized value
// Object allocated on the stack
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
@ -219,18 +283,33 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
}
const Variable *var = tok->variable();
if (var && var->nameToken() == tok)
return;
if (var && !var->isLocal())
return; // FIXME
if (var && !var->isPointer()) {
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;
// Are there unconditional assignment?
if (var && Token::Match(var->nameToken(), "%varid% ;| %varid%| =", tok->varId()))
return;
// 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
bool inconclusive = false;
if (Token::Match(tok->astParent(), "[,(]")) {
const Token *parent = tok->astParent();
int count = 0;
@ -248,10 +327,16 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
const Variable *argvar = parent->astOperand1()->function()->getArgumentVar(count);
if (argvar && argvar->isReference() && !argvar->isConst())
return;
if (uninitData && argvar && !argvar->isConst())
return;
if (!uninitStructMember.empty() && dataBase->isC() && argvar && !argvar->isConst())
return;
if (uninitData && argvar && !argvar->isConst()) {
if (parent->astOperand1()->function()->hasBody())
return;
inconclusive = true;
}
if (!uninitStructMember.empty() && dataBase->isC() && argvar && !argvar->isConst()) {
if (parent->astOperand1()->function()->hasBody())
return;
inconclusive = true;
}
} else if (uninitData) {
if (dataBase->settings->library.getFunction(parent->astOperand1()))
return;
@ -262,6 +347,9 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
return;
}
if (inconclusive && !dataBase->settings->inconclusive)
return;
// Avoid FP for array declaration
const Token *parent = tok->astParent();
while (parent && parent->str() == "[")
@ -269,13 +357,16 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
if (!parent)
return;
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",
"$symbol:" + symbol + "\nCannot determine that '$symbol' is initialized" + inconclusiveMessage,
CWE_USE_OF_UNINITIALIZED_VARIABLE,
false,
inconclusive,
value.type == ExprEngine::ValueType::BailoutValue);
return;
}
@ -284,12 +375,14 @@ 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",
symbol + "Cannot determine that '" + uninitexpr + "' is initialized" + inconclusiveMessage,
CWE_USE_OF_UNINITIALIZED_VARIABLE,
false,
inconclusive,
value.type == ExprEngine::ValueType::BailoutValue);
}

View File

@ -1991,6 +1991,9 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
if (tok1->str() == "this" && tok1->previous()->isAssignmentOp())
return false;
// non const pointer cast
if (tok1->valueType() && tok1->valueType()->pointer > 0 && tok1->astParent() && tok1->astParent()->isCast() && !Token::simpleMatch(tok1->astParent(), "( const"))
return false;
const Token* lhs = tok1->previous();
if (lhs->str() == "&") {

View File

@ -452,7 +452,13 @@ void CheckCondition::duplicateCondition()
continue;
bool modified = false;
visitAstNodes(cond1, [&](const Token *tok3) {
visitAstNodes(cond1, [&](const Token* tok3) {
if (exprDependsOnThis(tok3)) {
if (isThisChanged(scope.classDef->next(), cond2, false, mSettings, mTokenizer->isCPP())) {
modified = true;
return ChildrenToVisit::done;
}
}
if (tok3->varId() > 0 &&
isVariableChanged(scope.classDef->next(), cond2, tok3->varId(), false, mSettings, mTokenizer->isCPP())) {
modified = true;
@ -682,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))
@ -700,24 +701,22 @@ void CheckCondition::multiCondition2()
identicalInnerConditionError(firstCondition, cond2, errorPath);
}
}
}
return ChildrenToVisit::none;
});
} else {
std::stack<const Token *> tokens2;
tokens2.push(cond2);
while (!tokens2.empty()) {
const Token *secondCondition = tokens2.top();
tokens2.pop();
if (!secondCondition)
continue;
if (secondCondition->str() == "||" || secondCondition->str() == "&&") {
tokens2.push(secondCondition->astOperand1());
tokens2.push(secondCondition->astOperand2());
} else if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) &&
isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) {
if (!isAliased(vars))
visitAstNodes(cond2, [&](const Token *secondCondition) {
if (secondCondition->str() == "||" || secondCondition->str() == "&&")
return ChildrenToVisit::op1_and_op2;
if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) &&
isSameExpression(mTokenizer->isCPP(), true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) {
if (!isAliased(vars) && !mTokenizer->hasIfdef(cond1, secondCondition)) {
identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath);
return ChildrenToVisit::done;
}
}
}
return ChildrenToVisit::none;
});
}
}
if (Token::Match(tok, "%name% (") && isVariablesChanged(tok, tok->linkAt(1), true, varsInCond, mSettings, mTokenizer->isCPP())) {
@ -1436,20 +1435,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()) {
@ -1463,25 +1457,23 @@ void CheckCondition::alwaysTrueFalse()
// don't warn when condition checks sizeof result
bool hasSizeof = false;
tokens.push(tok);
while (!tokens.empty()) {
const Token *tok2 = tokens.top();
tokens.pop();
bool hasNonNumber = false;
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
break;
}
if (tokens.empty() && hasSizeof)
hasNonNumber = true;
return ChildrenToVisit::none;
});
if (!hasNonNumber && hasSizeof)
continue;
alwaysTrueFalseError(tok, &tok->values().front());

View File

@ -463,23 +463,21 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
VarInfo varInfo1(*varInfo); // VarInfo for if code
VarInfo varInfo2(*varInfo); // VarInfo for else code
// Skip expressions before commas
const Token * astOperand2AfterCommas = tok->next()->astOperand2();
while (Token::simpleMatch(astOperand2AfterCommas, ","))
astOperand2AfterCommas = astOperand2AfterCommas->astOperand2();
// Recursively scan variable comparisons in condition
std::stack<const Token *> tokens;
tokens.push(tok->next()->astOperand2());
while (!tokens.empty()) {
const Token *tok3 = tokens.top();
tokens.pop();
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) {
@ -496,7 +494,7 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
varInfo2.erase(vartok->varId());
}
}
continue;
return ChildrenToVisit::none;
}
const Token *vartok = nullptr;
@ -513,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);
@ -856,7 +855,8 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin
}
int argNr = 1;
for (const Token *arg = tokFirstArg; arg; arg = arg->nextArgument()) {
for (const Token *funcArg = tokFirstArg; funcArg; funcArg = funcArg->nextArgument()) {
const Token* arg = funcArg;
if (mTokenizer->isCPP() && arg->str() == "new") {
arg = arg->next();
if (Token::simpleMatch(arg, "( std :: nothrow )"))

View File

@ -764,6 +764,8 @@ void CheckMemoryLeakStructMember::check()
continue;
if (var->typeEndToken()->isStandardType())
continue;
if (var->scope()->hasInlineOrLambdaFunction())
continue;
checkStructVariable(var);
}
}

View File

@ -882,6 +882,9 @@ void CheckOther::checkVariableScope()
if (var->isConst())
continue;
if (mTokenizer->hasIfdef(var->nameToken(), var->scope()->bodyEnd))
continue;
// reference of range for loop variable..
if (Token::Match(var->nameToken()->previous(), "& %var% = %var% .")) {
const Token *otherVarToken = var->nameToken()->tokAt(2);
@ -2011,7 +2014,9 @@ void CheckOther::checkDuplicateExpression()
if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=")) {
if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true))
continue;
const bool followVar = !isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&");
const bool pointerDereference = (tok->astOperand1() && tok->astOperand1()->isUnaryOp("*")) ||
(tok->astOperand2() && tok->astOperand2()->isUnaryOp("*"));
const bool followVar = (!isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&")) && !pointerDereference;
if (isSameExpression(mTokenizer->isCPP(),
true,
tok->astOperand1(),
@ -2637,6 +2642,7 @@ void CheckOther::checkUnusedLabel()
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
for (const Scope * scope : symbolDatabase->functionScopes) {
const bool hasIfdef = mTokenizer->hasIfdef(scope->bodyStart, scope->bodyEnd);
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
if (!tok->scope()->isExecutable())
tok = tok->scope()->bodyEnd;
@ -2644,25 +2650,35 @@ void CheckOther::checkUnusedLabel()
if (Token::Match(tok, "{|}|; %name% :") && tok->strAt(1) != "default") {
const std::string tmp("goto " + tok->strAt(1));
if (!Token::findsimplematch(scope->bodyStart->next(), tmp.c_str(), tmp.size(), scope->bodyEnd->previous()))
unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch);
unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch, hasIfdef);
}
}
}
}
void CheckOther::unusedLabelError(const Token* tok, bool inSwitch)
void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef)
{
if (inSwitch) {
if (!tok || mSettings->isEnabled(Settings::WARNING))
reportError(tok, Severity::warning, "unusedLabelSwitch",
"$symbol:" + (tok ? tok->str() : emptyString) + "\n"
"Label '$symbol' is not used. Should this be a 'case' of the enclosing switch()?", CWE398, false);
} else {
if (!tok || mSettings->isEnabled(Settings::STYLE))
reportError(tok, Severity::style, "unusedLabel",
"$symbol:" + (tok ? tok->str() : emptyString) + "\n"
"Label '$symbol' is not used.", CWE398, false);
}
if (tok && !mSettings->isEnabled(inSwitch ? Settings::WARNING : Settings::STYLE))
return;
std::string id = "unusedLabel";
if (inSwitch)
id += "Switch";
if (hasIfdef)
id += "Configuration";
std::string msg = "$symbol:" + (tok ? tok->str() : emptyString) + "\nLabel '$symbol' is not used.";
if (hasIfdef)
msg += " There is #if in function body so the label might be used in code that is removed by the preprocessor.";
if (inSwitch)
msg += " Should this be a 'case' of the enclosing switch()?";
reportError(tok,
inSwitch ? Severity::warning : Severity::style,
id,
msg,
CWE398,
false);
}

View File

@ -266,7 +266,7 @@ private:
void commaSeparatedReturnError(const Token *tok);
void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive);
void raceAfterInterlockedDecrementError(const Token* tok);
void unusedLabelError(const Token* tok, bool inSwitch);
void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef);
void unknownEvaluationOrder(const Token* tok);
static bool isMovedParameterAllowedForInconclusiveFunction(const Token * tok);
void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive);
@ -331,8 +331,10 @@ private:
c.nanInArithmeticExpressionError(nullptr);
c.commaSeparatedReturnError(nullptr);
c.redundantPointerOpError(nullptr, "varname", false);
c.unusedLabelError(nullptr, true);
c.unusedLabelError(nullptr, false);
c.unusedLabelError(nullptr, false, false);
c.unusedLabelError(nullptr, false, true);
c.unusedLabelError(nullptr, true, false);
c.unusedLabelError(nullptr, true, true);
c.unknownEvaluationOrder(nullptr);
c.accessMovedError(nullptr, "v", nullptr, false);
c.funcArgNamesDifferent("function", 1, nullptr, nullptr);

View File

@ -2430,19 +2430,28 @@ void CheckStl::localMutexError(const Token* tok)
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% . 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()))

View File

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

View File

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

View File

@ -173,15 +173,18 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set<std::string>
if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) {
// Treat the pointer as initialized until it is assigned by malloc
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
if (Token::Match(tok, "[;{}] %varid% = %name% (", arg.declarationId()) &&
mSettings->library.returnuninitdata.count(tok->strAt(3)) == 1U) {
if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType()))
checkStruct(tok, arg);
else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) {
Alloc alloc = NO_ALLOC;
const std::map<int, VariableValue> variableValue;
checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue);
}
if (!Token::Match(tok, "[;{}] %varid% = %name% (", arg.declarationId()))
continue;
const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(tok->tokAt(3));
if (!allocFunc || allocFunc->initData)
continue;
if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType()))
checkStruct(tok, arg);
else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) {
Alloc alloc = NO_ALLOC;
const std::map<int, VariableValue> variableValue;
checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue);
}
}
}
@ -716,10 +719,12 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var
const Token *rhs = tok->next()->astOperand2();
while (rhs && rhs->isCast())
rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1();
if (rhs && Token::Match(rhs->previous(), "%name% (") &&
mSettings->library.returnuninitdata.count(rhs->previous()->str()) > 0U) {
*alloc = NO_CTOR_CALL;
continue;
if (rhs && Token::Match(rhs->previous(), "%name% (")) {
const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(rhs->astOperand1());
if (allocFunc && !allocFunc->initData) {
*alloc = NO_CTOR_CALL;
continue;
}
}
}
if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) {
@ -876,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 {

View File

@ -21,6 +21,7 @@
#include "checkunusedvar.h"
#include "astutils.h"
#include "preprocessor.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
@ -1340,6 +1341,17 @@ void CheckUnusedVar::checkStructMemberUsage()
// Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
if (scope.bodyEnd->isAttributePacked())
continue;
if (const Preprocessor *preprocessor = mTokenizer->getPreprocessor()) {
bool isPacked = false;
for (const Directive &d: preprocessor->getDirectives()) {
if (d.str == "#pragma pack(1)" && d.file == mTokenizer->list.getFiles().front() && d.linenr < scope.bodyStart->linenr()) {
isPacked=true;
break;
}
}
if (isPacked)
continue;
}
// Bail out if struct/union contains any functions
if (!scope.functionList.empty())

View File

@ -37,6 +37,15 @@
# define NOEXCEPT
#endif
// C++11 noreturn
#if (defined(__GNUC__) && (__GNUC__ >= 5)) \
|| (defined(__clang__) && (defined (__cplusplus)) && (__cplusplus >= 201103L)) \
|| defined(__CPPCHECK__)
# define NORETURN [[noreturn]]
#else
# define NORETURN
#endif
#define REQUIRES(msg, ...) class=typename std::enable_if<__VA_ARGS__::value>::type
#include <string>

View File

@ -688,16 +688,17 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
continue;
}
Tokenizer mTokenizer(&mSettings, this);
Tokenizer tokenizer(&mSettings, this);
tokenizer.setPreprocessor(&preprocessor);
if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE)
mTokenizer.setTimerResults(&s_timerResults);
tokenizer.setTimerResults(&s_timerResults);
try {
// Create tokens, skip rest of iteration if failed
{
Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults);
simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true);
mTokenizer.createTokens(std::move(tokensP));
tokenizer.createTokens(std::move(tokensP));
}
hasValidConfig = true;
@ -708,7 +709,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...");
}
if (!mTokenizer.tokens())
if (!tokenizer.tokens())
continue;
// skip rest of iteration if just checking configuration
@ -716,11 +717,11 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
continue;
// Check raw tokens
checkRawTokens(mTokenizer);
checkRawTokens(tokenizer);
// Simplify tokens into normal form, skip rest of iteration if failed
Timer timer2("Tokenizer::simplifyTokens1", mSettings.showtime, &s_timerResults);
bool result = mTokenizer.simplifyTokens1(mCurrentConfig);
bool result = tokenizer.simplifyTokens1(mCurrentConfig);
timer2.stop();
if (!result)
continue;
@ -733,13 +734,13 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
fdump << " <cpp version=\"" << mSettings.standards.getCPP() << "\"/>" << std::endl;
fdump << " </standards>" << std::endl;
preprocessor.dump(fdump);
mTokenizer.dump(fdump);
tokenizer.dump(fdump);
fdump << "</dump>" << std::endl;
}
// Skip if we already met the same simplified token list
if (mSettings.force || mSettings.maxConfigs > 1) {
const unsigned long long checksum = mTokenizer.list.calculateChecksum();
const unsigned long long checksum = tokenizer.list.calculateChecksum();
if (checksums.find(checksum) != checksums.end()) {
if (mSettings.debugwarnings)
purgedConfigurationMessage(filename, mCurrentConfig);
@ -749,11 +750,11 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
}
// Check normal tokens
checkNormalTokens(mTokenizer);
checkNormalTokens(tokenizer);
// Analyze info..
if (!mSettings.buildDir.empty())
checkUnusedFunctions.parseTokens(mTokenizer, filename.c_str(), &mSettings);
checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings);
// simplify more if required, skip rest of iteration if failed
if (mSimplify && hasRule("simple")) {
@ -761,13 +762,13 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
// if further simplification fails then skip rest of iteration
Timer timer3("Tokenizer::simplifyTokenList2", mSettings.showtime, &s_timerResults);
result = mTokenizer.simplifyTokenList2();
result = tokenizer.simplifyTokenList2();
timer3.stop();
if (!result)
continue;
if (!Settings::terminated())
executeRules("simple", mTokenizer);
executeRules("simple", tokenizer);
}
} catch (const simplecpp::Output &o) {
@ -779,16 +780,16 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
} catch (const InternalError &e) {
std::list<ErrorMessage::FileLocation> locationList;
if (e.token) {
ErrorMessage::FileLocation loc(e.token, &mTokenizer.list);
ErrorMessage::FileLocation loc(e.token, &tokenizer.list);
locationList.push_back(loc);
} else {
ErrorMessage::FileLocation loc(mTokenizer.list.getSourceFilePath(), 0, 0);
ErrorMessage::FileLocation loc(tokenizer.list.getSourceFilePath(), 0, 0);
ErrorMessage::FileLocation loc2(filename, 0, 0);
locationList.push_back(loc2);
locationList.push_back(loc);
}
ErrorMessage errmsg(locationList,
mTokenizer.list.getSourceFilePath(),
tokenizer.list.getSourceFilePath(),
Severity::error,
e.errorMessage,
e.id,

View File

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

View File

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

View File

@ -154,6 +154,7 @@ namespace {
const Token *tok;
const std::string what;
};
struct TerminateExpression {};
}
static std::string str(ExprEngine::ValuePtr val)
@ -716,6 +717,8 @@ namespace {
const std::string c = line.substr(pos, end-pos);
pos = end;
d.constraints.push_back(c);
} else {
throw ExprEngineException(nullptr, "Internal Error: Data::parsestr(), line:" + line);
}
}
importData->push_back(d);
@ -1887,10 +1890,22 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data)
}
}
auto val = getValueRangeFromValueType(tok->valueType(), data);
call(data.callbacks, tok, val, &data);
else if (const auto *f = data.settings->library.getAllocFuncInfo(tok->astOperand1())) {
if (!f->initData) {
const std::string name = data.getNewSymbolName();
auto size = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), 1, ~0U);
auto val = std::make_shared<ExprEngine::UninitValue>();
auto result = std::make_shared<ExprEngine::ArrayValue>(name, size, val, false, false, false);
call(data.callbacks, tok, result, &data);
data.functionCall();
return result;
}
}
auto result = getValueRangeFromValueType(tok->valueType(), data);
call(data.callbacks, tok, result, &data);
data.functionCall();
return val;
return result;
}
static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data)
@ -2139,6 +2154,9 @@ static ExprEngine::ValuePtr executeStringLiteral(const Token *tok, Data &data)
static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data)
{
if (data.settings->terminated())
throw TerminateExpression();
if (tok->str() == "return")
return executeReturn(tok, data);
@ -2223,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;
@ -2359,8 +2381,9 @@ static std::string execute(const Token *start, const Token *end, Data &data)
if (Token::simpleMatch(tok, "for (")) {
nonneg int varid;
bool hasKnownInitValue, partialCond;
MathLib::bigint initValue, stepValue, lastValue;
if (extractForLoopValues(tok, &varid, &initValue, &stepValue, &lastValue)) {
if (extractForLoopValues(tok, &varid, &hasKnownInitValue, &initValue, &partialCond, &stepValue, &lastValue) && hasKnownInitValue && !partialCond) {
auto loopValues = std::make_shared<ExprEngine::IntRange>(data.getNewSymbolName(), initValue, lastValue);
data.assignValue(tok, varid, loopValues);
tok = tok->linkAt(1);
@ -2474,6 +2497,8 @@ void ExprEngine::executeAllFunctions(ErrorLogger *errorLogger, const Tokenizer *
// FIXME.. there should not be exceptions
std::string functionName = functionScope->function->name();
std::cout << "Verify: Aborted analysis of function '" << functionName << "': " << e.what() << std::endl;
} catch (const TerminateExpression &) {
break;
}
}
}
@ -2546,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);

View File

@ -317,11 +317,9 @@ namespace ExprEngine {
bool isEqual(DataBase * /*dataBase*/, int /*value*/) const OVERRIDE {
return true;
}
/* FIXME: This is too noisy
bool isUninit() const OVERRIDE {
return true;
}
*/
};
typedef std::function<void(const Token *, const ExprEngine::Value &, ExprEngine::DataBase *)> Callback;

View File

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

View File

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

View File

@ -194,7 +194,9 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
temp.groupId = allocationId;
if (memorynode->Attribute("init", "false"))
returnuninitdata.insert(memorynode->GetText());
temp.initData = false;
else
temp.initData = true;
const char *arg = memorynode->Attribute("arg");
if (arg)

View File

@ -78,6 +78,7 @@ public:
int bufferSizeArg1;
int bufferSizeArg2;
int reallocArg;
bool initData;
};
/** get allocation info for function */
@ -418,7 +419,6 @@ public:
return -1;
}
std::set<std::string> returnuninitdata;
std::vector<std::string> defines; // to provide some library defines
std::set<std::string> smartPointers;

View File

@ -91,6 +91,9 @@ public:
void inlineSuppressions(const simplecpp::TokenList &tokens);
void setDirectives(const simplecpp::TokenList &tokens);
void setDirectives(const std::list<Directive> &directives) {
mDirectives = directives;
}
/** list of all directives met while preprocessing file */
const std::list<Directive> &getDirectives() const {

View File

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

View File

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

View File

@ -3560,7 +3560,6 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s
typeTok = typeTok->next();
if (Token::Match(typeTok, ",|)")) { // #8333
symbolDatabase->mTokenizer->syntaxError(typeTok);
return;
}
// skip over qualification
while (Token::Match(typeTok, "%type% ::"))
@ -4448,6 +4447,8 @@ bool Scope::hasInlineOrLambdaFunction() const
// Lambda function
if (s->type == Scope::eLambda)
return true;
if (s->hasInlineOrLambdaFunction())
return true;
}
return false;
}

View File

@ -1352,7 +1352,7 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *to
// skip decltype(...)
else if (Token::simpleMatch(tok->next(), "decltype (")) {
const Token * end = tok->linkAt(2)->previous();
while (tok && tok->next() && tok != end) {
while (tok->next() && tok != end) {
tok = tok->next();
namepos++;
}
@ -1361,7 +1361,7 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *to
if (closing) {
if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:", true))
return true;
while (tok && tok->next() && tok->next() != closing) {
while (tok->next() && tok->next() != closing) {
tok = tok->next();
namepos++;
}
@ -1384,7 +1384,7 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
// skip decltype(...)
else if (Token::simpleMatch(tok->next(), "decltype (")) {
const Token * end = tok->linkAt(2);
while (tok && tok->next() && tok != end) {
while (tok->next() && tok != end) {
tok = tok->next();
namepos++;
}
@ -1393,7 +1393,7 @@ bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *to
if (closing) {
if (Token::Match(closing->next(), "=|;"))
return true;
while (tok && tok->next() && tok->next() != closing) {
while (tok->next() && tok->next() != closing) {
tok = tok->next();
namepos++;
}

View File

@ -24,6 +24,7 @@
#include "library.h"
#include "mathlib.h"
#include "platform.h"
#include "preprocessor.h"
#include "settings.h"
#include "standards.h"
#include "symboldatabase.h"
@ -157,8 +158,9 @@ Tokenizer::Tokenizer() :
mCodeWithTemplates(false), //is there any templates?
mTimerResults(nullptr)
#ifdef MAXTIME
,mMaxTime(std::time(0) + MAXTIME)
, mMaxTime(std::time(0) + MAXTIME)
#endif
, mPreprocessor(nullptr)
{
}
@ -175,6 +177,7 @@ Tokenizer::Tokenizer(const Settings *settings, ErrorLogger *errorLogger) :
#ifdef MAXTIME
,mMaxTime(std::time(0) + MAXTIME)
#endif
, mPreprocessor(nullptr)
{
// make sure settings are specified
assert(mSettings);
@ -9621,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);
@ -11771,3 +11779,19 @@ bool Tokenizer::VariableMap::hasVariable(const std::string &varname) const
{
return mVariableId.find(varname) != mVariableId.end();
}
bool Tokenizer::hasIfdef(const Token *start, const Token *end) const
{
if (!mPreprocessor)
return false;
for (const Directive &d: mPreprocessor->getDirectives()) {
if (d.str.compare(0,3,"#if") == 0 &&
d.linenr >= start->linenr() &&
d.linenr <= end->linenr() &&
start->fileIndex() < list.getFiles().size() &&
d.file == list.getFiles()[start->fileIndex()])
return true;
}
return false;
}

View File

@ -37,6 +37,7 @@ class TimerResults;
class Token;
class TemplateSimplifier;
class ErrorLogger;
class Preprocessor;
namespace simplecpp {
class TokenList;
@ -560,6 +561,15 @@ public:
*/
static const Token * isFunctionHead(const Token *tok, const std::string &endsWith, bool cpp);
void setPreprocessor(const Preprocessor *preprocessor) {
mPreprocessor = preprocessor;
}
const Preprocessor *getPreprocessor() const {
return mPreprocessor;
}
bool hasIfdef(const Token *start, const Token *end) const;
private:
/**
@ -607,16 +617,16 @@ private:
public:
/** Syntax error */
void syntaxError(const Token *tok, const std::string &code = "") const;
NORETURN void syntaxError(const Token *tok, const std::string &code = "") const;
/** Syntax error. Unmatched character. */
void unmatchedToken(const Token *tok) const;
NORETURN void unmatchedToken(const Token *tok) const;
/** Syntax error. C++ code in C file. */
void syntaxErrorC(const Token *tok, const std::string &what) const;
NORETURN void syntaxErrorC(const Token *tok, const std::string &what) const;
/** Warn about unknown macro(s), configuration is recommended */
void unknownMacroError(const Token *tok1) const;
NORETURN void unknownMacroError(const Token *tok1) const;
private:
@ -956,6 +966,8 @@ private:
/** Tokenizer maxtime */
const std::time_t mMaxTime;
#endif
const Preprocessor *mPreprocessor;
};
/// @}

View File

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

View File

@ -4229,7 +4229,7 @@ struct ValueFlowConditionHandler {
// TODO: constValue could be true if there are no assignments in the conditional blocks and
// perhaps if there are no && and no || in the condition
bool constValue = false;
forward(after, top->scope()->bodyEnd, cond.vartok, values, constValue);
forward(after, scope->bodyEnd, cond.vartok, values, constValue);
}
}
}
@ -4441,44 +4441,6 @@ static void valueFlowInferCondition(TokenList* tokenlist,
}
}
static bool valueFlowForLoop1(const Token *tok, int * const varid, MathLib::bigint * const num1, MathLib::bigint * const num2, MathLib::bigint * const numAfter)
{
tok = tok->tokAt(2);
if (!Token::Match(tok, "%type%| %var% ="))
return false;
const Token * const vartok = Token::Match(tok, "%var% =") ? tok : tok->next();
*varid = vartok->varId();
tok = vartok->tokAt(2);
const Token * const num1tok = Token::Match(tok, "%num% ;") ? tok : nullptr;
if (num1tok)
*num1 = MathLib::toLongNumber(num1tok->str());
while (Token::Match(tok, "%name%|%num%|%or%|+|-|*|/|&|[|]|("))
tok = (tok->str() == "(") ? tok->link()->next() : tok->next();
if (!tok || tok->str() != ";")
return false;
tok = tok->next();
const Token *num2tok = nullptr;
if (Token::Match(tok, "%varid% <|<=|!=", vartok->varId())) {
tok = tok->next();
num2tok = tok->astOperand2();
if (num2tok && num2tok->str() == "(" && !num2tok->astOperand2())
num2tok = num2tok->astOperand1();
if (!Token::Match(num2tok, "%num% ;|%oror%")) // TODO: || enlarges the scope of the condition, so it should not cause FP, but it should no lnger be part of this pattern as soon as valueFlowForLoop2 can handle an unknown RHS of || better
num2tok = nullptr;
}
if (!num2tok)
return false;
*num2 = MathLib::toLongNumber(num2tok->str()) - ((tok->str()=="<=") ? 0 : 1);
*numAfter = *num2 + 1;
if (!num1tok)
*num1 = *num2;
while (tok && tok->str() != ";")
tok = tok->next();
if (!Token::Match(tok, "; %varid% ++ ) {", vartok->varId()) && !Token::Match(tok, "; ++ %varid% ) {", vartok->varId()))
return false;
return true;
}
static bool valueFlowForLoop2(const Token *tok,
ProgramMemory *memory1,
ProgramMemory *memory2,
@ -4660,16 +4622,19 @@ static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabas
!Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";"))
continue;
int varid(0);
MathLib::bigint num1(0), num2(0), numAfter(0);
nonneg int varid;
bool knownInitValue, partialCond;
MathLib::bigint initValue, stepValue, lastValue;
if (valueFlowForLoop1(tok, &varid, &num1, &num2, &numAfter)) {
if (num1 <= num2) {
valueFlowForLoopSimplify(bodyStart, varid, false, num1, tokenlist, errorLogger, settings);
valueFlowForLoopSimplify(bodyStart, varid, false, num2, tokenlist, errorLogger, settings);
valueFlowForLoopSimplifyAfter(tok, varid, numAfter, tokenlist, errorLogger, settings);
} else
valueFlowForLoopSimplifyAfter(tok, varid, num1, tokenlist, errorLogger, settings);
if (extractForLoopValues(tok, &varid, &knownInitValue, &initValue, &partialCond, &stepValue, &lastValue)) {
const bool executeBody = !knownInitValue || initValue <= lastValue;
if (executeBody) {
valueFlowForLoopSimplify(bodyStart, varid, false, initValue, tokenlist, errorLogger, settings);
if (stepValue == 1)
valueFlowForLoopSimplify(bodyStart, varid, false, lastValue, tokenlist, errorLogger, settings);
}
const MathLib::bigint afterValue = executeBody ? lastValue + stepValue : initValue;
valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist, errorLogger, settings);
} else {
ProgramMemory mem1, mem2, memAfter;
if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) {

View File

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

86
man/manual.css Normal file
View File

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

View File

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

View File

@ -1,6 +1,7 @@
# Test if --bug-hunting works using cve tests
import glob
import logging
import os
import re
import shutil
@ -14,26 +15,32 @@ else:
CPPCHECK_PATH = '../../cppcheck'
TEST_SUITE = 'cve'
slow = '--slow' in sys.argv
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', datefmt='%H:%M:%S')
def test(test_folder):
print(test_folder)
logging.info(test_folder)
cmd_file = os.path.join(test_folder, 'cmd.txt')
expected_file = os.path.join(test_folder, 'expected.txt')
cmd = [CPPCHECK_PATH,
cmd = ['nice',
CPPCHECK_PATH,
'-D__GNUC__',
'--bug-hunting',
'--inconclusive',
'--platform=unix64',
'--template={file}:{line}:{id}',
'-rp=' + test_folder,
test_folder]
'-rp=' + test_folder]
if os.path.isfile(cmd_file):
for line in open(cmd_file, 'rt'):
if len(line) > 1:
cmd.append(line.strip())
cmd.append(test_folder)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
comm = p.communicate()
stdout = comm[0].decode(encoding='utf-8', errors='ignore')
@ -49,10 +56,20 @@ def test(test_folder):
print(stderr)
sys.exit(1)
if len(sys.argv) > 1:
if (slow is False) and len(sys.argv) > 1:
test(sys.argv[1])
sys.exit(0)
SLOW = []
for test_folder in sorted(glob.glob(TEST_SUITE + '/CVE*')):
if slow is False:
check = False
for s in SLOW:
if s in test_folder:
check = True
if check is True:
logging.info('skipping %s', test_folder)
continue
test(test_folder)

View File

@ -0,0 +1,2 @@
-DMAGICKCORE_LIBOPENJP2_DELEGATE

View File

@ -0,0 +1 @@
jp2.c:865:bughuntingUninit

File diff suppressed because it is too large Load Diff

View File

@ -38,8 +38,6 @@ def get_error_lines(filename):
linenr = 176
elif linenr == 241:
linenr = 242 # warn about usage
elif linenr == 266:
continue # no warning should be written
ret.append(linenr)
return ret

View File

@ -2040,6 +2040,19 @@ void uninivar_bsearch(void)
(void)std::bsearch(key,base,num,size,(int(*)(const void*,const void*)) strcmp);
}
void minsize_bsearch(const void* key, const void* base,
size_t num, size_t size,
int (*compar)(const void*,const void*))
{
int Base [3] = {42, 43, 44};
(void)std::bsearch(key,Base,2,size,(int(*)(const void*,const void*)) strcmp);
(void)std::bsearch(key,Base,3,size,(int(*)(const void*,const void*)) strcmp);
(void)std::bsearch(key,Base,4,size,(int(*)(const void*,const void*)) strcmp);
(void)std::bsearch(key,base,2,size,(int(*)(const void*,const void*)) strcmp);
}
void uninitvar_qsort(void)
{
void *base;

View File

@ -25,21 +25,27 @@
class TestBughuntingChecks : public TestFixture {
public:
TestBughuntingChecks() : TestFixture("TestBughuntingChecks") {
settings.platform(cppcheck::Platform::Unix64);
}
private:
Settings settings;
void run() OVERRIDE {
#ifdef USE_Z3
settings.inconclusive = true;
LOAD_LIB_2(settings.library, "std.cfg");
TEST_CASE(uninit);
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
}
void check(const char code[]) {
Settings settings;
settings.platform(cppcheck::Platform::Unix64);
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
@ -83,12 +89,44 @@ private:
check("char foo(char id[]);\n"
"void bar() { char data[10]; foo(data); }");
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS("[test.cpp:2]: (error, inconclusive) Cannot determine that 'data[0]' is initialized. It is inconclusive if there would be a problem in the function call.\n", errout.str());
check("void foo(int *p) { if (p) *p=0; }");
ASSERT_EQUALS("", errout.str());
}
void uninit_malloc() {
check("void foo() { char *p = malloc(10); return *p; }");
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"

View File

@ -170,6 +170,7 @@ private:
TEST_CASE(const65); // ticket #8693
TEST_CASE(const66); // ticket #7714
TEST_CASE(const67); // ticket #9193
TEST_CASE(const68); // ticket #6471
TEST_CASE(const_handleDefaultParameters);
TEST_CASE(const_passThisToMemberOfOtherClass);
TEST_CASE(assigningPointerToPointerIsNotAConstOperation);
@ -5514,6 +5515,17 @@ private:
ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Test::get' can be const.\n", errout.str());
}
void const68() { // #6471
checkConst("class MyClass {\n"
" void clear() {\n"
" SVecPtr v = (SVecPtr) m_data;\n"
" v->clear();\n"
" }\n"
" void* m_data;\n"
"};\n");
ASSERT_EQUALS("", errout.str());
}
void const_handleDefaultParameters() {
checkConst("struct Foo {\n"
" void foo1(int i, int j = 0) {\n"

View File

@ -18,6 +18,7 @@
#include "checkcondition.h"
#include "library.h"
#include "preprocessor.h"
#include "settings.h"
#include "testsuite.h"
#include "tokenize.h"
@ -136,10 +137,14 @@ private:
std::map<std::string, simplecpp::TokenList*> filedata;
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
Preprocessor preprocessor(settings0, nullptr);
preprocessor.setDirectives(tokens1);
// Tokenizer..
Tokenizer tokenizer(&settings0, this);
tokenizer.createTokens(std::move(tokens2));
tokenizer.simplifyTokens1("");
tokenizer.setPreprocessor(&preprocessor);
// Run checks..
CheckCondition checkCondition;
@ -2581,6 +2586,18 @@ private:
" int FileIndex; \n"
"};\n");
ASSERT_EQUALS("", errout.str());
// #8858 - #if
check("short Do() {\n"
" short ret = bar1();\n"
" if ( ret )\n"
" return ret;\n"
"#ifdef FEATURE\n"
" ret = bar2();\n"
"#endif\n"
" return ret;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void innerConditionModified() {
@ -2981,14 +2998,17 @@ private:
// Avoid FP for sizeof condition
check("void f() {\n"
" if (sizeof(char) != 123) {}\n"
" if (123 != sizeof(char)) {}\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int x = 123;\n"
" if (sizeof(char) != x) {}\n"
" if (x != sizeof(char)) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n"
"[test.cpp:4]: (style) Condition 'x!=sizeof(char)' is always true\n", errout.str());
// Don't warn in assertions. Condition is often 'always true' by intention.
// If platform,defines,etc cause an 'always false' assertion then that is not very dangerous neither
@ -3679,6 +3699,55 @@ private:
" if (a.b) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" int a;\n"
" void b() const {\n"
" return a == 1;\n"
" }\n"
" void c();\n"
" void d() {\n"
" if(b()) {\n"
" c();\n"
" }\n"
" if (b()) {\n"
" a = 3;\n"
" }\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" int a;\n"
" void b() const {\n"
" return a == 1;\n"
" }\n"
" void d() {\n"
" if(b()) {\n"
" a = 2;\n"
" }\n"
" if (b()) {\n"
" a = 3;\n"
" }\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" int a;\n"
" void b() const {\n"
" return a == 1;\n"
" }\n"
" void d() {\n"
" if(b()) {\n"
" }\n"
" if (b()) {\n"
" a = 3;\n"
" }\n"
" }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) The if condition is the same as the previous if condition\n",
errout.str());
}
void checkInvalidTestForOverflow() {

View File

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

View File

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

View File

@ -134,6 +134,7 @@ private:
TEST_CASE(ifelse13); // #8392
TEST_CASE(ifelse14); // #9130 - if (x == (char*)NULL)
TEST_CASE(ifelse15); // #9206 - if (global_ptr = malloc(1))
TEST_CASE(ifelse16); // #9635 - if (p = malloc(4), p == NULL)
// switch
TEST_CASE(switch1);
@ -184,6 +185,8 @@ private:
TEST_CASE(smartPtrInContainer); // #8262
TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097
TEST_CASE(functionCallCastConfig); // #9652
}
void check(const char code[], bool cpp = false) {
@ -202,6 +205,22 @@ private:
c.runChecks(&tokenizer, &settings, this);
}
void check(const char code[], Settings & settings) {
// Clear the error buffer..
errout.str("");
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
// Check for leaks..
CheckLeakAutoVar c;
settings.checkLibrary = true;
settings.addEnabled("information");
c.runChecks(&tokenizer, &settings, this);
}
void checkP(const char code[], bool cpp = false) {
// Clear the error buffer..
errout.str("");
@ -1466,6 +1485,26 @@ private:
ASSERT_EQUALS("", errout.str());
}
void ifelse16() { // #9635
check("void f(void) {\n"
" char *p;\n"
" if(p = malloc(4), p == NULL)\n"
" return;\n"
" free(p);\n"
" return;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f(void) {\n"
" char *p, q;\n"
" if(p = malloc(4), q = 1, p == NULL)\n"
" return;\n"
" free(p);\n"
" return;\n"
"}");
ASSERT_EQUALS("", errout.str());
}
void switch1() {
check("void f() {\n"
" char *p = 0;\n"
@ -2023,6 +2062,31 @@ private:
"}"));
}
void functionCallCastConfig() { // #9652
Settings settingsFunctionCall = settings;
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def format=\"2\">\n"
" <function name=\"free_func\">\n"
" <noreturn>false</noreturn>\n"
" <arg nr=\"1\">\n"
" <not-uninit/>\n"
" </arg>\n"
" <arg nr=\"2\">\n"
" <not-uninit/>\n"
" </arg>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
settingsFunctionCall.library.load(doc);
check("void test_func()\n"
"{\n"
" char * buf = malloc(4);\n"
" free_func((void *)(1), buf);\n"
"}", settingsFunctionCall);
ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: Function free_func() should have <use>/<leak-ignore> configuration\n", errout.str());
}
};
REGISTER_TEST(TestLeakAutoVar)

View File

@ -686,11 +686,9 @@ private:
ASSERT(library.functions.empty());
const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX");
ASSERT(af && af->arg == 5);
ASSERT(af && af->arg == 5 && !af->initData);
const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX");
ASSERT(df && df->arg == 2);
ASSERT(library.returnuninitdata.find("CreateX") != library.returnuninitdata.cend());
}
void resource() const {

View File

@ -1677,6 +1677,8 @@ private:
TEST_CASE(varid_2); // #5315: Analysis confused by ((variable).attribute) notation
TEST_CASE(customAllocation);
TEST_CASE(lambdaInForLoop); // #9793
}
void err() {
@ -2062,6 +2064,22 @@ private:
"}", false);
ASSERT_EQUALS("[test.c:7]: (error) Memory leak: abc.a\n", errout.str());
}
void lambdaInForLoop() { // #9793
check(
"struct S { int * p{nullptr}; };\n"
"int main()\n"
"{\n"
" S s;\n"
" s.p = new int[10];\n"
" for (int i = 0; i < 10; ++i) {\n"
" s.p[i] = []() { return 1; }();\n"
" }\n"
" delete[] s.p;\n"
" return 0;\n"
"}", true);
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestMemleakStructMember)

View File

@ -262,7 +262,7 @@ private:
" }\n"
" tok->str();\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning, inconclusive) Possible null pointer dereference: tok - otherwise it is redundant to check it against null.\n", "", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout.str());
check("int foo(const Token *tok)\n"
"{\n"

View File

@ -19,6 +19,7 @@
#include "checkother.h"
#include "library.h"
#include "platform.h"
#include "preprocessor.h"
#include "settings.h"
#include "standards.h"
#include "testsuite.h"
@ -86,6 +87,7 @@ private:
TEST_CASE(varScope24); // pointer / reference
TEST_CASE(varScope25); // time_t
TEST_CASE(varScope26); // range for loop, map
TEST_CASE(varScope27); // #7733 - #if
TEST_CASE(oldStylePointerCast);
TEST_CASE(invalidPointerCast);
@ -241,6 +243,8 @@ private:
TEST_CASE(unusedVariableValueTemplate); // #8994
TEST_CASE(moduloOfOne);
TEST_CASE(sameExpressionPointers);
}
void check(const char code[], const char *filename = nullptr, bool experimental = false, bool inconclusive = true, bool runSimpleChecks=true, bool verbose=false, Settings* settings = nullptr) {
@ -300,10 +304,14 @@ private:
std::map<std::string, simplecpp::TokenList*> filedata;
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
Preprocessor preprocessor(*settings, nullptr);
preprocessor.setDirectives(tokens1);
// Tokenizer..
Tokenizer tokenizer(settings, this);
tokenizer.createTokens(std::move(tokens2));
tokenizer.simplifyTokens1("");
tokenizer.setPreprocessor(&preprocessor);
// Check..
CheckOther checkOther(&tokenizer, settings, this);
@ -1229,6 +1237,24 @@ private:
ASSERT_EQUALS("", errout.str());
}
void varScope27() {
checkP("void f() {\n"
" int x = 0;\n"
"#ifdef X\n"
"#endif\n"
" if (id == ABC) { return x; }\n"
"}");
ASSERT_EQUALS("", errout.str());
checkP("void f() {\n"
"#ifdef X\n"
"#endif\n"
" int x = 0;\n"
" if (id == ABC) { return x; }\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'x' can be reduced.\n", errout.str());
}
void checkOldStylePointerCast(const char code[]) {
// Clear the error buffer..
errout.str("");
@ -5460,6 +5486,24 @@ private:
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n"
"[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val > 0' is always false.\n", errout.str());
check("void f() {\n"
" int val = 0;\n"
" int *p = &val;n"
" val = 1;\n"
" if (*p < 0) continue;\n"
" if ((*p > 0)) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int val = 0;\n"
" int *p = &val;n"
" if (*p < 0) continue;\n"
" if ((*p > 0)) {}\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison '*p < 0' is always false.\n"
"[test.cpp:2] -> [test.cpp:4]: (style) The comparison '*p > 0' is always false.\n", "", errout.str());
check("void f() {\n"
" int val = 0;\n"
" if (val < 0) {\n"
@ -8677,6 +8721,15 @@ private:
ASSERT_EQUALS("", errout.str());
}
void sameExpressionPointers() {
check("int f(int *i);\n"
"void g(int *a, int *b) {\n"
" int c = *a;\n"
" f(a);\n"
" if (b && c != *a) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestOther)

Some files were not shown because too many files have changed in this diff Show More